199e242abSchristos /* $OpenBSD$ */ 2698d5317Sjmmv 3698d5317Sjmmv /* 4f26e8bc9Schristos * Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com> 5698d5317Sjmmv * 6698d5317Sjmmv * Permission to use, copy, modify, and distribute this software for any 7698d5317Sjmmv * purpose with or without fee is hereby granted, provided that the above 8698d5317Sjmmv * copyright notice and this permission notice appear in all copies. 9698d5317Sjmmv * 10698d5317Sjmmv * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11698d5317Sjmmv * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12698d5317Sjmmv * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13698d5317Sjmmv * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14698d5317Sjmmv * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER 15698d5317Sjmmv * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 16698d5317Sjmmv * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17698d5317Sjmmv */ 18698d5317Sjmmv 19698d5317Sjmmv #include <sys/types.h> 20698d5317Sjmmv 21928fc495Schristos #include <stdlib.h> 22698d5317Sjmmv #include <string.h> 23698d5317Sjmmv 24698d5317Sjmmv #include "tmux.h" 25698d5317Sjmmv 26698d5317Sjmmv /* 27698d5317Sjmmv * Grid data. This is the basic data structure that represents what is shown on 28698d5317Sjmmv * screen. 29698d5317Sjmmv * 30698d5317Sjmmv * A grid is a grid of cells (struct grid_cell). Lines are not allocated until 31698d5317Sjmmv * cells in that line are written to. The grid is split into history and 32698d5317Sjmmv * viewable data with the history starting at row (line) 0 and extending to 33698d5317Sjmmv * (hsize - 1); from hsize to hsize + (sy - 1) is the viewable data. All 34698d5317Sjmmv * functions in this file work on absolute coordinates, grid-view.c has 35698d5317Sjmmv * functions which work on the screen data. 36698d5317Sjmmv */ 37698d5317Sjmmv 38698d5317Sjmmv /* Default grid cell data. */ 39f26e8bc9Schristos const struct grid_cell grid_default_cell = { 40f844e94eSwiz { { ' ' }, 0, 1, 1 }, 0, 0, 8, 8, 8, 0 41f26e8bc9Schristos }; 420a274e86Schristos 43e271dbb8Schristos /* 44e271dbb8Schristos * Padding grid cell data. Padding cells are the only zero width cell that 45e271dbb8Schristos * appears in the grid - because of this, they are always extended cells. 46e271dbb8Schristos */ 47e271dbb8Schristos static const struct grid_cell grid_padding_cell = { 48f844e94eSwiz { { '!' }, 0, 0, 0 }, 0, GRID_FLAG_PADDING, 8, 8, 8, 0 49e271dbb8Schristos }; 50e271dbb8Schristos 510a274e86Schristos /* Cleared grid cell data. */ 52e271dbb8Schristos static const struct grid_cell grid_cleared_cell = { 53f844e94eSwiz { { ' ' }, 0, 1, 1 }, 0, GRID_FLAG_CLEARED, 8, 8, 8, 0 540a274e86Schristos }; 550a274e86Schristos static const struct grid_cell_entry grid_cleared_entry = { 56f844e94eSwiz { .data = { 0, 8, 8, ' ' } }, GRID_FLAG_CLEARED 57f26e8bc9Schristos }; 58698d5317Sjmmv 59e9a2d6faSchristos /* Store cell in entry. */ 60e9a2d6faSchristos static void 61e9a2d6faSchristos grid_store_cell(struct grid_cell_entry *gce, const struct grid_cell *gc, 62e9a2d6faSchristos u_char c) 63e9a2d6faSchristos { 640a274e86Schristos gce->flags = (gc->flags & ~GRID_FLAG_CLEARED); 65e9a2d6faSchristos 66e9a2d6faSchristos gce->data.fg = gc->fg & 0xff; 67e9a2d6faSchristos if (gc->fg & COLOUR_FLAG_256) 68e9a2d6faSchristos gce->flags |= GRID_FLAG_FG256; 69e9a2d6faSchristos 70e9a2d6faSchristos gce->data.bg = gc->bg & 0xff; 71e9a2d6faSchristos if (gc->bg & COLOUR_FLAG_256) 72e9a2d6faSchristos gce->flags |= GRID_FLAG_BG256; 73e9a2d6faSchristos 74e9a2d6faSchristos gce->data.attr = gc->attr; 75e9a2d6faSchristos gce->data.data = c; 76e9a2d6faSchristos } 77e9a2d6faSchristos 780a274e86Schristos /* Check if a cell should be an extended cell. */ 79e9a2d6faSchristos static int 80e9a2d6faSchristos grid_need_extended_cell(const struct grid_cell_entry *gce, 81e9a2d6faSchristos const struct grid_cell *gc) 82e9a2d6faSchristos { 83e9a2d6faSchristos if (gce->flags & GRID_FLAG_EXTENDED) 84e9a2d6faSchristos return (1); 85e9a2d6faSchristos if (gc->attr > 0xff) 86e9a2d6faSchristos return (1); 87e9a2d6faSchristos if (gc->data.size != 1 || gc->data.width != 1) 88e9a2d6faSchristos return (1); 89e9a2d6faSchristos if ((gc->fg & COLOUR_FLAG_RGB) || (gc->bg & COLOUR_FLAG_RGB)) 90e9a2d6faSchristos return (1); 91*890b6d91Swiz if (gc->us != 8) /* only supports 256 or RGB */ 9230744affSchristos return (1); 93f844e94eSwiz if (gc->link != 0) 94f844e94eSwiz return (1); 95e9a2d6faSchristos return (0); 96e9a2d6faSchristos } 97e9a2d6faSchristos 980a274e86Schristos /* Get an extended cell. */ 990a274e86Schristos static void 1000a274e86Schristos grid_get_extended_cell(struct grid_line *gl, struct grid_cell_entry *gce, 1010a274e86Schristos int flags) 1020a274e86Schristos { 1030a274e86Schristos u_int at = gl->extdsize + 1; 1040a274e86Schristos 1050a274e86Schristos gl->extddata = xreallocarray(gl->extddata, at, sizeof *gl->extddata); 1060a274e86Schristos gl->extdsize = at; 1070a274e86Schristos 1080a274e86Schristos gce->offset = at - 1; 1090a274e86Schristos gce->flags = (flags | GRID_FLAG_EXTENDED); 1100a274e86Schristos } 1110a274e86Schristos 1120a274e86Schristos /* Set cell as extended. */ 113e271dbb8Schristos static struct grid_extd_entry * 1140a274e86Schristos grid_extended_cell(struct grid_line *gl, struct grid_cell_entry *gce, 1150a274e86Schristos const struct grid_cell *gc) 1160a274e86Schristos { 117e271dbb8Schristos struct grid_extd_entry *gee; 1180a274e86Schristos int flags = (gc->flags & ~GRID_FLAG_CLEARED); 119e271dbb8Schristos utf8_char uc; 1200a274e86Schristos 1210a274e86Schristos if (~gce->flags & GRID_FLAG_EXTENDED) 1220a274e86Schristos grid_get_extended_cell(gl, gce, flags); 1230a274e86Schristos else if (gce->offset >= gl->extdsize) 1240a274e86Schristos fatalx("offset too big"); 1250a274e86Schristos gl->flags |= GRID_LINE_EXTENDED; 1260a274e86Schristos 127e271dbb8Schristos utf8_from_data(&gc->data, &uc); 128e271dbb8Schristos 129e271dbb8Schristos gee = &gl->extddata[gce->offset]; 130e271dbb8Schristos gee->data = uc; 131e271dbb8Schristos gee->attr = gc->attr; 132e271dbb8Schristos gee->flags = flags; 133e271dbb8Schristos gee->fg = gc->fg; 134e271dbb8Schristos gee->bg = gc->bg; 135e271dbb8Schristos gee->us = gc->us; 136f844e94eSwiz gee->link = gc->link; 137e271dbb8Schristos return (gee); 1380a274e86Schristos } 1390a274e86Schristos 140fe99a117Schristos /* Free up unused extended cells. */ 141fe99a117Schristos static void 142fe99a117Schristos grid_compact_line(struct grid_line *gl) 143fe99a117Schristos { 144fe99a117Schristos int new_extdsize = 0; 145e271dbb8Schristos struct grid_extd_entry *new_extddata; 146fe99a117Schristos struct grid_cell_entry *gce; 147e271dbb8Schristos struct grid_extd_entry *gee; 148fe99a117Schristos u_int px, idx; 149fe99a117Schristos 150fe99a117Schristos if (gl->extdsize == 0) 151fe99a117Schristos return; 152fe99a117Schristos 153fe99a117Schristos for (px = 0; px < gl->cellsize; px++) { 154fe99a117Schristos gce = &gl->celldata[px]; 155fe99a117Schristos if (gce->flags & GRID_FLAG_EXTENDED) 156fe99a117Schristos new_extdsize++; 157fe99a117Schristos } 158fe99a117Schristos 159fe99a117Schristos if (new_extdsize == 0) { 160fe99a117Schristos free(gl->extddata); 161fe99a117Schristos gl->extddata = NULL; 162fe99a117Schristos gl->extdsize = 0; 163fe99a117Schristos return; 164fe99a117Schristos } 165fe99a117Schristos new_extddata = xreallocarray(NULL, new_extdsize, sizeof *gl->extddata); 166fe99a117Schristos 167fe99a117Schristos idx = 0; 168fe99a117Schristos for (px = 0; px < gl->cellsize; px++) { 169fe99a117Schristos gce = &gl->celldata[px]; 170fe99a117Schristos if (gce->flags & GRID_FLAG_EXTENDED) { 171e271dbb8Schristos gee = &gl->extddata[gce->offset]; 172e271dbb8Schristos memcpy(&new_extddata[idx], gee, sizeof *gee); 173fe99a117Schristos gce->offset = idx++; 174fe99a117Schristos } 175fe99a117Schristos } 176fe99a117Schristos 177fe99a117Schristos free(gl->extddata); 178fe99a117Schristos gl->extddata = new_extddata; 179fe99a117Schristos gl->extdsize = new_extdsize; 180fe99a117Schristos } 181fe99a117Schristos 182c7e17de0Schristos /* Get line data. */ 183c7e17de0Schristos struct grid_line * 184c7e17de0Schristos grid_get_line(struct grid *gd, u_int line) 185c7e17de0Schristos { 186c7e17de0Schristos return (&gd->linedata[line]); 187c7e17de0Schristos } 188c7e17de0Schristos 189c7e17de0Schristos /* Adjust number of lines. */ 190c7e17de0Schristos void 191c7e17de0Schristos grid_adjust_lines(struct grid *gd, u_int lines) 192c7e17de0Schristos { 193c7e17de0Schristos gd->linedata = xreallocarray(gd->linedata, lines, sizeof *gd->linedata); 194c7e17de0Schristos } 195c7e17de0Schristos 196f26e8bc9Schristos /* Copy default into a cell. */ 197f26e8bc9Schristos static void 198e9a2d6faSchristos grid_clear_cell(struct grid *gd, u_int px, u_int py, u_int bg) 199f26e8bc9Schristos { 200e9a2d6faSchristos struct grid_line *gl = &gd->linedata[py]; 201e9a2d6faSchristos struct grid_cell_entry *gce = &gl->celldata[px]; 202e271dbb8Schristos struct grid_extd_entry *gee; 203e9a2d6faSchristos 2040a274e86Schristos memcpy(gce, &grid_cleared_entry, sizeof *gce); 20568e6ba84Schristos if (bg != 8) { 206e9a2d6faSchristos if (bg & COLOUR_FLAG_RGB) { 2070a274e86Schristos grid_get_extended_cell(gl, gce, gce->flags); 208e271dbb8Schristos gee = grid_extended_cell(gl, gce, &grid_cleared_cell); 209e271dbb8Schristos gee->bg = bg; 210e9a2d6faSchristos } else { 211e9a2d6faSchristos if (bg & COLOUR_FLAG_256) 212e9a2d6faSchristos gce->flags |= GRID_FLAG_BG256; 213e9a2d6faSchristos gce->data.bg = bg; 214e9a2d6faSchristos } 215f26e8bc9Schristos } 21668e6ba84Schristos } 217f26e8bc9Schristos 21899e242abSchristos /* Check grid y position. */ 219e9a2d6faSchristos static int 220c7e17de0Schristos grid_check_y(struct grid *gd, const char *from, u_int py) 221698d5317Sjmmv { 222c7e17de0Schristos if (py >= gd->hsize + gd->sy) { 223c7e17de0Schristos log_debug("%s: y out of range: %u", from, py); 224698d5317Sjmmv return (-1); 225698d5317Sjmmv } 226698d5317Sjmmv return (0); 227698d5317Sjmmv } 228928fc495Schristos 229e271dbb8Schristos /* Check if two styles are (visibly) the same. */ 230e271dbb8Schristos int 231e271dbb8Schristos grid_cells_look_equal(const struct grid_cell *gc1, const struct grid_cell *gc2) 232e271dbb8Schristos { 233e271dbb8Schristos if (gc1->fg != gc2->fg || gc1->bg != gc2->bg) 234e271dbb8Schristos return (0); 235e271dbb8Schristos if (gc1->attr != gc2->attr || gc1->flags != gc2->flags) 236e271dbb8Schristos return (0); 237f844e94eSwiz if (gc1->link != gc2->link) 238f844e94eSwiz return (0); 239e271dbb8Schristos return (1); 240e271dbb8Schristos } 241e271dbb8Schristos 242e9a2d6faSchristos /* Compare grid cells. Return 1 if equal, 0 if not. */ 243e9a2d6faSchristos int 244e271dbb8Schristos grid_cells_equal(const struct grid_cell *gc1, const struct grid_cell *gc2) 245e9a2d6faSchristos { 246e271dbb8Schristos if (!grid_cells_look_equal(gc1, gc2)) 247e9a2d6faSchristos return (0); 248e271dbb8Schristos if (gc1->data.width != gc2->data.width) 249e9a2d6faSchristos return (0); 250e271dbb8Schristos if (gc1->data.size != gc2->data.size) 251e9a2d6faSchristos return (0); 252e271dbb8Schristos return (memcmp(gc1->data.data, gc2->data.data, gc1->data.size) == 0); 253e9a2d6faSchristos } 254e9a2d6faSchristos 255fe99a117Schristos /* Free one line. */ 256fe99a117Schristos static void 257fe99a117Schristos grid_free_line(struct grid *gd, u_int py) 258fe99a117Schristos { 259fe99a117Schristos free(gd->linedata[py].celldata); 260fe99a117Schristos gd->linedata[py].celldata = NULL; 261fe99a117Schristos free(gd->linedata[py].extddata); 262fe99a117Schristos gd->linedata[py].extddata = NULL; 263fe99a117Schristos } 264fe99a117Schristos 265fe99a117Schristos /* Free several lines. */ 266fe99a117Schristos static void 267fe99a117Schristos grid_free_lines(struct grid *gd, u_int py, u_int ny) 268fe99a117Schristos { 269fe99a117Schristos u_int yy; 270fe99a117Schristos 271fe99a117Schristos for (yy = py; yy < py + ny; yy++) 272fe99a117Schristos grid_free_line(gd, yy); 273fe99a117Schristos } 274fe99a117Schristos 275698d5317Sjmmv /* Create a new grid. */ 276698d5317Sjmmv struct grid * 277698d5317Sjmmv grid_create(u_int sx, u_int sy, u_int hlimit) 278698d5317Sjmmv { 279698d5317Sjmmv struct grid *gd; 280698d5317Sjmmv 281698d5317Sjmmv gd = xmalloc(sizeof *gd); 282698d5317Sjmmv gd->sx = sx; 283698d5317Sjmmv gd->sy = sy; 284698d5317Sjmmv 285e271dbb8Schristos if (hlimit != 0) 286698d5317Sjmmv gd->flags = GRID_HISTORY; 287e271dbb8Schristos else 288e271dbb8Schristos gd->flags = 0; 289698d5317Sjmmv 290e9a2d6faSchristos gd->hscrolled = 0; 291698d5317Sjmmv gd->hsize = 0; 292698d5317Sjmmv gd->hlimit = hlimit; 293698d5317Sjmmv 294c7e17de0Schristos if (gd->sy != 0) 295698d5317Sjmmv gd->linedata = xcalloc(gd->sy, sizeof *gd->linedata); 296c7e17de0Schristos else 297c7e17de0Schristos gd->linedata = NULL; 298698d5317Sjmmv 299698d5317Sjmmv return (gd); 300698d5317Sjmmv } 301698d5317Sjmmv 302698d5317Sjmmv /* Destroy grid. */ 303698d5317Sjmmv void 304698d5317Sjmmv grid_destroy(struct grid *gd) 305698d5317Sjmmv { 306fe99a117Schristos grid_free_lines(gd, 0, gd->hsize + gd->sy); 307698d5317Sjmmv 308928fc495Schristos free(gd->linedata); 309698d5317Sjmmv 310928fc495Schristos free(gd); 311698d5317Sjmmv } 312698d5317Sjmmv 313698d5317Sjmmv /* Compare grids. */ 314698d5317Sjmmv int 315698d5317Sjmmv grid_compare(struct grid *ga, struct grid *gb) 316698d5317Sjmmv { 317698d5317Sjmmv struct grid_line *gla, *glb; 318f26e8bc9Schristos struct grid_cell gca, gcb; 319698d5317Sjmmv u_int xx, yy; 320698d5317Sjmmv 321928fc495Schristos if (ga->sx != gb->sx || ga->sy != gb->sy) 322698d5317Sjmmv return (1); 323698d5317Sjmmv 324698d5317Sjmmv for (yy = 0; yy < ga->sy; yy++) { 325698d5317Sjmmv gla = &ga->linedata[yy]; 326698d5317Sjmmv glb = &gb->linedata[yy]; 327698d5317Sjmmv if (gla->cellsize != glb->cellsize) 328698d5317Sjmmv return (1); 329f26e8bc9Schristos for (xx = 0; xx < gla->cellsize; xx++) { 330f26e8bc9Schristos grid_get_cell(ga, xx, yy, &gca); 331f26e8bc9Schristos grid_get_cell(gb, xx, yy, &gcb); 332e9a2d6faSchristos if (!grid_cells_equal(&gca, &gcb)) 333698d5317Sjmmv return (1); 334698d5317Sjmmv } 335698d5317Sjmmv } 336698d5317Sjmmv 337698d5317Sjmmv return (0); 338698d5317Sjmmv } 339698d5317Sjmmv 340c7e17de0Schristos /* Trim lines from the history. */ 341c7e17de0Schristos static void 342c7e17de0Schristos grid_trim_history(struct grid *gd, u_int ny) 343c7e17de0Schristos { 344c7e17de0Schristos grid_free_lines(gd, 0, ny); 345c7e17de0Schristos memmove(&gd->linedata[0], &gd->linedata[ny], 346c7e17de0Schristos (gd->hsize + gd->sy - ny) * (sizeof *gd->linedata)); 347c7e17de0Schristos } 348c7e17de0Schristos 349698d5317Sjmmv /* 350698d5317Sjmmv * Collect lines from the history if at the limit. Free the top (oldest) 10% 351698d5317Sjmmv * and shift up. 352698d5317Sjmmv */ 353698d5317Sjmmv void 354fe99a117Schristos grid_collect_history(struct grid *gd) 355698d5317Sjmmv { 356fe99a117Schristos u_int ny; 357698d5317Sjmmv 358fe99a117Schristos if (gd->hsize == 0 || gd->hsize < gd->hlimit) 359698d5317Sjmmv return; 360698d5317Sjmmv 361fe99a117Schristos ny = gd->hlimit / 10; 362fe99a117Schristos if (ny < 1) 363fe99a117Schristos ny = 1; 364fe99a117Schristos if (ny > gd->hsize) 365fe99a117Schristos ny = gd->hsize; 366698d5317Sjmmv 367fe99a117Schristos /* 368fe99a117Schristos * Free the lines from 0 to ny then move the remaining lines over 369fe99a117Schristos * them. 370fe99a117Schristos */ 371c7e17de0Schristos grid_trim_history(gd, ny); 372fe99a117Schristos 373fe99a117Schristos gd->hsize -= ny; 374e9a2d6faSchristos if (gd->hscrolled > gd->hsize) 375e9a2d6faSchristos gd->hscrolled = gd->hsize; 376698d5317Sjmmv } 377698d5317Sjmmv 378e271dbb8Schristos /* Remove lines from the bottom of the history. */ 379e271dbb8Schristos void 380e271dbb8Schristos grid_remove_history(struct grid *gd, u_int ny) 381e271dbb8Schristos { 382e271dbb8Schristos u_int yy; 383e271dbb8Schristos 384e271dbb8Schristos if (ny > gd->hsize) 385e271dbb8Schristos return; 386e271dbb8Schristos for (yy = 0; yy < ny; yy++) 387e271dbb8Schristos grid_free_line(gd, gd->hsize + gd->sy - 1 - yy); 388e271dbb8Schristos gd->hsize -= ny; 389e271dbb8Schristos } 390e271dbb8Schristos 391698d5317Sjmmv /* 392698d5317Sjmmv * Scroll the entire visible screen, moving one line into the history. Just 393698d5317Sjmmv * allocate a new line at the bottom and move the history size indicator. 394698d5317Sjmmv */ 395698d5317Sjmmv void 396e9a2d6faSchristos grid_scroll_history(struct grid *gd, u_int bg) 397698d5317Sjmmv { 398698d5317Sjmmv u_int yy; 399698d5317Sjmmv 400698d5317Sjmmv yy = gd->hsize + gd->sy; 40199e242abSchristos gd->linedata = xreallocarray(gd->linedata, yy + 1, 40299e242abSchristos sizeof *gd->linedata); 403e9a2d6faSchristos grid_empty_line(gd, yy, bg); 404698d5317Sjmmv 405e9a2d6faSchristos gd->hscrolled++; 406fe99a117Schristos grid_compact_line(&gd->linedata[gd->hsize]); 407f844e94eSwiz gd->linedata[gd->hsize].time = current_time; 408698d5317Sjmmv gd->hsize++; 409698d5317Sjmmv } 410698d5317Sjmmv 41199e242abSchristos /* Clear the history. */ 41299e242abSchristos void 41399e242abSchristos grid_clear_history(struct grid *gd) 41499e242abSchristos { 415c7e17de0Schristos grid_trim_history(gd, gd->hsize); 41699e242abSchristos 417e9a2d6faSchristos gd->hscrolled = 0; 41899e242abSchristos gd->hsize = 0; 419e9a2d6faSchristos 42099e242abSchristos gd->linedata = xreallocarray(gd->linedata, gd->sy, 42199e242abSchristos sizeof *gd->linedata); 42299e242abSchristos } 42399e242abSchristos 424698d5317Sjmmv /* Scroll a region up, moving the top line into the history. */ 425698d5317Sjmmv void 426fe99a117Schristos grid_scroll_history_region(struct grid *gd, u_int upper, u_int lower, u_int bg) 427698d5317Sjmmv { 428fe99a117Schristos struct grid_line *gl_history, *gl_upper; 429698d5317Sjmmv u_int yy; 430698d5317Sjmmv 431698d5317Sjmmv /* Create a space for a new line. */ 432698d5317Sjmmv yy = gd->hsize + gd->sy; 43399e242abSchristos gd->linedata = xreallocarray(gd->linedata, yy + 1, 43499e242abSchristos sizeof *gd->linedata); 435698d5317Sjmmv 436698d5317Sjmmv /* Move the entire screen down to free a space for this line. */ 437698d5317Sjmmv gl_history = &gd->linedata[gd->hsize]; 438698d5317Sjmmv memmove(gl_history + 1, gl_history, gd->sy * sizeof *gl_history); 439698d5317Sjmmv 440698d5317Sjmmv /* Adjust the region and find its start and end. */ 441698d5317Sjmmv upper++; 442698d5317Sjmmv gl_upper = &gd->linedata[upper]; 443698d5317Sjmmv lower++; 444698d5317Sjmmv 445698d5317Sjmmv /* Move the line into the history. */ 446698d5317Sjmmv memcpy(gl_history, gl_upper, sizeof *gl_history); 447f844e94eSwiz gl_history->time = current_time; 448698d5317Sjmmv 449698d5317Sjmmv /* Then move the region up and clear the bottom line. */ 450698d5317Sjmmv memmove(gl_upper, gl_upper + 1, (lower - upper) * sizeof *gl_upper); 451fe99a117Schristos grid_empty_line(gd, lower, bg); 452698d5317Sjmmv 453698d5317Sjmmv /* Move the history offset down over the line. */ 454e9a2d6faSchristos gd->hscrolled++; 455698d5317Sjmmv gd->hsize++; 456698d5317Sjmmv } 457698d5317Sjmmv 458698d5317Sjmmv /* Expand line to fit to cell. */ 459e9a2d6faSchristos static void 460e9a2d6faSchristos grid_expand_line(struct grid *gd, u_int py, u_int sx, u_int bg) 461698d5317Sjmmv { 462698d5317Sjmmv struct grid_line *gl; 463698d5317Sjmmv u_int xx; 464698d5317Sjmmv 465698d5317Sjmmv gl = &gd->linedata[py]; 466698d5317Sjmmv if (sx <= gl->cellsize) 467698d5317Sjmmv return; 468698d5317Sjmmv 469e9a2d6faSchristos if (sx < gd->sx / 4) 470e9a2d6faSchristos sx = gd->sx / 4; 471e9a2d6faSchristos else if (sx < gd->sx / 2) 472e9a2d6faSchristos sx = gd->sx / 2; 473e271dbb8Schristos else if (gd->sx > sx) 474e9a2d6faSchristos sx = gd->sx; 475e9a2d6faSchristos 47699e242abSchristos gl->celldata = xreallocarray(gl->celldata, sx, sizeof *gl->celldata); 477698d5317Sjmmv for (xx = gl->cellsize; xx < sx; xx++) 478e9a2d6faSchristos grid_clear_cell(gd, xx, py, bg); 479698d5317Sjmmv gl->cellsize = sx; 480698d5317Sjmmv } 481698d5317Sjmmv 482e9a2d6faSchristos /* Empty a line and set background colour if needed. */ 483e271dbb8Schristos void 484e9a2d6faSchristos grid_empty_line(struct grid *gd, u_int py, u_int bg) 485e9a2d6faSchristos { 486e9a2d6faSchristos memset(&gd->linedata[py], 0, sizeof gd->linedata[py]); 4870a274e86Schristos if (!COLOUR_DEFAULT(bg)) 488e9a2d6faSchristos grid_expand_line(gd, py, gd->sx, bg); 489e9a2d6faSchristos } 490e9a2d6faSchristos 491928fc495Schristos /* Peek at grid line. */ 492928fc495Schristos const struct grid_line * 493928fc495Schristos grid_peek_line(struct grid *gd, u_int py) 494698d5317Sjmmv { 495c7e17de0Schristos if (grid_check_y(gd, __func__, py) != 0) 496928fc495Schristos return (NULL); 497928fc495Schristos return (&gd->linedata[py]); 498698d5317Sjmmv } 499698d5317Sjmmv 500c7e17de0Schristos /* Get cell from line. */ 501c7e17de0Schristos static void 502c7e17de0Schristos grid_get_cell1(struct grid_line *gl, u_int px, struct grid_cell *gc) 503698d5317Sjmmv { 504c7e17de0Schristos struct grid_cell_entry *gce = &gl->celldata[px]; 505e271dbb8Schristos struct grid_extd_entry *gee; 506698d5317Sjmmv 507f26e8bc9Schristos if (gce->flags & GRID_FLAG_EXTENDED) { 508f26e8bc9Schristos if (gce->offset >= gl->extdsize) 509f26e8bc9Schristos memcpy(gc, &grid_default_cell, sizeof *gc); 510e271dbb8Schristos else { 511e271dbb8Schristos gee = &gl->extddata[gce->offset]; 512e271dbb8Schristos gc->flags = gee->flags; 513e271dbb8Schristos gc->attr = gee->attr; 514e271dbb8Schristos gc->fg = gee->fg; 515e271dbb8Schristos gc->bg = gee->bg; 516e271dbb8Schristos gc->us = gee->us; 517f844e94eSwiz gc->link = gee->link; 518e271dbb8Schristos utf8_to_data(gee->data, &gc->data); 519e271dbb8Schristos } 520f26e8bc9Schristos return; 521f26e8bc9Schristos } 522f26e8bc9Schristos 523e9a2d6faSchristos gc->flags = gce->flags & ~(GRID_FLAG_FG256|GRID_FLAG_BG256); 524f26e8bc9Schristos gc->attr = gce->data.attr; 525f26e8bc9Schristos gc->fg = gce->data.fg; 526e9a2d6faSchristos if (gce->flags & GRID_FLAG_FG256) 527e9a2d6faSchristos gc->fg |= COLOUR_FLAG_256; 528f26e8bc9Schristos gc->bg = gce->data.bg; 529e9a2d6faSchristos if (gce->flags & GRID_FLAG_BG256) 530e9a2d6faSchristos gc->bg |= COLOUR_FLAG_256; 531f844e94eSwiz gc->us = 8; 532f26e8bc9Schristos utf8_set(&gc->data, gce->data.data); 533f844e94eSwiz gc->link = 0; 534698d5317Sjmmv } 535698d5317Sjmmv 536c7e17de0Schristos /* Get cell for reading. */ 537c7e17de0Schristos void 538c7e17de0Schristos grid_get_cell(struct grid *gd, u_int px, u_int py, struct grid_cell *gc) 539c7e17de0Schristos { 540c7e17de0Schristos if (grid_check_y(gd, __func__, py) != 0 || 5410a274e86Schristos px >= gd->linedata[py].cellsize) 542c7e17de0Schristos memcpy(gc, &grid_default_cell, sizeof *gc); 5430a274e86Schristos else 5440a274e86Schristos grid_get_cell1(&gd->linedata[py], px, gc); 545c7e17de0Schristos } 546c7e17de0Schristos 547e271dbb8Schristos /* Set cell at position. */ 548698d5317Sjmmv void 549928fc495Schristos grid_set_cell(struct grid *gd, u_int px, u_int py, const struct grid_cell *gc) 550698d5317Sjmmv { 551f26e8bc9Schristos struct grid_line *gl; 552f26e8bc9Schristos struct grid_cell_entry *gce; 553f26e8bc9Schristos 554c7e17de0Schristos if (grid_check_y(gd, __func__, py) != 0) 555698d5317Sjmmv return; 556698d5317Sjmmv 557e9a2d6faSchristos grid_expand_line(gd, py, px + 1, 8); 558f26e8bc9Schristos 559f26e8bc9Schristos gl = &gd->linedata[py]; 560e9a2d6faSchristos if (px + 1 > gl->cellused) 561e9a2d6faSchristos gl->cellused = px + 1; 562e9a2d6faSchristos 563f26e8bc9Schristos gce = &gl->celldata[px]; 564e9a2d6faSchristos if (grid_need_extended_cell(gce, gc)) 565e9a2d6faSchristos grid_extended_cell(gl, gce, gc); 566e9a2d6faSchristos else 567e9a2d6faSchristos grid_store_cell(gce, gc, gc->data.data[0]); 568f26e8bc9Schristos } 569f26e8bc9Schristos 570e271dbb8Schristos /* Set padding at position. */ 571e271dbb8Schristos void 572e271dbb8Schristos grid_set_padding(struct grid *gd, u_int px, u_int py) 573e271dbb8Schristos { 574e271dbb8Schristos grid_set_cell(gd, px, py, &grid_padding_cell); 575e271dbb8Schristos } 576e271dbb8Schristos 577e271dbb8Schristos /* Set cells at position. */ 578e9a2d6faSchristos void 579e9a2d6faSchristos grid_set_cells(struct grid *gd, u_int px, u_int py, const struct grid_cell *gc, 580e9a2d6faSchristos const char *s, size_t slen) 581e9a2d6faSchristos { 582e9a2d6faSchristos struct grid_line *gl; 583e9a2d6faSchristos struct grid_cell_entry *gce; 584e271dbb8Schristos struct grid_extd_entry *gee; 585e9a2d6faSchristos u_int i; 586e9a2d6faSchristos 587c7e17de0Schristos if (grid_check_y(gd, __func__, py) != 0) 588f26e8bc9Schristos return; 589f26e8bc9Schristos 590e9a2d6faSchristos grid_expand_line(gd, py, px + slen, 8); 591e9a2d6faSchristos 592e9a2d6faSchristos gl = &gd->linedata[py]; 593e9a2d6faSchristos if (px + slen > gl->cellused) 594e9a2d6faSchristos gl->cellused = px + slen; 595e9a2d6faSchristos 596e9a2d6faSchristos for (i = 0; i < slen; i++) { 597e9a2d6faSchristos gce = &gl->celldata[px + i]; 598e9a2d6faSchristos if (grid_need_extended_cell(gce, gc)) { 599e271dbb8Schristos gee = grid_extended_cell(gl, gce, gc); 600e271dbb8Schristos gee->data = utf8_build_one(s[i]); 601e9a2d6faSchristos } else 602e9a2d6faSchristos grid_store_cell(gce, gc, s[i]); 603e9a2d6faSchristos } 604698d5317Sjmmv } 605698d5317Sjmmv 606698d5317Sjmmv /* Clear area. */ 607698d5317Sjmmv void 608e9a2d6faSchristos grid_clear(struct grid *gd, u_int px, u_int py, u_int nx, u_int ny, u_int bg) 609698d5317Sjmmv { 6100a274e86Schristos struct grid_line *gl; 61130744affSchristos u_int xx, yy, ox, sx; 612698d5317Sjmmv 613698d5317Sjmmv if (nx == 0 || ny == 0) 614698d5317Sjmmv return; 615698d5317Sjmmv 616698d5317Sjmmv if (px == 0 && nx == gd->sx) { 617e9a2d6faSchristos grid_clear_lines(gd, py, ny, bg); 618698d5317Sjmmv return; 619698d5317Sjmmv } 620698d5317Sjmmv 621c7e17de0Schristos if (grid_check_y(gd, __func__, py) != 0) 622698d5317Sjmmv return; 623c7e17de0Schristos if (grid_check_y(gd, __func__, py + ny - 1) != 0) 624698d5317Sjmmv return; 625698d5317Sjmmv 626698d5317Sjmmv for (yy = py; yy < py + ny; yy++) { 6270a274e86Schristos gl = &gd->linedata[yy]; 62830744affSchristos 62930744affSchristos sx = gd->sx; 63030744affSchristos if (sx > gl->cellsize) 63130744affSchristos sx = gl->cellsize; 63230744affSchristos ox = nx; 63330744affSchristos if (COLOUR_DEFAULT(bg)) { 63430744affSchristos if (px > sx) 635698d5317Sjmmv continue; 63630744affSchristos if (px + nx > sx) 63730744affSchristos ox = sx - px; 638698d5317Sjmmv } 63930744affSchristos 64030744affSchristos grid_expand_line(gd, yy, px + ox, 8); /* default bg first */ 64130744affSchristos for (xx = px; xx < px + ox; xx++) 642e9a2d6faSchristos grid_clear_cell(gd, xx, yy, bg); 643698d5317Sjmmv } 644698d5317Sjmmv } 645698d5317Sjmmv 646698d5317Sjmmv /* Clear lines. This just frees and truncates the lines. */ 647698d5317Sjmmv void 648e9a2d6faSchristos grid_clear_lines(struct grid *gd, u_int py, u_int ny, u_int bg) 649698d5317Sjmmv { 650698d5317Sjmmv u_int yy; 651698d5317Sjmmv 652698d5317Sjmmv if (ny == 0) 653698d5317Sjmmv return; 654698d5317Sjmmv 655c7e17de0Schristos if (grid_check_y(gd, __func__, py) != 0) 656698d5317Sjmmv return; 657c7e17de0Schristos if (grid_check_y(gd, __func__, py + ny - 1) != 0) 658698d5317Sjmmv return; 659698d5317Sjmmv 660698d5317Sjmmv for (yy = py; yy < py + ny; yy++) { 661fe99a117Schristos grid_free_line(gd, yy); 662e9a2d6faSchristos grid_empty_line(gd, yy, bg); 663698d5317Sjmmv } 664e271dbb8Schristos if (py != 0) 665e271dbb8Schristos gd->linedata[py - 1].flags &= ~GRID_LINE_WRAPPED; 666698d5317Sjmmv } 667698d5317Sjmmv 668698d5317Sjmmv /* Move a group of lines. */ 669698d5317Sjmmv void 670e9a2d6faSchristos grid_move_lines(struct grid *gd, u_int dy, u_int py, u_int ny, u_int bg) 671698d5317Sjmmv { 672698d5317Sjmmv u_int yy; 673698d5317Sjmmv 674698d5317Sjmmv if (ny == 0 || py == dy) 675698d5317Sjmmv return; 676698d5317Sjmmv 677c7e17de0Schristos if (grid_check_y(gd, __func__, py) != 0) 678698d5317Sjmmv return; 679c7e17de0Schristos if (grid_check_y(gd, __func__, py + ny - 1) != 0) 680698d5317Sjmmv return; 681c7e17de0Schristos if (grid_check_y(gd, __func__, dy) != 0) 682698d5317Sjmmv return; 683c7e17de0Schristos if (grid_check_y(gd, __func__, dy + ny - 1) != 0) 684698d5317Sjmmv return; 685698d5317Sjmmv 686698d5317Sjmmv /* Free any lines which are being replaced. */ 687698d5317Sjmmv for (yy = dy; yy < dy + ny; yy++) { 688698d5317Sjmmv if (yy >= py && yy < py + ny) 689698d5317Sjmmv continue; 690fe99a117Schristos grid_free_line(gd, yy); 691698d5317Sjmmv } 692e271dbb8Schristos if (dy != 0) 693e271dbb8Schristos gd->linedata[dy - 1].flags &= ~GRID_LINE_WRAPPED; 694698d5317Sjmmv 69599e242abSchristos memmove(&gd->linedata[dy], &gd->linedata[py], 69699e242abSchristos ny * (sizeof *gd->linedata)); 697698d5317Sjmmv 698fe99a117Schristos /* 699fe99a117Schristos * Wipe any lines that have been moved (without freeing them - they are 700fe99a117Schristos * still present). 701fe99a117Schristos */ 702698d5317Sjmmv for (yy = py; yy < py + ny; yy++) { 703e9a2d6faSchristos if (yy < dy || yy >= dy + ny) 704e9a2d6faSchristos grid_empty_line(gd, yy, bg); 705698d5317Sjmmv } 706e271dbb8Schristos if (py != 0 && (py < dy || py >= dy + ny)) 707e271dbb8Schristos gd->linedata[py - 1].flags &= ~GRID_LINE_WRAPPED; 708698d5317Sjmmv } 709698d5317Sjmmv 710698d5317Sjmmv /* Move a group of cells. */ 711698d5317Sjmmv void 712e9a2d6faSchristos grid_move_cells(struct grid *gd, u_int dx, u_int px, u_int py, u_int nx, 713e9a2d6faSchristos u_int bg) 714698d5317Sjmmv { 715698d5317Sjmmv struct grid_line *gl; 716698d5317Sjmmv u_int xx; 717698d5317Sjmmv 718698d5317Sjmmv if (nx == 0 || px == dx) 719698d5317Sjmmv return; 720698d5317Sjmmv 721c7e17de0Schristos if (grid_check_y(gd, __func__, py) != 0) 722698d5317Sjmmv return; 723698d5317Sjmmv gl = &gd->linedata[py]; 724698d5317Sjmmv 725e9a2d6faSchristos grid_expand_line(gd, py, px + nx, 8); 726e9a2d6faSchristos grid_expand_line(gd, py, dx + nx, 8); 72799e242abSchristos memmove(&gl->celldata[dx], &gl->celldata[px], 72899e242abSchristos nx * sizeof *gl->celldata); 729e9a2d6faSchristos if (dx + nx > gl->cellused) 730e9a2d6faSchristos gl->cellused = dx + nx; 731698d5317Sjmmv 732698d5317Sjmmv /* Wipe any cells that have been moved. */ 733698d5317Sjmmv for (xx = px; xx < px + nx; xx++) { 734698d5317Sjmmv if (xx >= dx && xx < dx + nx) 735698d5317Sjmmv continue; 736e9a2d6faSchristos grid_clear_cell(gd, xx, py, bg); 737698d5317Sjmmv } 738698d5317Sjmmv } 739698d5317Sjmmv 740928fc495Schristos /* Get ANSI foreground sequence. */ 741e9a2d6faSchristos static size_t 742928fc495Schristos grid_string_cells_fg(const struct grid_cell *gc, int *values) 743928fc495Schristos { 744928fc495Schristos size_t n; 745e9a2d6faSchristos u_char r, g, b; 746928fc495Schristos 747928fc495Schristos n = 0; 748e9a2d6faSchristos if (gc->fg & COLOUR_FLAG_256) { 749928fc495Schristos values[n++] = 38; 750928fc495Schristos values[n++] = 5; 751e9a2d6faSchristos values[n++] = gc->fg & 0xff; 752e9a2d6faSchristos } else if (gc->fg & COLOUR_FLAG_RGB) { 753f26e8bc9Schristos values[n++] = 38; 754f26e8bc9Schristos values[n++] = 2; 755e9a2d6faSchristos colour_split_rgb(gc->fg, &r, &g, &b); 756e9a2d6faSchristos values[n++] = r; 757e9a2d6faSchristos values[n++] = g; 758e9a2d6faSchristos values[n++] = b; 759928fc495Schristos } else { 760928fc495Schristos switch (gc->fg) { 761928fc495Schristos case 0: 762928fc495Schristos case 1: 763928fc495Schristos case 2: 764928fc495Schristos case 3: 765928fc495Schristos case 4: 766928fc495Schristos case 5: 767928fc495Schristos case 6: 768928fc495Schristos case 7: 769928fc495Schristos values[n++] = gc->fg + 30; 770928fc495Schristos break; 771928fc495Schristos case 8: 772928fc495Schristos values[n++] = 39; 773928fc495Schristos break; 774928fc495Schristos case 90: 775928fc495Schristos case 91: 776928fc495Schristos case 92: 777928fc495Schristos case 93: 778928fc495Schristos case 94: 779928fc495Schristos case 95: 780928fc495Schristos case 96: 781928fc495Schristos case 97: 782928fc495Schristos values[n++] = gc->fg; 783928fc495Schristos break; 784928fc495Schristos } 785928fc495Schristos } 786928fc495Schristos return (n); 787928fc495Schristos } 788928fc495Schristos 789928fc495Schristos /* Get ANSI background sequence. */ 790e9a2d6faSchristos static size_t 791928fc495Schristos grid_string_cells_bg(const struct grid_cell *gc, int *values) 792928fc495Schristos { 793928fc495Schristos size_t n; 794e9a2d6faSchristos u_char r, g, b; 795928fc495Schristos 796928fc495Schristos n = 0; 797e9a2d6faSchristos if (gc->bg & COLOUR_FLAG_256) { 798928fc495Schristos values[n++] = 48; 799928fc495Schristos values[n++] = 5; 800e9a2d6faSchristos values[n++] = gc->bg & 0xff; 801e9a2d6faSchristos } else if (gc->bg & COLOUR_FLAG_RGB) { 802f26e8bc9Schristos values[n++] = 48; 803f26e8bc9Schristos values[n++] = 2; 804e9a2d6faSchristos colour_split_rgb(gc->bg, &r, &g, &b); 805e9a2d6faSchristos values[n++] = r; 806e9a2d6faSchristos values[n++] = g; 807e9a2d6faSchristos values[n++] = b; 808928fc495Schristos } else { 809928fc495Schristos switch (gc->bg) { 810928fc495Schristos case 0: 811928fc495Schristos case 1: 812928fc495Schristos case 2: 813928fc495Schristos case 3: 814928fc495Schristos case 4: 815928fc495Schristos case 5: 816928fc495Schristos case 6: 817928fc495Schristos case 7: 818928fc495Schristos values[n++] = gc->bg + 40; 819928fc495Schristos break; 820928fc495Schristos case 8: 821928fc495Schristos values[n++] = 49; 822928fc495Schristos break; 823e271dbb8Schristos case 90: 824e271dbb8Schristos case 91: 825e271dbb8Schristos case 92: 826e271dbb8Schristos case 93: 827e271dbb8Schristos case 94: 828e271dbb8Schristos case 95: 829e271dbb8Schristos case 96: 830e271dbb8Schristos case 97: 831e271dbb8Schristos values[n++] = gc->bg + 10; 832928fc495Schristos break; 833928fc495Schristos } 834928fc495Schristos } 835928fc495Schristos return (n); 836928fc495Schristos } 837928fc495Schristos 83846548964Swiz /* Get underscore colour sequence. */ 83946548964Swiz static size_t 84046548964Swiz grid_string_cells_us(const struct grid_cell *gc, int *values) 84146548964Swiz { 84246548964Swiz size_t n; 84346548964Swiz u_char r, g, b; 84446548964Swiz 84546548964Swiz n = 0; 84646548964Swiz if (gc->us & COLOUR_FLAG_256) { 84746548964Swiz values[n++] = 58; 84846548964Swiz values[n++] = 5; 84946548964Swiz values[n++] = gc->us & 0xff; 85046548964Swiz } else if (gc->us & COLOUR_FLAG_RGB) { 85146548964Swiz values[n++] = 58; 85246548964Swiz values[n++] = 2; 85346548964Swiz colour_split_rgb(gc->us, &r, &g, &b); 85446548964Swiz values[n++] = r; 85546548964Swiz values[n++] = g; 85646548964Swiz values[n++] = b; 85746548964Swiz } 85846548964Swiz return (n); 85946548964Swiz } 86046548964Swiz 86146548964Swiz /* Add on SGR code. */ 86246548964Swiz static void 86346548964Swiz grid_string_cells_add_code(char *buf, size_t len, u_int n, int *s, int *newc, 864f844e94eSwiz int *oldc, size_t nnewc, size_t noldc, int flags) 86546548964Swiz { 86646548964Swiz u_int i; 86746548964Swiz char tmp[64]; 868f844e94eSwiz int reset = (n != 0 && s[0] == 0); 86946548964Swiz 870f844e94eSwiz if (nnewc == 0) 871f844e94eSwiz return; /* no code to add */ 872f844e94eSwiz if (!reset && 873f844e94eSwiz nnewc == noldc && 874f844e94eSwiz memcmp(newc, oldc, nnewc * sizeof newc[0]) == 0) 875f844e94eSwiz return; /* no reset and colour unchanged */ 876f844e94eSwiz if (reset && (newc[0] == 49 || newc[0] == 39)) 877f844e94eSwiz return; /* reset and colour default */ 878f844e94eSwiz 879f844e94eSwiz if (flags & GRID_STRING_ESCAPE_SEQUENCES) 88046548964Swiz strlcat(buf, "\\033[", len); 88146548964Swiz else 88246548964Swiz strlcat(buf, "\033[", len); 88346548964Swiz for (i = 0; i < nnewc; i++) { 88446548964Swiz if (i + 1 < nnewc) 88546548964Swiz xsnprintf(tmp, sizeof tmp, "%d;", newc[i]); 88646548964Swiz else 88746548964Swiz xsnprintf(tmp, sizeof tmp, "%d", newc[i]); 88846548964Swiz strlcat(buf, tmp, len); 88946548964Swiz } 89046548964Swiz strlcat(buf, "m", len); 89146548964Swiz } 892f844e94eSwiz 893f844e94eSwiz static int 894f844e94eSwiz grid_string_cells_add_hyperlink(char *buf, size_t len, const char *id, 895f844e94eSwiz const char *uri, int flags) 896f844e94eSwiz { 897f844e94eSwiz char *tmp; 898f844e94eSwiz 899f844e94eSwiz if (strlen(uri) + strlen(id) + 17 >= len) 900f844e94eSwiz return (0); 901f844e94eSwiz 902f844e94eSwiz if (flags & GRID_STRING_ESCAPE_SEQUENCES) 903f844e94eSwiz strlcat(buf, "\\033]8;", len); 904f844e94eSwiz else 905f844e94eSwiz strlcat(buf, "\033]8;", len); 906f844e94eSwiz if (*id != '\0') { 907f844e94eSwiz xasprintf(&tmp, "id=%s;", id); 908f844e94eSwiz strlcat(buf, tmp, len); 909f844e94eSwiz free(tmp); 910f844e94eSwiz } else 911f844e94eSwiz strlcat(buf, ";", len); 912f844e94eSwiz strlcat(buf, uri, len); 913f844e94eSwiz if (flags & GRID_STRING_ESCAPE_SEQUENCES) 914f844e94eSwiz strlcat(buf, "\\033\\\\", len); 915f844e94eSwiz else 916f844e94eSwiz strlcat(buf, "\033\\", len); 917f844e94eSwiz return (1); 91846548964Swiz } 91946548964Swiz 920928fc495Schristos /* 921928fc495Schristos * Returns ANSI code to set particular attributes (colour, bold and so on) 922fe99a117Schristos * given a current state. 923928fc495Schristos */ 924e9a2d6faSchristos static void 925928fc495Schristos grid_string_cells_code(const struct grid_cell *lastgc, 926f844e94eSwiz const struct grid_cell *gc, char *buf, size_t len, int flags, 927f844e94eSwiz struct screen *sc, int *has_link) 928928fc495Schristos { 929f26e8bc9Schristos int oldc[64], newc[64], s[128]; 930928fc495Schristos size_t noldc, nnewc, n, i; 931fe99a117Schristos u_int attr = gc->attr, lastattr = lastgc->attr; 932928fc495Schristos char tmp[64]; 933f844e94eSwiz const char *uri, *id; 934928fc495Schristos 935f844e94eSwiz static const struct { 936928fc495Schristos u_int mask; 937928fc495Schristos u_int code; 938928fc495Schristos } attrs[] = { 939928fc495Schristos { GRID_ATTR_BRIGHT, 1 }, 940928fc495Schristos { GRID_ATTR_DIM, 2 }, 941928fc495Schristos { GRID_ATTR_ITALICS, 3 }, 942928fc495Schristos { GRID_ATTR_UNDERSCORE, 4 }, 943928fc495Schristos { GRID_ATTR_BLINK, 5 }, 944928fc495Schristos { GRID_ATTR_REVERSE, 7 }, 945e9a2d6faSchristos { GRID_ATTR_HIDDEN, 8 }, 9460a274e86Schristos { GRID_ATTR_STRIKETHROUGH, 9 }, 9470a274e86Schristos { GRID_ATTR_UNDERSCORE_2, 42 }, 9480a274e86Schristos { GRID_ATTR_UNDERSCORE_3, 43 }, 9490a274e86Schristos { GRID_ATTR_UNDERSCORE_4, 44 }, 9500a274e86Schristos { GRID_ATTR_UNDERSCORE_5, 45 }, 95130744affSchristos { GRID_ATTR_OVERLINE, 53 }, 952928fc495Schristos }; 953928fc495Schristos n = 0; 954928fc495Schristos 955928fc495Schristos /* If any attribute is removed, begin with 0. */ 956928fc495Schristos for (i = 0; i < nitems(attrs); i++) { 95746548964Swiz if (((~attr & attrs[i].mask) && 95846548964Swiz (lastattr & attrs[i].mask)) || 959f844e94eSwiz (lastgc->us != 8 && gc->us == 8)) { 960928fc495Schristos s[n++] = 0; 961928fc495Schristos lastattr &= GRID_ATTR_CHARSET; 962928fc495Schristos break; 963928fc495Schristos } 964928fc495Schristos } 965928fc495Schristos /* For each attribute that is newly set, add its code. */ 966928fc495Schristos for (i = 0; i < nitems(attrs); i++) { 967928fc495Schristos if ((attr & attrs[i].mask) && !(lastattr & attrs[i].mask)) 968928fc495Schristos s[n++] = attrs[i].code; 969928fc495Schristos } 970928fc495Schristos 971fe99a117Schristos /* Write the attributes. */ 972928fc495Schristos *buf = '\0'; 973928fc495Schristos if (n > 0) { 974f844e94eSwiz if (flags & GRID_STRING_ESCAPE_SEQUENCES) 975928fc495Schristos strlcat(buf, "\\033[", len); 976928fc495Schristos else 977928fc495Schristos strlcat(buf, "\033[", len); 978928fc495Schristos for (i = 0; i < n; i++) { 9790a274e86Schristos if (s[i] < 10) 980928fc495Schristos xsnprintf(tmp, sizeof tmp, "%d", s[i]); 9810a274e86Schristos else { 9820a274e86Schristos xsnprintf(tmp, sizeof tmp, "%d:%d", s[i] / 10, 9830a274e86Schristos s[i] % 10); 9840a274e86Schristos } 985928fc495Schristos strlcat(buf, tmp, len); 9860a274e86Schristos if (i + 1 < n) 9870a274e86Schristos strlcat(buf, ";", len); 988928fc495Schristos } 989928fc495Schristos strlcat(buf, "m", len); 990928fc495Schristos } 991928fc495Schristos 992fe99a117Schristos /* If the foreground colour changed, write its parameters. */ 993fe99a117Schristos nnewc = grid_string_cells_fg(gc, newc); 994fe99a117Schristos noldc = grid_string_cells_fg(lastgc, oldc); 99546548964Swiz grid_string_cells_add_code(buf, len, n, s, newc, oldc, nnewc, noldc, 996f844e94eSwiz flags); 997fe99a117Schristos 998fe99a117Schristos /* If the background colour changed, append its parameters. */ 999fe99a117Schristos nnewc = grid_string_cells_bg(gc, newc); 1000fe99a117Schristos noldc = grid_string_cells_bg(lastgc, oldc); 100146548964Swiz grid_string_cells_add_code(buf, len, n, s, newc, oldc, nnewc, noldc, 1002f844e94eSwiz flags); 100346548964Swiz 100446548964Swiz /* If the underscore colour changed, append its parameters. */ 100546548964Swiz nnewc = grid_string_cells_us(gc, newc); 100646548964Swiz noldc = grid_string_cells_us(lastgc, oldc); 100746548964Swiz grid_string_cells_add_code(buf, len, n, s, newc, oldc, nnewc, noldc, 1008f844e94eSwiz flags); 1009fe99a117Schristos 1010928fc495Schristos /* Append shift in/shift out if needed. */ 1011928fc495Schristos if ((attr & GRID_ATTR_CHARSET) && !(lastattr & GRID_ATTR_CHARSET)) { 1012f844e94eSwiz if (flags & GRID_STRING_ESCAPE_SEQUENCES) 1013928fc495Schristos strlcat(buf, "\\016", len); /* SO */ 1014928fc495Schristos else 1015928fc495Schristos strlcat(buf, "\016", len); /* SO */ 1016928fc495Schristos } 1017928fc495Schristos if (!(attr & GRID_ATTR_CHARSET) && (lastattr & GRID_ATTR_CHARSET)) { 1018f844e94eSwiz if (flags & GRID_STRING_ESCAPE_SEQUENCES) 1019928fc495Schristos strlcat(buf, "\\017", len); /* SI */ 1020928fc495Schristos else 1021928fc495Schristos strlcat(buf, "\017", len); /* SI */ 1022928fc495Schristos } 1023f844e94eSwiz 1024f844e94eSwiz /* Add hyperlink if changed. */ 1025f844e94eSwiz if (sc != NULL && sc->hyperlinks != NULL && lastgc->link != gc->link) { 1026f844e94eSwiz if (hyperlinks_get(sc->hyperlinks, gc->link, &uri, &id, NULL)) { 1027f844e94eSwiz *has_link = grid_string_cells_add_hyperlink(buf, len, 1028f844e94eSwiz id, uri, flags); 1029f844e94eSwiz } else if (*has_link) { 1030f844e94eSwiz grid_string_cells_add_hyperlink(buf, len, "", "", 1031f844e94eSwiz flags); 1032f844e94eSwiz *has_link = 0; 1033f844e94eSwiz } 1034f844e94eSwiz } 1035928fc495Schristos } 1036928fc495Schristos 1037698d5317Sjmmv /* Convert cells into a string. */ 1038698d5317Sjmmv char * 1039928fc495Schristos grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx, 1040f844e94eSwiz struct grid_cell **lastgc, int flags, struct screen *s) 1041698d5317Sjmmv { 1042f26e8bc9Schristos struct grid_cell gc; 1043928fc495Schristos static struct grid_cell lastgc1; 1044928fc495Schristos const char *data; 1045f844e94eSwiz char *buf, code[8192]; 1046928fc495Schristos size_t len, off, size, codelen; 1047f844e94eSwiz u_int xx, end; 1048f844e94eSwiz int has_link = 0; 1049928fc495Schristos const struct grid_line *gl; 1050698d5317Sjmmv 1051928fc495Schristos if (lastgc != NULL && *lastgc == NULL) { 1052928fc495Schristos memcpy(&lastgc1, &grid_default_cell, sizeof lastgc1); 1053928fc495Schristos *lastgc = &lastgc1; 1054928fc495Schristos } 1055928fc495Schristos 1056698d5317Sjmmv len = 128; 1057698d5317Sjmmv buf = xmalloc(len); 1058698d5317Sjmmv off = 0; 1059698d5317Sjmmv 1060928fc495Schristos gl = grid_peek_line(gd, py); 1061f844e94eSwiz if (flags & GRID_STRING_EMPTY_CELLS) 1062f844e94eSwiz end = gl->cellsize; 1063f844e94eSwiz else 1064f844e94eSwiz end = gl->cellused; 1065698d5317Sjmmv for (xx = px; xx < px + nx; xx++) { 1066f844e94eSwiz if (gl == NULL || xx >= end) 1067928fc495Schristos break; 1068f26e8bc9Schristos grid_get_cell(gd, xx, py, &gc); 1069f26e8bc9Schristos if (gc.flags & GRID_FLAG_PADDING) 1070698d5317Sjmmv continue; 1071698d5317Sjmmv 1072f844e94eSwiz if (flags & GRID_STRING_WITH_SEQUENCES) { 1073f26e8bc9Schristos grid_string_cells_code(*lastgc, &gc, code, sizeof code, 1074f844e94eSwiz flags, s, &has_link); 1075928fc495Schristos codelen = strlen(code); 1076f26e8bc9Schristos memcpy(*lastgc, &gc, sizeof **lastgc); 1077928fc495Schristos } else 1078928fc495Schristos codelen = 0; 1079698d5317Sjmmv 1080f26e8bc9Schristos data = (void *)gc.data.data; 1081f26e8bc9Schristos size = gc.data.size; 1082f844e94eSwiz if ((flags & GRID_STRING_ESCAPE_SEQUENCES) && 1083f844e94eSwiz size == 1 && 1084f844e94eSwiz *data == '\\') { 1085928fc495Schristos data = "\\\\"; 1086928fc495Schristos size = 2; 1087928fc495Schristos } 1088928fc495Schristos 1089928fc495Schristos while (len < off + size + codelen + 1) { 109099e242abSchristos buf = xreallocarray(buf, 2, len); 1091698d5317Sjmmv len *= 2; 1092698d5317Sjmmv } 1093698d5317Sjmmv 1094928fc495Schristos if (codelen != 0) { 1095928fc495Schristos memcpy(buf + off, code, codelen); 1096928fc495Schristos off += codelen; 1097928fc495Schristos } 1098928fc495Schristos memcpy(buf + off, data, size); 1099928fc495Schristos off += size; 1100698d5317Sjmmv } 1101698d5317Sjmmv 1102f844e94eSwiz if (has_link) { 1103f844e94eSwiz grid_string_cells_add_hyperlink(code, sizeof code, "", "", 1104f844e94eSwiz flags); 1105f844e94eSwiz codelen = strlen(code); 1106f844e94eSwiz while (len < off + size + codelen + 1) { 1107f844e94eSwiz buf = xreallocarray(buf, 2, len); 1108f844e94eSwiz len *= 2; 1109f844e94eSwiz } 1110f844e94eSwiz memcpy(buf + off, code, codelen); 1111f844e94eSwiz off += codelen; 1112f844e94eSwiz } 1113f844e94eSwiz 1114f844e94eSwiz if (flags & GRID_STRING_TRIM_SPACES) { 1115698d5317Sjmmv while (off > 0 && buf[off - 1] == ' ') 1116698d5317Sjmmv off--; 1117928fc495Schristos } 1118698d5317Sjmmv buf[off] = '\0'; 1119928fc495Schristos 1120698d5317Sjmmv return (buf); 1121698d5317Sjmmv } 1122698d5317Sjmmv 1123698d5317Sjmmv /* 1124fe99a117Schristos * Duplicate a set of lines between two grids. Both source and destination 1125fe99a117Schristos * should be big enough. 1126698d5317Sjmmv */ 1127698d5317Sjmmv void 1128928fc495Schristos grid_duplicate_lines(struct grid *dst, u_int dy, struct grid *src, u_int sy, 1129928fc495Schristos u_int ny) 1130698d5317Sjmmv { 1131698d5317Sjmmv struct grid_line *dstl, *srcl; 1132698d5317Sjmmv u_int yy; 1133698d5317Sjmmv 1134698d5317Sjmmv if (dy + ny > dst->hsize + dst->sy) 1135698d5317Sjmmv ny = dst->hsize + dst->sy - dy; 1136698d5317Sjmmv if (sy + ny > src->hsize + src->sy) 1137698d5317Sjmmv ny = src->hsize + src->sy - sy; 1138fe99a117Schristos grid_free_lines(dst, dy, ny); 1139698d5317Sjmmv 1140698d5317Sjmmv for (yy = 0; yy < ny; yy++) { 1141698d5317Sjmmv srcl = &src->linedata[sy]; 1142698d5317Sjmmv dstl = &dst->linedata[dy]; 1143698d5317Sjmmv 1144698d5317Sjmmv memcpy(dstl, srcl, sizeof *dstl); 1145698d5317Sjmmv if (srcl->cellsize != 0) { 114699e242abSchristos dstl->celldata = xreallocarray(NULL, 1147698d5317Sjmmv srcl->cellsize, sizeof *dstl->celldata); 1148698d5317Sjmmv memcpy(dstl->celldata, srcl->celldata, 1149698d5317Sjmmv srcl->cellsize * sizeof *dstl->celldata); 115099e242abSchristos } else 115199e242abSchristos dstl->celldata = NULL; 1152f26e8bc9Schristos if (srcl->extdsize != 0) { 1153f26e8bc9Schristos dstl->extdsize = srcl->extdsize; 1154f26e8bc9Schristos dstl->extddata = xreallocarray(NULL, dstl->extdsize, 1155f26e8bc9Schristos sizeof *dstl->extddata); 1156f26e8bc9Schristos memcpy(dstl->extddata, srcl->extddata, dstl->extdsize * 1157f26e8bc9Schristos sizeof *dstl->extddata); 1158e271dbb8Schristos } else 1159e271dbb8Schristos dstl->extddata = NULL; 1160f26e8bc9Schristos 1161698d5317Sjmmv sy++; 1162698d5317Sjmmv dy++; 1163698d5317Sjmmv } 1164698d5317Sjmmv } 1165928fc495Schristos 1166c7e17de0Schristos /* Mark line as dead. */ 1167e9a2d6faSchristos static void 1168c7e17de0Schristos grid_reflow_dead(struct grid_line *gl) 1169f26e8bc9Schristos { 1170c7e17de0Schristos memset(gl, 0, sizeof *gl); 1171c7e17de0Schristos gl->flags = GRID_LINE_DEAD; 1172c7e17de0Schristos } 1173f26e8bc9Schristos 1174c7e17de0Schristos /* Add lines, return the first new one. */ 1175c7e17de0Schristos static struct grid_line * 1176c7e17de0Schristos grid_reflow_add(struct grid *gd, u_int n) 1177c7e17de0Schristos { 1178c7e17de0Schristos struct grid_line *gl; 1179c7e17de0Schristos u_int sy = gd->sy + n; 1180f26e8bc9Schristos 1181c7e17de0Schristos gd->linedata = xreallocarray(gd->linedata, sy, sizeof *gd->linedata); 1182c7e17de0Schristos gl = &gd->linedata[gd->sy]; 1183c7e17de0Schristos memset(gl, 0, n * (sizeof *gl)); 1184c7e17de0Schristos gd->sy = sy; 1185c7e17de0Schristos return (gl); 1186c7e17de0Schristos } 1187c7e17de0Schristos 1188c7e17de0Schristos /* Move a line across. */ 1189c7e17de0Schristos static struct grid_line * 1190c7e17de0Schristos grid_reflow_move(struct grid *gd, struct grid_line *from) 1191c7e17de0Schristos { 1192c7e17de0Schristos struct grid_line *to; 1193c7e17de0Schristos 1194c7e17de0Schristos to = grid_reflow_add(gd, 1); 1195c7e17de0Schristos memcpy(to, from, sizeof *to); 1196c7e17de0Schristos grid_reflow_dead(from); 1197c7e17de0Schristos return (to); 1198c7e17de0Schristos } 1199c7e17de0Schristos 1200c7e17de0Schristos /* Join line below onto this one. */ 1201c7e17de0Schristos static void 1202c7e17de0Schristos grid_reflow_join(struct grid *target, struct grid *gd, u_int sx, u_int yy, 12030a274e86Schristos u_int width, int already) 1204c7e17de0Schristos { 1205c7e17de0Schristos struct grid_line *gl, *from = NULL; 1206c7e17de0Schristos struct grid_cell gc; 1207c7e17de0Schristos u_int lines, left, i, to, line, want = 0; 1208c7e17de0Schristos u_int at; 1209c7e17de0Schristos int wrapped = 1; 1210c7e17de0Schristos 1211c7e17de0Schristos /* 1212c7e17de0Schristos * Add a new target line. 1213c7e17de0Schristos */ 1214c7e17de0Schristos if (!already) { 1215c7e17de0Schristos to = target->sy; 1216c7e17de0Schristos gl = grid_reflow_move(target, &gd->linedata[yy]); 1217c7e17de0Schristos } else { 1218c7e17de0Schristos to = target->sy - 1; 1219c7e17de0Schristos gl = &target->linedata[to]; 1220c7e17de0Schristos } 1221c7e17de0Schristos at = gl->cellused; 1222c7e17de0Schristos 1223c7e17de0Schristos /* 1224c7e17de0Schristos * Loop until no more to consume or the target line is full. 1225c7e17de0Schristos */ 1226c7e17de0Schristos lines = 0; 1227c7e17de0Schristos for (;;) { 1228c7e17de0Schristos /* 1229c7e17de0Schristos * If this is now the last line, there is nothing more to be 1230c7e17de0Schristos * done. 1231c7e17de0Schristos */ 1232c7e17de0Schristos if (yy + 1 + lines == gd->hsize + gd->sy) 1233c7e17de0Schristos break; 1234c7e17de0Schristos line = yy + 1 + lines; 1235c7e17de0Schristos 1236c7e17de0Schristos /* If the next line is empty, skip it. */ 1237c7e17de0Schristos if (~gd->linedata[line].flags & GRID_LINE_WRAPPED) 1238c7e17de0Schristos wrapped = 0; 1239c7e17de0Schristos if (gd->linedata[line].cellused == 0) { 1240c7e17de0Schristos if (!wrapped) 1241c7e17de0Schristos break; 1242c7e17de0Schristos lines++; 1243f26e8bc9Schristos continue; 1244928fc495Schristos } 1245928fc495Schristos 1246928fc495Schristos /* 1247c7e17de0Schristos * Is the destination line now full? Copy the first character 1248c7e17de0Schristos * separately because we need to leave "from" set to the last 1249c7e17de0Schristos * line if this line is full. 1250928fc495Schristos */ 1251c7e17de0Schristos grid_get_cell1(&gd->linedata[line], 0, &gc); 1252c7e17de0Schristos if (width + gc.data.width > sx) 1253c7e17de0Schristos break; 1254c7e17de0Schristos width += gc.data.width; 1255c7e17de0Schristos grid_set_cell(target, at, to, &gc); 1256c7e17de0Schristos at++; 1257c7e17de0Schristos 1258c7e17de0Schristos /* Join as much more as possible onto the current line. */ 1259c7e17de0Schristos from = &gd->linedata[line]; 1260c7e17de0Schristos for (want = 1; want < from->cellused; want++) { 1261c7e17de0Schristos grid_get_cell1(from, want, &gc); 1262c7e17de0Schristos if (width + gc.data.width > sx) 1263c7e17de0Schristos break; 1264c7e17de0Schristos width += gc.data.width; 1265c7e17de0Schristos 1266c7e17de0Schristos grid_set_cell(target, at, to, &gc); 1267c7e17de0Schristos at++; 1268c7e17de0Schristos } 1269c7e17de0Schristos lines++; 1270c7e17de0Schristos 1271c7e17de0Schristos /* 1272c7e17de0Schristos * If this line wasn't wrapped or we didn't consume the entire 1273c7e17de0Schristos * line, don't try to join any further lines. 1274c7e17de0Schristos */ 1275c7e17de0Schristos if (!wrapped || want != from->cellused || width == sx) 1276c7e17de0Schristos break; 1277c7e17de0Schristos } 1278c7e17de0Schristos if (lines == 0) 1279c7e17de0Schristos return; 1280c7e17de0Schristos 1281c7e17de0Schristos /* 1282c7e17de0Schristos * If we didn't consume the entire final line, then remove what we did 1283c7e17de0Schristos * consume. If we consumed the entire line and it wasn't wrapped, 1284c7e17de0Schristos * remove the wrap flag from this line. 1285c7e17de0Schristos */ 1286c7e17de0Schristos left = from->cellused - want; 1287c7e17de0Schristos if (left != 0) { 1288c7e17de0Schristos grid_move_cells(gd, 0, want, yy + lines, left, 8); 1289c7e17de0Schristos from->cellsize = from->cellused = left; 1290c7e17de0Schristos lines--; 1291c7e17de0Schristos } else if (!wrapped) 1292c7e17de0Schristos gl->flags &= ~GRID_LINE_WRAPPED; 1293c7e17de0Schristos 1294c7e17de0Schristos /* Remove the lines that were completely consumed. */ 1295c7e17de0Schristos for (i = yy + 1; i < yy + 1 + lines; i++) { 1296c7e17de0Schristos free(gd->linedata[i].celldata); 1297c7e17de0Schristos free(gd->linedata[i].extddata); 1298c7e17de0Schristos grid_reflow_dead(&gd->linedata[i]); 1299c7e17de0Schristos } 1300c7e17de0Schristos 13010a274e86Schristos /* Adjust scroll position. */ 1302c7e17de0Schristos if (gd->hscrolled > to + lines) 1303c7e17de0Schristos gd->hscrolled -= lines; 1304c7e17de0Schristos else if (gd->hscrolled > to) 1305c7e17de0Schristos gd->hscrolled = to; 1306c7e17de0Schristos } 1307c7e17de0Schristos 1308c7e17de0Schristos /* Split this line into several new ones */ 1309c7e17de0Schristos static void 1310c7e17de0Schristos grid_reflow_split(struct grid *target, struct grid *gd, u_int sx, u_int yy, 13110a274e86Schristos u_int at) 1312928fc495Schristos { 1313c7e17de0Schristos struct grid_line *gl = &gd->linedata[yy], *first; 1314c7e17de0Schristos struct grid_cell gc; 1315c7e17de0Schristos u_int line, lines, width, i, xx; 1316c7e17de0Schristos u_int used = gl->cellused; 1317c7e17de0Schristos int flags = gl->flags; 1318928fc495Schristos 1319c7e17de0Schristos /* How many lines do we need to insert? We know we need at least two. */ 1320c7e17de0Schristos if (~gl->flags & GRID_LINE_EXTENDED) 1321c7e17de0Schristos lines = 1 + (gl->cellused - 1) / sx; 1322c7e17de0Schristos else { 1323c7e17de0Schristos lines = 2; 1324c7e17de0Schristos width = 0; 1325c7e17de0Schristos for (i = at; i < used; i++) { 1326c7e17de0Schristos grid_get_cell1(gl, i, &gc); 1327c7e17de0Schristos if (width + gc.data.width > sx) { 1328c7e17de0Schristos lines++; 1329c7e17de0Schristos width = 0; 1330c7e17de0Schristos } 1331c7e17de0Schristos width += gc.data.width; 1332c7e17de0Schristos } 1333c7e17de0Schristos } 1334928fc495Schristos 1335c7e17de0Schristos /* Insert new lines. */ 1336c7e17de0Schristos line = target->sy + 1; 1337c7e17de0Schristos first = grid_reflow_add(target, lines); 1338c7e17de0Schristos 1339c7e17de0Schristos /* Copy sections from the original line. */ 1340c7e17de0Schristos width = 0; 1341c7e17de0Schristos xx = 0; 1342c7e17de0Schristos for (i = at; i < used; i++) { 1343c7e17de0Schristos grid_get_cell1(gl, i, &gc); 1344c7e17de0Schristos if (width + gc.data.width > sx) { 1345c7e17de0Schristos target->linedata[line].flags |= GRID_LINE_WRAPPED; 1346c7e17de0Schristos 1347c7e17de0Schristos line++; 1348c7e17de0Schristos width = 0; 1349c7e17de0Schristos xx = 0; 1350c7e17de0Schristos } 1351c7e17de0Schristos width += gc.data.width; 1352c7e17de0Schristos grid_set_cell(target, xx, line, &gc); 1353c7e17de0Schristos xx++; 1354c7e17de0Schristos } 1355c7e17de0Schristos if (flags & GRID_LINE_WRAPPED) 1356c7e17de0Schristos target->linedata[line].flags |= GRID_LINE_WRAPPED; 1357c7e17de0Schristos 1358c7e17de0Schristos /* Move the remainder of the original line. */ 1359c7e17de0Schristos gl->cellsize = gl->cellused = at; 1360c7e17de0Schristos gl->flags |= GRID_LINE_WRAPPED; 1361c7e17de0Schristos memcpy(first, gl, sizeof *first); 1362c7e17de0Schristos grid_reflow_dead(gl); 1363c7e17de0Schristos 13640a274e86Schristos /* Adjust the scroll position. */ 1365c7e17de0Schristos if (yy <= gd->hscrolled) 1366c7e17de0Schristos gd->hscrolled += lines - 1; 1367c7e17de0Schristos 1368c7e17de0Schristos /* 1369c7e17de0Schristos * If the original line had the wrapped flag and there is still space 1370c7e17de0Schristos * in the last new line, try to join with the next lines. 1371c7e17de0Schristos */ 1372c7e17de0Schristos if (width < sx && (flags & GRID_LINE_WRAPPED)) 13730a274e86Schristos grid_reflow_join(target, gd, sx, yy, width, 1); 1374c7e17de0Schristos } 1375c7e17de0Schristos 1376c7e17de0Schristos /* Reflow lines on grid to new width. */ 1377c7e17de0Schristos void 13780a274e86Schristos grid_reflow(struct grid *gd, u_int sx) 1379c7e17de0Schristos { 1380c7e17de0Schristos struct grid *target; 1381c7e17de0Schristos struct grid_line *gl; 1382c7e17de0Schristos struct grid_cell gc; 1383e271dbb8Schristos u_int yy, width, i, at; 1384c7e17de0Schristos 1385c7e17de0Schristos /* 1386c7e17de0Schristos * Create a destination grid. This is just used as a container for the 1387c7e17de0Schristos * line data and may not be fully valid. 1388c7e17de0Schristos */ 1389c7e17de0Schristos target = grid_create(gd->sx, 0, 0); 1390c7e17de0Schristos 1391c7e17de0Schristos /* 1392c7e17de0Schristos * Loop over each source line. 1393c7e17de0Schristos */ 1394c7e17de0Schristos for (yy = 0; yy < gd->hsize + gd->sy; yy++) { 1395c7e17de0Schristos gl = &gd->linedata[yy]; 1396c7e17de0Schristos if (gl->flags & GRID_LINE_DEAD) 1397c7e17de0Schristos continue; 1398c7e17de0Schristos 1399c7e17de0Schristos /* 1400e271dbb8Schristos * Work out the width of this line. at is the point at which 1401e271dbb8Schristos * the available width is hit, and width is the full line 1402e271dbb8Schristos * width. 1403c7e17de0Schristos */ 1404e271dbb8Schristos at = width = 0; 1405c7e17de0Schristos if (~gl->flags & GRID_LINE_EXTENDED) { 1406c7e17de0Schristos width = gl->cellused; 1407c7e17de0Schristos if (width > sx) 1408c7e17de0Schristos at = sx; 1409928fc495Schristos else 1410c7e17de0Schristos at = width; 1411928fc495Schristos } else { 1412c7e17de0Schristos for (i = 0; i < gl->cellused; i++) { 1413c7e17de0Schristos grid_get_cell1(gl, i, &gc); 1414c7e17de0Schristos if (at == 0 && width + gc.data.width > sx) 1415c7e17de0Schristos at = i; 1416c7e17de0Schristos width += gc.data.width; 1417928fc495Schristos } 1418928fc495Schristos } 1419928fc495Schristos 1420c7e17de0Schristos /* 1421e271dbb8Schristos * If the line is exactly right, just move it across 1422e271dbb8Schristos * unchanged. 1423c7e17de0Schristos */ 1424e271dbb8Schristos if (width == sx) { 1425c7e17de0Schristos grid_reflow_move(target, gl); 1426c7e17de0Schristos continue; 1427c7e17de0Schristos } 1428928fc495Schristos 1429c7e17de0Schristos /* 1430c7e17de0Schristos * If the line is too big, it needs to be split, whether or not 1431c7e17de0Schristos * it was previously wrapped. 1432c7e17de0Schristos */ 1433c7e17de0Schristos if (width > sx) { 14340a274e86Schristos grid_reflow_split(target, gd, sx, yy, at); 1435c7e17de0Schristos continue; 1436c7e17de0Schristos } 1437c7e17de0Schristos 1438c7e17de0Schristos /* 1439c7e17de0Schristos * If the line was previously wrapped, join as much as possible 1440c7e17de0Schristos * of the next line. 1441c7e17de0Schristos */ 1442c7e17de0Schristos if (gl->flags & GRID_LINE_WRAPPED) 14430a274e86Schristos grid_reflow_join(target, gd, sx, yy, width, 0); 1444c7e17de0Schristos else 1445c7e17de0Schristos grid_reflow_move(target, gl); 1446c7e17de0Schristos } 1447c7e17de0Schristos 1448c7e17de0Schristos /* 1449c7e17de0Schristos * Replace the old grid with the new. 1450c7e17de0Schristos */ 1451c7e17de0Schristos if (target->sy < gd->sy) 1452c7e17de0Schristos grid_reflow_add(target, gd->sy - target->sy); 1453c7e17de0Schristos gd->hsize = target->sy - gd->sy; 14540a274e86Schristos if (gd->hscrolled > gd->hsize) 14550a274e86Schristos gd->hscrolled = gd->hsize; 1456c7e17de0Schristos free(gd->linedata); 1457c7e17de0Schristos gd->linedata = target->linedata; 1458c7e17de0Schristos free(target); 14590a274e86Schristos } 14600a274e86Schristos 14610a274e86Schristos /* Convert to position based on wrapped lines. */ 14620a274e86Schristos void 14630a274e86Schristos grid_wrap_position(struct grid *gd, u_int px, u_int py, u_int *wx, u_int *wy) 14640a274e86Schristos { 14650a274e86Schristos u_int ax = 0, ay = 0, yy; 14660a274e86Schristos 14670a274e86Schristos for (yy = 0; yy < py; yy++) { 14680a274e86Schristos if (gd->linedata[yy].flags & GRID_LINE_WRAPPED) 14690a274e86Schristos ax += gd->linedata[yy].cellused; 14700a274e86Schristos else { 14710a274e86Schristos ax = 0; 14720a274e86Schristos ay++; 14730a274e86Schristos } 14740a274e86Schristos } 14750a274e86Schristos if (px >= gd->linedata[yy].cellused) 14760a274e86Schristos ax = UINT_MAX; 14770a274e86Schristos else 14780a274e86Schristos ax += px; 14790a274e86Schristos *wx = ax; 14800a274e86Schristos *wy = ay; 14810a274e86Schristos } 14820a274e86Schristos 14830a274e86Schristos /* Convert position based on wrapped lines back. */ 14840a274e86Schristos void 14850a274e86Schristos grid_unwrap_position(struct grid *gd, u_int *px, u_int *py, u_int wx, u_int wy) 14860a274e86Schristos { 1487e271dbb8Schristos u_int yy, ay = 0; 14880a274e86Schristos 14890a274e86Schristos for (yy = 0; yy < gd->hsize + gd->sy - 1; yy++) { 14900a274e86Schristos if (ay == wy) 14910a274e86Schristos break; 1492e271dbb8Schristos if (~gd->linedata[yy].flags & GRID_LINE_WRAPPED) 14930a274e86Schristos ay++; 14940a274e86Schristos } 1495c7e17de0Schristos 1496c7e17de0Schristos /* 14970a274e86Schristos * yy is now 0 on the unwrapped line which contains wx. Walk forwards 14980a274e86Schristos * until we find the end or the line now containing wx. 1499c7e17de0Schristos */ 15000a274e86Schristos if (wx == UINT_MAX) { 15010a274e86Schristos while (gd->linedata[yy].flags & GRID_LINE_WRAPPED) 15020a274e86Schristos yy++; 15030a274e86Schristos wx = gd->linedata[yy].cellused; 15040a274e86Schristos } else { 15050a274e86Schristos while (gd->linedata[yy].flags & GRID_LINE_WRAPPED) { 15060a274e86Schristos if (wx < gd->linedata[yy].cellused) 15070a274e86Schristos break; 15080a274e86Schristos wx -= gd->linedata[yy].cellused; 15090a274e86Schristos yy++; 15100a274e86Schristos } 15110a274e86Schristos } 15120a274e86Schristos *px = wx; 15130a274e86Schristos *py = yy; 1514928fc495Schristos } 151530744affSchristos 151630744affSchristos /* Get length of line. */ 151730744affSchristos u_int 151830744affSchristos grid_line_length(struct grid *gd, u_int py) 151930744affSchristos { 152030744affSchristos struct grid_cell gc; 152130744affSchristos u_int px; 152230744affSchristos 152330744affSchristos px = grid_get_line(gd, py)->cellsize; 152430744affSchristos if (px > gd->sx) 152530744affSchristos px = gd->sx; 152630744affSchristos while (px > 0) { 152730744affSchristos grid_get_cell(gd, px - 1, py, &gc); 1528e271dbb8Schristos if ((gc.flags & GRID_FLAG_PADDING) || 1529e271dbb8Schristos gc.data.size != 1 || 1530e271dbb8Schristos *gc.data.data != ' ') 153130744affSchristos break; 153230744affSchristos px--; 153330744affSchristos } 153430744affSchristos return (px); 153530744affSchristos } 1536