=================================================================== RCS file: /cvsrepo/anoncvs/cvs/src/usr.bin/tmux/grid.c,v retrieving revision 1.78 retrieving revision 1.79 diff -u -r1.78 -r1.79 --- src/usr.bin/tmux/grid.c 2017/11/03 17:02:33 1.78 +++ src/usr.bin/tmux/grid.c 2017/11/15 19:21:24 1.79 @@ -1,4 +1,4 @@ -/* $OpenBSD: grid.c,v 1.78 2017/11/03 17:02:33 nicm Exp $ */ +/* $OpenBSD: grid.c,v 1.79 2017/11/15 19:21:24 nicm Exp $ */ /* * Copyright (c) 2008 Nicholas Marriott @@ -43,22 +43,8 @@ 0, { .data = { 0, 8, 8, ' ' } } }; -static void grid_expand_line(struct grid *, u_int, u_int, u_int); static void grid_empty_line(struct grid *, u_int, u_int); -static void grid_reflow_copy(struct grid_line *, u_int, struct grid_line *, - u_int, u_int); -static void grid_reflow_join(struct grid *, u_int *, struct grid_line *, - u_int); -static void grid_reflow_split(struct grid *, u_int *, struct grid_line *, - u_int, u_int); -static void grid_reflow_move(struct grid *, u_int *, struct grid_line *); - -static size_t grid_string_cells_fg(const struct grid_cell *, int *); -static size_t grid_string_cells_bg(const struct grid_cell *, int *); -static void grid_string_cells_code(const struct grid_cell *, - const struct grid_cell *, char *, size_t, int); - /* Store cell in entry. */ static void grid_store_cell(struct grid_cell_entry *gce, const struct grid_cell *gc, @@ -423,21 +409,12 @@ return (&gd->linedata[py]); } -/* Get cell for reading. */ -void -grid_get_cell(struct grid *gd, u_int px, u_int py, struct grid_cell *gc) +/* Get cell from line. */ +static void +grid_get_cell1(struct grid_line *gl, u_int px, struct grid_cell *gc) { - struct grid_line *gl; - struct grid_cell_entry *gce; + struct grid_cell_entry *gce = &gl->celldata[px]; - if (grid_check_y(gd, py) != 0 || px >= gd->linedata[py].cellsize) { - memcpy(gc, &grid_default_cell, sizeof *gc); - return; - } - - gl = &gd->linedata[py]; - gce = &gl->celldata[px]; - if (gce->flags & GRID_FLAG_EXTENDED) { if (gce->offset >= gl->extdsize) memcpy(gc, &grid_default_cell, sizeof *gc); @@ -457,6 +434,17 @@ utf8_set(&gc->data, gce->data.data); } +/* Get cell for reading. */ +void +grid_get_cell(struct grid *gd, u_int px, u_int py, struct grid_cell *gc) +{ + if (grid_check_y(gd, py) != 0 || px >= gd->linedata[py].cellsize) { + memcpy(gc, &grid_default_cell, sizeof *gc); + return; + } + return (grid_get_cell1(&gd->linedata[py], px, gc)); +} + /* Set cell at relative position. */ void grid_set_cell(struct grid *gd, u_int px, u_int py, const struct grid_cell *gc) @@ -953,164 +941,269 @@ } } -/* Copy a section of a line. */ +/* Join line below onto this one. */ static void -grid_reflow_copy(struct grid_line *dst_gl, u_int to, struct grid_line *src_gl, - u_int from, u_int to_copy) +grid_reflow_join(struct grid *gd, u_int sx, u_int yy, u_int width, u_int *cy) { - struct grid_cell_entry *gce; - u_int i, was; + struct grid_line *gl = &gd->linedata[yy], *from; + struct grid_cell gc; + u_int lines, want, left, i, at = gl->cellused, line; + int wrapped = 1; - memcpy(&dst_gl->celldata[to], &src_gl->celldata[from], - to_copy * sizeof *dst_gl->celldata); + lines = 0; + for (;;) { + /* + * If this is now the last line, there is nothing more to be + * done. + */ + if (yy + lines == gd->hsize + gd->sy) + break; + line = yy + 1 + lines; - for (i = to; i < to + to_copy; i++) { - gce = &dst_gl->celldata[i]; - if (~gce->flags & GRID_FLAG_EXTENDED) + /* If the next line is empty, skip it. */ + if (~gd->linedata[line].flags & GRID_LINE_WRAPPED) + wrapped = 0; + if (gd->linedata[line].cellused == 0) { + if (!wrapped) + break; continue; - was = gce->offset; + } - dst_gl->extddata = xreallocarray(dst_gl->extddata, - dst_gl->extdsize + 1, sizeof *dst_gl->extddata); - gce->offset = dst_gl->extdsize++; - memcpy(&dst_gl->extddata[gce->offset], &src_gl->extddata[was], - sizeof *dst_gl->extddata); - } -} + /* + * Is the destination line now full? Copy the first character + * separately because we need to leave "from" set to the last + * line if this line is full. + */ + grid_get_cell1(&gd->linedata[line], 0, &gc); + if (width + gc.data.width > sx) + break; + width += gc.data.width; + grid_set_cell(gd, at, yy, &gc); + at++; -/* Join line data. */ -static void -grid_reflow_join(struct grid *dst, u_int *py, struct grid_line *src_gl, - u_int new_x) -{ - struct grid_line *dst_gl = &dst->linedata[(*py) - 1]; - u_int left, to_copy, ox, nx; + /* Join as much more as possible onto the current line. */ + from = &gd->linedata[line]; + for (want = 1; want < from->cellused; want++) { + grid_get_cell1(from, want, &gc); + if (width + gc.data.width > sx) + break; + width += gc.data.width; - /* How much is left on the old line? */ - left = new_x - dst_gl->cellused; + grid_set_cell(gd, at, yy, &gc); + at++; + } + lines++; - /* Work out how much to append. */ - to_copy = src_gl->cellused; - if (to_copy > left) - to_copy = left; - ox = dst_gl->cellused; - nx = ox + to_copy; + /* + * If this line wasn't wrapped or we didn't consume the entire + * line, don't try to join any further lines. + */ + if (!wrapped || want != from->cellused || width == sx) + break; + } + if (lines == 0) + return; - /* Resize the destination line. */ - dst_gl->celldata = xreallocarray(dst_gl->celldata, nx, - sizeof *dst_gl->celldata); - dst_gl->cellsize = dst_gl->cellused = nx; + /* + * If we didn't consume the entire final line, then remove what we did + * consume. If we consumed the entire line and it wasn't wrapped, + * remove the wrap flag from this line. + */ + left = from->cellused - want; + if (left != 0) { + grid_move_cells(gd, 0, want, yy + lines, left, 8); + from->cellsize = from->cellused = left; + lines--; + } else if (!wrapped) + gl->flags &= ~GRID_LINE_WRAPPED; - /* Append as much as possible. */ - grid_reflow_copy(dst_gl, ox, src_gl, 0, to_copy); - - /* If there is any left in the source, split it. */ - if (src_gl->cellused > to_copy) { - dst_gl->flags |= GRID_LINE_WRAPPED; - - src_gl->cellused -= to_copy; - grid_reflow_split(dst, py, src_gl, new_x, to_copy); + /* Remove the lines that were completely consumed. */ + if (lines != 0) { + if (yy + lines != gd->hsize + gd->sy) { + memmove(&gd->linedata[yy + 1], + &gd->linedata[yy + lines + 1], + ((gd->hsize + gd->sy) - (yy + lines + 1)) * + (sizeof *gd->linedata)); + } + if (gd->hsize >= lines) + gd->hsize -= lines; + else { + lines -= gd->hsize; + gd->hsize = 0; + for (i = 1; i < lines + 1; i++) + grid_empty_line(gd, gd->hsize + gd->sy - i, 8); + } } + + /* Adjust cursor and scroll positions. */ + if (*cy > yy + lines) + *cy -= lines; + else if (*cy > yy) + *cy = yy; + if (gd->hscrolled > yy + lines) + gd->hscrolled -= lines; + else if (gd->hscrolled > yy) + gd->hscrolled = yy; } -/* Split line data. */ +/* Split this line into several new ones */ static void -grid_reflow_split(struct grid *dst, u_int *py, struct grid_line *src_gl, - u_int new_x, u_int offset) +grid_reflow_split(struct grid *gd, u_int sx, u_int yy, u_int at, u_int *cy) { - struct grid_line *dst_gl = NULL; - u_int to_copy; + struct grid_line *gl = &gd->linedata[yy]; + struct grid_cell gc; + u_int line, lines, width, i, used = gl->cellused, xx; + int flags = gl->flags; - /* Loop and copy sections of the source line. */ - while (src_gl->cellused > 0) { - /* Create new line. */ - if (*py >= dst->hsize + dst->sy) - grid_scroll_history(dst, 8); - dst_gl = &dst->linedata[*py]; - (*py)++; + /* How many lines do we need to insert? We know we need at least one. */ + if (~gl->flags & GRID_LINE_EXTENDED) + lines = (gl->cellused - 1) / sx; + else { + lines = 1; + width = 0; + for (i = at; i < used; i++) { + grid_get_cell1(gl, i, &gc); + if (width + gc.data.width > sx) { + lines++; + width = 0; + } + width += gc.data.width; + } + } - /* How much should we copy? */ - to_copy = new_x; - if (to_copy > src_gl->cellused) - to_copy = src_gl->cellused; + /* Trim the original line size. */ + gl->cellsize = gl->cellused = at; + gl->flags |= GRID_LINE_WRAPPED; - /* Expand destination line. */ - dst_gl->celldata = xreallocarray(NULL, to_copy, - sizeof *dst_gl->celldata); - dst_gl->cellsize = dst_gl->cellused = to_copy; - dst_gl->flags |= GRID_LINE_WRAPPED; + /* Insert new lines. */ + gd->linedata = xreallocarray(gd->linedata, gd->hsize + gd->sy + lines, + sizeof *gd->linedata); + memmove(&gd->linedata[yy + lines + 1], &gd->linedata[yy + 1], + ((gd->hsize + gd->sy) - (yy + 1)) * (sizeof *gd->linedata)); + gd->hsize += lines; + for (i = 0; i < lines; i++) + grid_empty_line(gd, yy + 1 + i, 8); + gl = &gd->linedata[yy]; - /* Copy the data. */ - grid_reflow_copy(dst_gl, 0, src_gl, offset, to_copy); + /* Copy sections from the original line. */ + line = yy + 1; + width = 0; + xx = 0; + for (i = at; i < used; i++) { + grid_get_cell1(gl, i, &gc); + if (width + gc.data.width > sx) { + gd->linedata[line].flags |= GRID_LINE_WRAPPED; - /* Move offset and reduce old line size. */ - offset += to_copy; - src_gl->cellused -= to_copy; + line++; + width = 0; + xx = 0; + } + width += gc.data.width; + grid_set_cell(gd, xx, line, &gc); + xx++; } + if (flags & GRID_LINE_WRAPPED) + gd->linedata[line].flags |= GRID_LINE_WRAPPED; - /* Last line is not wrapped. */ - if (dst_gl != NULL) - dst_gl->flags &= ~GRID_LINE_WRAPPED; + /* Adjust the cursor and scroll positions. */ + if (yy <= *cy) + (*cy) += lines; + if (yy <= gd->hscrolled) + gd->hscrolled += lines; + + /* + * If the original line had the wrapped flag and there is still space + * in the last new line, try to join with the next lines. + */ + if (width < sx && (flags & GRID_LINE_WRAPPED)) + grid_reflow_join(gd, sx, line, width, cy); } -/* Move line data. */ -static void -grid_reflow_move(struct grid *dst, u_int *py, struct grid_line *src_gl) +/* Reflow lines on grid to new width. */ +void +grid_reflow(struct grid *gd, u_int sx, u_int *cursor) { - struct grid_line *dst_gl; + struct grid_line *gl; + struct grid_cell gc; + u_int yy, cy, width, i, at, first; + struct timeval start, tv; - /* Create new line. */ - if (*py >= dst->hsize + dst->sy) - grid_scroll_history(dst, 8); - dst_gl = &dst->linedata[*py]; - (*py)++; + gettimeofday(&start, NULL); - /* Copy the old line. */ - memcpy(dst_gl, src_gl, sizeof *dst_gl); - dst_gl->flags &= ~GRID_LINE_WRAPPED; + log_debug("%s: %u lines, new width %u", __func__, gd->hsize + gd->sy, + sx); + cy = gd->hsize + (*cursor); - /* Clear old line. */ - src_gl->celldata = NULL; - src_gl->extddata = NULL; -} + /* + * Loop over lines from top to bottom. The size may change during the + * loop, but it is OK because we are always adding or removing lines + * below the current one. + */ + for (yy = 0; yy < gd->hsize + gd->sy; yy++) { + gl = &gd->linedata[yy]; -/* - * Reflow lines from src grid into dst grid of width new_x. Returns number of - * lines fewer in the visible area. The source grid is destroyed. - */ -u_int -grid_reflow(struct grid *dst, struct grid *src, u_int new_x) -{ - u_int py, sy, line; - int previous_wrapped; - struct grid_line *src_gl; - - py = 0; - sy = src->sy; - - previous_wrapped = 0; - for (line = 0; line < sy + src->hsize; line++) { - src_gl = src->linedata + line; - if (!previous_wrapped) { - /* Wasn't wrapped. If smaller, move to destination. */ - if (src_gl->cellused <= new_x) - grid_reflow_move(dst, &py, src_gl); + /* Work out the width of this line. */ + first = at = width = 0; + if (~gl->flags & GRID_LINE_EXTENDED) { + first = 1; + width = gl->cellused; + if (width > sx) + at = sx; else - grid_reflow_split(dst, &py, src_gl, new_x, 0); + at = width; } else { - /* Previous was wrapped. Try to join. */ - grid_reflow_join(dst, &py, src_gl, new_x); + for (i = 0; i < gl->cellused; i++) { + grid_get_cell1(gl, i, &gc); + if (i == 0) + first = gc.data.width; + if (at == 0 && width + gc.data.width > sx) + at = i; + width += gc.data.width; + } } - previous_wrapped = (src_gl->flags & GRID_LINE_WRAPPED); - /* This is where we started scrolling. */ - if (line == sy + src->hsize - src->hscrolled - 1) - dst->hscrolled = 0; + /* + * If the line is exactly right, there is no need to do + * anything. + */ + if (width == sx) + continue; + + /* + * If the first character is wider than the target width, there + * is no point in trying to do anything. + */ + if (first > sx) + continue; + + /* + * If the line is too big, it needs to be split, whether or not + * it was previously wrapped. + */ + if (width > sx) { + grid_reflow_split(gd, sx, yy, at, &cy); + continue; + } + + /* + * If the line was previously wrapped, join as much as possible + * of the next line. + */ + if (gl->flags & GRID_LINE_WRAPPED) + grid_reflow_join(gd, sx, yy, width, &cy); + } - grid_destroy(src); + if (gd->hscrolled > gd->hsize) + gd->hscrolled = gd->hsize; + if (cy < gd->hsize) + *cursor = 0; + else + *cursor = cy - gd->hsize; - if (py > sy) - return (0); - return (sy - py); + gettimeofday(&tv, NULL); + timersub(&tv, &start, &tv); + log_debug("%s: now %u lines (in %llu.%06u seconds)", __func__, + gd->hsize + gd->sy, (unsigned long long)tv.tv_sec, + (u_int)tv.tv_usec); }