=================================================================== RCS file: /cvsrepo/anoncvs/cvs/src/usr.bin/tmux/grid.c,v retrieving revision 1.78 retrieving revision 1.79 diff -c -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 **** ! /* $OpenBSD: grid.c,v 1.78 2017/11/03 17:02:33 nicm Exp $ */ /* * Copyright (c) 2008 Nicholas Marriott --- 1,4 ---- ! /* $OpenBSD: grid.c,v 1.79 2017/11/15 19:21:24 nicm Exp $ */ /* * Copyright (c) 2008 Nicholas Marriott *************** *** 43,64 **** 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, --- 43,50 ---- *************** *** 423,443 **** 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) { ! struct grid_line *gl; ! struct grid_cell_entry *gce; - 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); --- 409,420 ---- return (&gd->linedata[py]); } ! /* Get cell from line. */ ! static void ! grid_get_cell1(struct grid_line *gl, u_int px, struct grid_cell *gc) { ! struct grid_cell_entry *gce = &gl->celldata[px]; if (gce->flags & GRID_FLAG_EXTENDED) { if (gce->offset >= gl->extdsize) memcpy(gc, &grid_default_cell, sizeof *gc); *************** *** 457,462 **** --- 434,450 ---- 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,1116 **** } } ! /* Copy a section of a line. */ 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) { ! struct grid_cell_entry *gce; ! u_int i, was; ! memcpy(&dst_gl->celldata[to], &src_gl->celldata[from], ! to_copy * sizeof *dst_gl->celldata); ! for (i = to; i < to + to_copy; i++) { ! gce = &dst_gl->celldata[i]; ! if (~gce->flags & GRID_FLAG_EXTENDED) 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); ! } ! } ! /* 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; ! /* How much is left on the old line? */ ! left = new_x - dst_gl->cellused; ! /* 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; ! /* Resize the destination line. */ ! dst_gl->celldata = xreallocarray(dst_gl->celldata, nx, ! sizeof *dst_gl->celldata); ! dst_gl->cellsize = dst_gl->cellused = nx; ! /* 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); } } ! /* Split line data. */ static void ! grid_reflow_split(struct grid *dst, u_int *py, struct grid_line *src_gl, ! u_int new_x, u_int offset) { ! struct grid_line *dst_gl = NULL; ! u_int to_copy; ! /* 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 much should we copy? */ ! to_copy = new_x; ! if (to_copy > src_gl->cellused) ! to_copy = src_gl->cellused; ! /* 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; ! /* Copy the data. */ ! grid_reflow_copy(dst_gl, 0, src_gl, offset, to_copy); ! /* Move offset and reduce old line size. */ ! offset += to_copy; ! src_gl->cellused -= to_copy; } ! /* Last line is not wrapped. */ ! if (dst_gl != NULL) ! dst_gl->flags &= ~GRID_LINE_WRAPPED; } ! /* Move line data. */ ! static void ! grid_reflow_move(struct grid *dst, u_int *py, struct grid_line *src_gl) { ! struct grid_line *dst_gl; ! /* Create new line. */ ! if (*py >= dst->hsize + dst->sy) ! grid_scroll_history(dst, 8); ! dst_gl = &dst->linedata[*py]; ! (*py)++; ! /* Copy the old line. */ ! memcpy(dst_gl, src_gl, sizeof *dst_gl); ! dst_gl->flags &= ~GRID_LINE_WRAPPED; ! /* Clear old line. */ ! src_gl->celldata = NULL; ! src_gl->extddata = NULL; ! } ! /* ! * 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); else ! grid_reflow_split(dst, &py, src_gl, new_x, 0); } else { ! /* Previous was wrapped. Try to join. */ ! grid_reflow_join(dst, &py, src_gl, new_x); } - 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; } ! grid_destroy(src); ! if (py > sy) ! return (0); ! return (sy - py); } --- 941,1209 ---- } } ! /* Join line below onto this one. */ static void ! grid_reflow_join(struct grid *gd, u_int sx, u_int yy, u_int width, u_int *cy) { ! 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; ! 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; ! /* 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; ! } ! /* ! * 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 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; ! grid_set_cell(gd, at, yy, &gc); ! at++; ! } ! lines++; ! /* ! * 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; ! /* ! * 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; ! /* 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 this line into several new ones */ static void ! grid_reflow_split(struct grid *gd, u_int sx, u_int yy, u_int at, u_int *cy) { ! 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; ! /* 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; ! } ! } ! /* Trim the original line size. */ ! gl->cellsize = gl->cellused = at; ! 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 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; ! 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; ! /* 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); } ! /* Reflow lines on grid to new width. */ ! void ! grid_reflow(struct grid *gd, u_int sx, u_int *cursor) { ! struct grid_line *gl; ! struct grid_cell gc; ! u_int yy, cy, width, i, at, first; ! struct timeval start, tv; ! gettimeofday(&start, NULL); ! log_debug("%s: %u lines, new width %u", __func__, gd->hsize + gd->sy, ! sx); ! cy = gd->hsize + (*cursor); ! /* ! * 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]; ! /* 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 ! at = width; } else { ! 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; ! } } ! /* ! * 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); ! } ! if (gd->hscrolled > gd->hsize) ! gd->hscrolled = gd->hsize; ! if (cy < gd->hsize) ! *cursor = 0; ! else ! *cursor = cy - gd->hsize; ! 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); }