diff --git a/grid-view.c b/grid-view.c index da0433bf..b84ac64e 100644 --- a/grid-view.c +++ b/grid-view.c @@ -45,6 +45,15 @@ grid_view_set_cell(struct grid *gd, u_int px, u_int py, grid_set_cell(gd, grid_view_x(gd, px), grid_view_y(gd, py), gc); } +/* Set cells. */ +void +grid_view_set_cells(struct grid *gd, u_int px, u_int py, + const struct grid_cell *gc, const char *s, size_t slen) +{ + grid_set_cells(gd, grid_view_x(gd, px), grid_view_y(gd, py), gc, s, + slen); +} + /* Clear into history. */ void grid_view_clear_history(struct grid *gd, u_int bg) diff --git a/grid.c b/grid.c index 7fcddf61..388a2c1b 100644 --- a/grid.c +++ b/grid.c @@ -59,6 +59,25 @@ 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, + u_char c) +{ + gce->flags = gc->flags; + + gce->data.fg = gc->fg & 0xff; + if (gc->fg & COLOUR_FLAG_256) + gce->flags |= GRID_FLAG_FG256; + + gce->data.bg = gc->bg & 0xff; + if (gc->bg & COLOUR_FLAG_256) + gce->flags |= GRID_FLAG_BG256; + + gce->data.attr = gc->attr; + gce->data.data = c; +} + /* Set cell as extended. */ static struct grid_cell * grid_extended_cell(struct grid_line *gl, struct grid_cell_entry *gce, @@ -371,11 +390,10 @@ grid_set_cell(struct grid *gd, u_int px, u_int py, const struct grid_cell *gc) grid_expand_line(gd, py, px + 1, 8); gl = &gd->linedata[py]; - gce = &gl->celldata[px]; - if (px + 1 > gl->cellused) gl->cellused = px + 1; + gce = &gl->celldata[px]; extended = (gce->flags & GRID_FLAG_EXTENDED); if (!extended && (gc->data.size != 1 || gc->data.width != 1)) extended = 1; @@ -383,20 +401,40 @@ grid_set_cell(struct grid *gd, u_int px, u_int py, const struct grid_cell *gc) extended = 1; if (!extended && (gc->bg & COLOUR_FLAG_RGB)) extended = 1; - if (extended) { + if (extended) grid_extended_cell(gl, gce, gc); - return; - } + else + grid_store_cell(gce, gc, gc->data.data[0]); +} - gce->flags = gc->flags; - gce->data.attr = gc->attr; - gce->data.fg = gc->fg & 0xff; - if (gc->fg & COLOUR_FLAG_256) - gce->flags |= GRID_FLAG_FG256; - gce->data.bg = gc->bg & 0xff; - if (gc->bg & COLOUR_FLAG_256) - gce->flags |= GRID_FLAG_BG256; - gce->data.data = gc->data.data[0]; +/* Set cells at relative position. */ +void +grid_set_cells(struct grid *gd, u_int px, u_int py, const struct grid_cell *gc, + const char *s, size_t slen) +{ + struct grid_line *gl; + struct grid_cell_entry *gce; + struct grid_cell *gcp; + u_int i; + + if (grid_check_y(gd, py) != 0) + return; + + grid_expand_line(gd, py, px + slen, 8); + + gl = &gd->linedata[py]; + if (px + slen > gl->cellused) + gl->cellused = px + slen; + + for (i = 0; i < slen; i++) { + gce = &gl->celldata[px + i]; + if (gce->flags & GRID_FLAG_EXTENDED) { + gcp = &gl->extddata[gce->offset]; + memcpy(gcp, gc, sizeof *gcp); + utf8_set(&gcp->data, s[i]); + } else + grid_store_cell(gce, gc, s[i]); + } } /* Clear area. */ diff --git a/input.c b/input.c index 26eb1686..b96ac7ef 100644 --- a/input.c +++ b/input.c @@ -222,6 +222,7 @@ enum input_csi_type { INPUT_CSI_SGR, INPUT_CSI_SM, INPUT_CSI_SM_PRIVATE, + INPUT_CSI_SU, INPUT_CSI_TBC, INPUT_CSI_VPA, INPUT_CSI_WINOPS, @@ -243,6 +244,7 @@ static const struct input_table_entry input_csi_table[] = { { 'L', "", INPUT_CSI_IL }, { 'M', "", INPUT_CSI_DL }, { 'P', "", INPUT_CSI_DCH }, + { 'S', "", INPUT_CSI_SU }, { 'X', "", INPUT_CSI_ECH }, { 'Z', "", INPUT_CSI_CBT }, { 'c', "", INPUT_CSI_DA }, @@ -895,6 +897,16 @@ input_parse(struct window_pane *wp) fatalx("no transition from state"); } + /* + * Any state except print stops the current collection. This is + * an optimization to avoid checking if the attributes have + * changed for every character. It will stop unnecessarily for + * sequences that don't make a terminal change, but they should + * be the minority. + */ + if (itr->handler != input_print) + screen_write_collect_end(&ictx->ctx); + /* * Execute the handler, if any. Don't switch state if it * returns non-zero. @@ -920,7 +932,6 @@ input_parse(struct window_pane *wp) /* Split the parameter list (if any). */ static int input_split(struct input_ctx *ictx) - { const char *errstr; char *ptr, *out; @@ -1021,7 +1032,7 @@ input_print(struct input_ctx *ictx) ictx->cell.cell.attr &= ~GRID_ATTR_CHARSET; utf8_set(&ictx->cell.cell.data, ictx->ch); - screen_write_cell(&ictx->ctx, &ictx->cell.cell); + screen_write_collect_add(&ictx->ctx, &ictx->cell.cell); ictx->cell.cell.attr &= ~GRID_ATTR_CHARSET; @@ -1404,6 +1415,9 @@ input_csi_dispatch(struct input_ctx *ictx) case INPUT_CSI_SM_PRIVATE: input_csi_dispatch_sm_private(ictx); break; + case INPUT_CSI_SU: + screen_write_scrollup(sctx, input_get(ictx, 0, 1, 1)); + break; case INPUT_CSI_TBC: switch (input_get(ictx, 0, 0, 0)) { case 0: diff --git a/screen-redraw.c b/screen-redraw.c index ae2948aa..c2e39410 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -423,6 +423,9 @@ screen_redraw_pane(struct client *c, struct window_pane *wp) if (status_at_line(c) == 0) yoff++; + log_debug("%s: redraw pane %%%u (at %u,%u)", c->tty.path, wp->id, + wp->xoff, yoff); + for (i = 0; i < wp->sy; i++) tty_draw_pane(&c->tty, wp, i, wp->xoff, yoff); tty_reset(&c->tty); diff --git a/screen-write.c b/screen-write.c index eff0195b..bd615a09 100644 --- a/screen-write.c +++ b/screen-write.c @@ -25,7 +25,10 @@ static void screen_write_initctx(struct screen_write_ctx *, struct tty_ctx *); -static void screen_write_flush(struct screen_write_ctx *); +static void screen_write_collect_clear(struct screen_write_ctx *, u_int, + u_int); +static void screen_write_collect_scroll(struct screen_write_ctx *); +static void screen_write_collect_flush(struct screen_write_ctx *); static int screen_write_overwrite(struct screen_write_ctx *, struct grid_cell *, u_int); @@ -36,24 +39,29 @@ static const struct grid_cell screen_write_pad_cell = { GRID_FLAG_PADDING, 0, 8, 8, { { 0 }, 0, 0, 0 } }; -#define screen_dirty_bit(s, x, y) (((y) * screen_size_x(s)) + (x)) -#define screen_dirty_clear(s, sx, sy, ex, ey) \ - do { \ - if (s->dirty != NULL) { \ - bit_nclear(s->dirty, \ - screen_dirty_bit(s, sx, sy), \ - screen_dirty_bit(s, ex, ey)); \ - } \ - } while (0) +struct screen_write_collect_item { + u_int x; + + u_int used; + char data[256]; + + struct grid_cell gc; + + TAILQ_ENTRY (screen_write_collect_item) entry; +}; +struct screen_write_collect_line { + TAILQ_HEAD(, screen_write_collect_item) items; +}; /* Initialize writing with a window. */ void screen_write_start(struct screen_write_ctx *ctx, struct window_pane *wp, struct screen *s) { - u_int size; - char tmp[16]; - const char *cp = tmp; + char tmp[16]; + u_int y; + + memset(ctx, 0, sizeof *ctx); ctx->wp = wp; if (wp != NULL && s == NULL) @@ -61,77 +69,29 @@ screen_write_start(struct screen_write_ctx *ctx, struct window_pane *wp, else ctx->s = s; - size = screen_size_x(ctx->s) * screen_size_y(ctx->s); - if (ctx->s->dirtysize != size) { - free(ctx->s->dirty); - ctx->s->dirty = NULL; - ctx->s->dirtysize = size; - } - ctx->dirty = 0; + ctx->list = xcalloc(screen_size_y(ctx->s), sizeof *ctx->list); + for (y = 0; y < screen_size_y(ctx->s); y++) + TAILQ_INIT(&ctx->list[y].items); + ctx->item = xcalloc(1, sizeof *ctx->item); - ctx->cells = ctx->written = ctx->skipped = 0; - - if (wp == NULL) - cp = "no pane"; - else + if (wp != NULL) snprintf(tmp, sizeof tmp, "pane %%%u", wp->id); log_debug("%s: size %ux%u, %s", __func__, screen_size_x(ctx->s), - screen_size_y(ctx->s), cp); + screen_size_y(ctx->s), wp == NULL ? "no pane" : tmp); } /* Finish writing. */ void screen_write_stop(struct screen_write_ctx *ctx) { - screen_write_flush(ctx); + screen_write_collect_end(ctx); + screen_write_collect_flush(ctx); - log_debug("%s: %u of %u written (dirty %u, skipped %u)", __func__, - ctx->written, ctx->cells, ctx->cells - ctx->written, ctx->skipped); -} + log_debug("%s: %u cells (%u written, %u skipped)", __func__, + ctx->cells, ctx->written, ctx->skipped); -/* Flush outstanding cell writes. */ -static void -screen_write_flush(struct screen_write_ctx *ctx) -{ - struct screen *s = ctx->s; - struct tty_ctx ttyctx; - u_int x, y, offset, cx, cy, dirty; - struct grid_cell gc; - - if (ctx->dirty == 0) - return; - dirty = 0; - log_debug("%s: dirty %u", __func__, ctx->dirty); - - cx = s->cx; - cy = s->cy; - - offset = 0; - for (y = 0; y < screen_size_y(s); y++) { - for (x = 0; x < screen_size_x(s); x++) { - offset++; - if (!bit_test(s->dirty, offset - 1)) - continue; - bit_clear(s->dirty, offset - 1); - - screen_write_cursormove(ctx, x, y); - grid_view_get_cell(s->grid, x, y, &gc); - - screen_write_initctx(ctx, &ttyctx); - ttyctx.cell = &gc; - tty_write(tty_cmd_cell, &ttyctx); - ctx->written++; - - if (++dirty == ctx->dirty) - break; - } - if (dirty == ctx->dirty) - break; - } - ctx->dirty = 0; - - s->cx = cx; - s->cy = cy; + free(ctx->item); + free(ctx->list); /* flush will have emptied */ } /* Reset screen state. */ @@ -566,12 +526,9 @@ screen_write_alignmenttest(struct screen_write_ctx *ctx) struct tty_ctx ttyctx; struct grid_cell gc; u_int xx, yy; - u_int sx = screen_size_x(s), sy = screen_size_y(s); screen_write_initctx(ctx, &ttyctx); - screen_dirty_clear(s, 0, 0, sx - 1, sy - 1); - memcpy(&gc, &grid_default_cell, sizeof gc); utf8_set(&gc.data, 'E'); @@ -586,6 +543,7 @@ screen_write_alignmenttest(struct screen_write_ctx *ctx) s->rupper = 0; s->rlower = screen_size_y(s) - 1; + screen_write_collect_clear(ctx, 0, screen_size_y(s) - 1); tty_write(tty_cmd_alignmenttest, &ttyctx); } @@ -604,14 +562,16 @@ screen_write_insertcharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg) if (nx == 0) return; - screen_write_flush(ctx); + if (s->cx > screen_size_x(s) - 1) + return; + screen_write_initctx(ctx, &ttyctx); - - if (s->cx <= screen_size_x(s) - 1) - grid_view_insert_cells(s->grid, s->cx, s->cy, nx, bg); - - ttyctx.num = nx; ttyctx.bg = bg; + + grid_view_insert_cells(s->grid, s->cx, s->cy, nx, bg); + + screen_write_collect_flush(ctx); + ttyctx.num = nx; tty_write(tty_cmd_insertcharacter, &ttyctx); } @@ -630,14 +590,16 @@ screen_write_deletecharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg) if (nx == 0) return; - screen_write_flush(ctx); + if (s->cx > screen_size_x(s) - 1) + return; + screen_write_initctx(ctx, &ttyctx); - - if (s->cx <= screen_size_x(s) - 1) - grid_view_delete_cells(s->grid, s->cx, s->cy, nx, bg); - - ttyctx.num = nx; ttyctx.bg = bg; + + grid_view_delete_cells(s->grid, s->cx, s->cy, nx, bg); + + screen_write_collect_flush(ctx); + ttyctx.num = nx; tty_write(tty_cmd_deletecharacter, &ttyctx); } @@ -656,14 +618,14 @@ screen_write_clearcharacter(struct screen_write_ctx *ctx, u_int nx) if (nx == 0) return; - screen_write_initctx(ctx, &ttyctx); - - if (s->cx <= screen_size_x(s) - 1) { - screen_dirty_clear(s, s->cx, s->cy, s->cx + nx - 1, s->cy); - grid_view_clear(s->grid, s->cx, s->cy, nx, 1, 8); - } else + if (s->cx > screen_size_x(s) - 1) return; + screen_write_initctx(ctx, &ttyctx); + + grid_view_clear(s->grid, s->cx, s->cy, nx, 1, 8); + + screen_write_collect_flush(ctx); ttyctx.num = nx; tty_write(tty_cmd_clearcharacter, &ttyctx); } @@ -673,6 +635,7 @@ void screen_write_insertline(struct screen_write_ctx *ctx, u_int ny, u_int bg) { struct screen *s = ctx->s; + struct grid *gd = s->grid; struct tty_ctx ttyctx; if (ny == 0) @@ -684,13 +647,13 @@ screen_write_insertline(struct screen_write_ctx *ctx, u_int ny, u_int bg) if (ny == 0) return; - screen_write_flush(ctx); screen_write_initctx(ctx, &ttyctx); - - grid_view_insert_lines(s->grid, s->cy, ny, bg); - - ttyctx.num = ny; ttyctx.bg = bg; + + grid_view_insert_lines(gd, s->cy, ny, bg); + + screen_write_collect_flush(ctx); + ttyctx.num = ny; tty_write(tty_cmd_insertline, &ttyctx); return; } @@ -700,18 +663,16 @@ screen_write_insertline(struct screen_write_ctx *ctx, u_int ny, u_int bg) if (ny == 0) return; - screen_write_flush(ctx); screen_write_initctx(ctx, &ttyctx); + ttyctx.bg = bg; if (s->cy < s->rupper || s->cy > s->rlower) - grid_view_insert_lines(s->grid, s->cy, ny, bg); - else { - grid_view_insert_lines_region(s->grid, s->rlower, s->cy, ny, - bg); - } + grid_view_insert_lines(gd, s->cy, ny, bg); + else + grid_view_insert_lines_region(gd, s->rlower, s->cy, ny, bg); + screen_write_collect_flush(ctx); ttyctx.num = ny; - ttyctx.bg = bg; tty_write(tty_cmd_insertline, &ttyctx); } @@ -720,6 +681,7 @@ void screen_write_deleteline(struct screen_write_ctx *ctx, u_int ny, u_int bg) { struct screen *s = ctx->s; + struct grid *gd = s->grid; struct tty_ctx ttyctx; if (ny == 0) @@ -731,13 +693,13 @@ screen_write_deleteline(struct screen_write_ctx *ctx, u_int ny, u_int bg) if (ny == 0) return; - screen_write_flush(ctx); screen_write_initctx(ctx, &ttyctx); - - grid_view_delete_lines(s->grid, s->cy, ny, bg); - - ttyctx.num = ny; ttyctx.bg = bg; + + grid_view_delete_lines(gd, s->cy, ny, bg); + + screen_write_collect_flush(ctx); + ttyctx.num = ny; tty_write(tty_cmd_deleteline, &ttyctx); return; } @@ -747,18 +709,16 @@ screen_write_deleteline(struct screen_write_ctx *ctx, u_int ny, u_int bg) if (ny == 0) return; - screen_write_flush(ctx); screen_write_initctx(ctx, &ttyctx); + ttyctx.bg = bg; if (s->cy < s->rupper || s->cy > s->rlower) - grid_view_delete_lines(s->grid, s->cy, ny, bg); - else { - grid_view_delete_lines_region(s->grid, s->rlower, s->cy, ny, - bg); - } + grid_view_delete_lines(gd, s->cy, ny, bg); + else + grid_view_delete_lines_region(gd, s->rlower, s->cy, ny, bg); + screen_write_collect_flush(ctx); ttyctx.num = ny; - ttyctx.bg = bg; tty_write(tty_cmd_deleteline, &ttyctx); } @@ -771,16 +731,16 @@ screen_write_clearline(struct screen_write_ctx *ctx, u_int bg) struct tty_ctx ttyctx; u_int sx = screen_size_x(s); - screen_write_initctx(ctx, &ttyctx); - ttyctx.bg = bg; - gl = &s->grid->linedata[s->grid->hsize + s->cy]; if (gl->cellsize == 0 && bg == 8) return; - screen_dirty_clear(s, 0, s->cy, sx - 1, s->cy); + screen_write_initctx(ctx, &ttyctx); + ttyctx.bg = bg; + grid_view_clear(s->grid, 0, s->cy, sx, 1, bg); + screen_write_collect_clear(ctx, s->cy, 1); tty_write(tty_cmd_clearline, &ttyctx); } @@ -793,16 +753,19 @@ screen_write_clearendofline(struct screen_write_ctx *ctx, u_int bg) struct tty_ctx ttyctx; u_int sx = screen_size_x(s); - screen_write_initctx(ctx, &ttyctx); - ttyctx.bg = bg; - gl = &s->grid->linedata[s->grid->hsize + s->cy]; if (s->cx > sx - 1 || (s->cx >= gl->cellsize && bg == 8)) return; - screen_dirty_clear(s, s->cx, s->cy, sx - 1, s->cy); + screen_write_initctx(ctx, &ttyctx); + ttyctx.bg = bg; + grid_view_clear(s->grid, s->cx, s->cy, sx - s->cx, 1, bg); + if (s->cx == 0) + screen_write_collect_clear(ctx, s->cy, 1); + else + screen_write_collect_flush(ctx); tty_write(tty_cmd_clearendofline, &ttyctx); } @@ -817,14 +780,15 @@ screen_write_clearstartofline(struct screen_write_ctx *ctx, u_int bg) screen_write_initctx(ctx, &ttyctx); ttyctx.bg = bg; - if (s->cx > sx - 1) { - screen_dirty_clear(s, 0, s->cy, sx - 1, s->cy); + if (s->cx > sx - 1) grid_view_clear(s->grid, 0, s->cy, sx, 1, bg); - } else { - screen_dirty_clear(s, 0, s->cy, s->cx, s->cy); + else grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1, bg); - } + if (s->cx > sx - 1) + screen_write_collect_clear(ctx, s->cy, 1); + else + screen_write_collect_flush(ctx); tty_write(tty_cmd_clearstartofline, &ttyctx); } @@ -852,12 +816,12 @@ screen_write_reverseindex(struct screen_write_ctx *ctx) screen_write_initctx(ctx, &ttyctx); - if (s->cy == s->rupper) { - screen_write_flush(ctx); + if (s->cy == s->rupper) grid_view_scroll_region_down(s->grid, s->rupper, s->rlower); - } else if (s->cy > 0) + else if (s->cy > 0) s->cy--; + screen_write_collect_flush(ctx); tty_write(tty_cmd_reverseindex, &ttyctx); } @@ -875,6 +839,8 @@ screen_write_scrollregion(struct screen_write_ctx *ctx, u_int rupper, if (rupper >= rlower) /* cannot be one line */ return; + screen_write_collect_flush(ctx); + /* Cursor moves to top-left. */ s->cx = 0; s->cy = 0; @@ -888,27 +854,41 @@ void screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped) { struct screen *s = ctx->s; + struct grid *gd = s->grid; struct grid_line *gl; - struct tty_ctx ttyctx; - u_int sx = screen_size_x(s), sy = screen_size_y(s); - screen_write_initctx(ctx, &ttyctx); - - gl = &s->grid->linedata[s->grid->hsize + s->cy]; + gl = &gd->linedata[gd->hsize + s->cy]; if (wrapped) gl->flags |= GRID_LINE_WRAPPED; else gl->flags &= ~GRID_LINE_WRAPPED; if (s->cy == s->rlower) { - screen_dirty_clear(s, 0, s->rupper, sx - 1, s->rupper); - screen_write_flush(ctx); - grid_view_scroll_region_up(s->grid, s->rupper, s->rlower); - } else if (s->cy < sy - 1) + grid_view_scroll_region_up(gd, s->rupper, s->rlower); + screen_write_collect_scroll(ctx); + ctx->scrolled++; + } else if (s->cy < screen_size_y(s) - 1) s->cy++; +} - ttyctx.num = wrapped; - tty_write(tty_cmd_linefeed, &ttyctx); +/* Scroll up. */ +void +screen_write_scrollup(struct screen_write_ctx *ctx, u_int lines) +{ + struct screen *s = ctx->s; + struct grid *gd = s->grid; + u_int i; + + if (lines == 0) + lines = 1; + else if (lines > s->rlower - s->rupper + 1) + lines = s->rlower - s->rupper + 1; + + for (i = 0; i < lines; i++) { + grid_view_scroll_region_up(gd, s->rupper, s->rlower); + screen_write_collect_scroll(ctx); + } + ctx->scrolled += lines; } /* Carriage return (cursor to start of line). */ @@ -925,6 +905,7 @@ void screen_write_clearendofscreen(struct screen_write_ctx *ctx, u_int bg) { struct screen *s = ctx->s; + struct grid *gd = s->grid; struct tty_ctx ttyctx; u_int sx = screen_size_x(s), sy = screen_size_y(s); @@ -932,20 +913,16 @@ screen_write_clearendofscreen(struct screen_write_ctx *ctx, u_int bg) ttyctx.bg = bg; /* Scroll into history if it is enabled and clearing entire screen. */ - if (s->cx == 0 && s->cy == 0 && s->grid->flags & GRID_HISTORY) { - screen_dirty_clear(s, 0, 0, sx - 1, sy - 1); - grid_view_clear_history(s->grid, bg); - } else { - if (s->cx <= sx - 1) { - screen_dirty_clear(s, s->cx, s->cy, sx - 1, s->cy); - grid_view_clear(s->grid, s->cx, s->cy, sx - s->cx, 1, - bg); - } - screen_dirty_clear(s, 0, s->cy + 1, sx - 1, sy - 1); - grid_view_clear(s->grid, 0, s->cy + 1, sx, sy - (s->cy + 1), - bg); + if (s->cx == 0 && s->cy == 0 && (gd->flags & GRID_HISTORY)) + grid_view_clear_history(gd, bg); + else { + if (s->cx <= sx - 1) + grid_view_clear(gd, s->cx, s->cy, sx - s->cx, 1, bg); + grid_view_clear(gd, 0, s->cy + 1, sx, sy - (s->cy + 1), bg); } + screen_write_collect_clear(ctx, s->cy + 1, sy - (s->cy + 1)); + screen_write_collect_flush(ctx); tty_write(tty_cmd_clearendofscreen, &ttyctx); } @@ -960,18 +937,15 @@ screen_write_clearstartofscreen(struct screen_write_ctx *ctx, u_int bg) screen_write_initctx(ctx, &ttyctx); ttyctx.bg = bg; - if (s->cy > 0) { - screen_dirty_clear(s, 0, 0, sx - 1, s->cy); + if (s->cy > 0) grid_view_clear(s->grid, 0, 0, sx, s->cy, bg); - } - if (s->cx > sx - 1) { - screen_dirty_clear(s, 0, s->cy, sx - 1, s->cy); - grid_view_clear(s->grid, 0, s->cy, sx, 1, bg); - } else { - screen_dirty_clear(s, 0, s->cy, s->cx, s->cy); - grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1, bg); - } + if (s->cx > sx - 1) + grid_view_clear(s->grid, 0, s->cy, sx, 1, 8); + else + grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1, 8); + screen_write_collect_clear(ctx, 0, s->cy); + screen_write_collect_flush(ctx); tty_write(tty_cmd_clearstartofscreen, &ttyctx); } @@ -986,14 +960,13 @@ screen_write_clearscreen(struct screen_write_ctx *ctx, u_int bg) screen_write_initctx(ctx, &ttyctx); ttyctx.bg = bg; - screen_dirty_clear(s, 0, 0, sx - 1, sy - 1); - /* Scroll into history if it is enabled. */ if (s->grid->flags & GRID_HISTORY) grid_view_clear_history(s->grid, bg); else grid_view_clear(s->grid, 0, 0, sx, sy, bg); + screen_write_collect_clear(ctx, 0, sy); tty_write(tty_cmd_clearscreen, &ttyctx); } @@ -1008,39 +981,177 @@ screen_write_clearhistory(struct screen_write_ctx *ctx) gd->hscrolled = gd->hsize = 0; } +/* Clear a collected line. */ +static void +screen_write_collect_clear(struct screen_write_ctx *ctx, u_int y, u_int n) +{ + struct screen_write_collect_item *ci, *tmp; + u_int i; + size_t size; + + for (i = y ; i < y + n; i++) { + if (TAILQ_EMPTY(&ctx->list[i].items)) + continue; + size = 0; + TAILQ_FOREACH_SAFE(ci, &ctx->list[i].items, entry, tmp) { + size += ci->used; + TAILQ_REMOVE(&ctx->list[i].items, ci, entry); + free(ci); + } + ctx->skipped += size; + log_debug("discarding %zu bytes on line %u", size, i); + } +} + +/* Scroll collected lines up. */ +static void +screen_write_collect_scroll(struct screen_write_ctx *ctx) +{ + struct screen *s = ctx->s; + struct screen_write_collect_line *cl; + u_int y; + + screen_write_collect_clear(ctx, s->rupper, 1); + for (y = s->rupper; y < s->rlower; y++) { + cl = &ctx->list[y + 1]; + TAILQ_CONCAT(&ctx->list[y].items, &cl->items, entry); + TAILQ_INIT(&cl->items); + } +} + +/* Flush collected lines. */ +static void +screen_write_collect_flush(struct screen_write_ctx *ctx) +{ + struct screen *s = ctx->s; + struct screen_write_collect_item *ci, *tmp; + u_int y, cx, cy; + struct tty_ctx ttyctx; + + if (ctx->scrolled != 0) { + log_debug("%s: scrolled %u (region %u-%u)", __func__, + ctx->scrolled, s->rupper, s->rlower); + if (ctx->scrolled > s->rlower - s->rupper + 1) + ctx->scrolled = s->rlower - s->rupper + 1; + + screen_write_initctx(ctx, &ttyctx); + ttyctx.num = ctx->scrolled; + tty_write(tty_cmd_scrollup, &ttyctx); + } + ctx->scrolled = 0; + + cx = s->cx; cy = s->cy; + for (y = 0; y < screen_size_y(s); y++) { + TAILQ_FOREACH_SAFE(ci, &ctx->list[y].items, entry, tmp) { + screen_write_cursormove(ctx, ci->x, y); + screen_write_initctx(ctx, &ttyctx); + ttyctx.cell = &ci->gc; + ttyctx.ptr = ci->data; + ttyctx.num = ci->used; + tty_write(tty_cmd_cells, &ttyctx); + ctx->written += ci->used; + + TAILQ_REMOVE(&ctx->list[y].items, ci, entry); + free(ci); + } + } + s->cx = cx; s->cy = cy; +} + +/* Finish and store collected cells. */ +void +screen_write_collect_end(struct screen_write_ctx *ctx) +{ + struct screen *s = ctx->s; + struct screen_write_collect_item *ci = ctx->item; + struct grid_cell gc; + + if (ci->used == 0) + return; + ci->data[ci->used] = '\0'; + + ci->x = s->cx; + TAILQ_INSERT_TAIL(&ctx->list[s->cy].items, ci, entry); + ctx->item = xcalloc(1, sizeof *ctx->item); + + log_debug("%s: %u %s (at %u,%u)", __func__, ci->used, ci->data, s->cx, + s->cy); + + memcpy(&gc, &ci->gc, sizeof gc); + grid_view_set_cells(s->grid, s->cx, s->cy, &gc, ci->data, ci->used); + s->cx += ci->used; +} + +/* Write cell data, collecting if necessary. */ +void +screen_write_collect_add(struct screen_write_ctx *ctx, + const struct grid_cell *gc) +{ + struct screen *s = ctx->s; + struct screen_write_collect_item *ci; + u_int sx = screen_size_x(s); + int collect; + + /* + * Don't need to check that the attributes and whatnot are still the + * same - input_parse will do a flush when anything that isn't a plain + * character is encountered. Also nothing should make it here that + * isn't a single ASCII character. + */ + + collect = 1; + if (gc->data.width != 1) + collect = 0; + else if (gc->attr & GRID_ATTR_CHARSET) + collect = 0; + else if (~s->mode & MODE_WRAP) + collect = 0; + else if (s->mode & MODE_INSERT) + collect = 0; + else if (s->sel.flag) + collect = 0; + if (!collect) { + screen_write_collect_end(ctx); + screen_write_cell(ctx, gc); + return; + } + ctx->cells++; + + if (s->cx > sx - 1 || ctx->item->used > sx - 1 - s->cx) + screen_write_collect_end(ctx); + if (s->cx > sx - 1) { + screen_write_linefeed(ctx, 1); + s->cx = 0; + } + + ci = ctx->item; /* may have changed */ + if (ci->used == 0) + memcpy(&ci->gc, gc, sizeof ci->gc); + ci->data[ci->used++] = gc->data.data[0]; + if (ci->used == (sizeof ci->data) - 1) + screen_write_collect_end(ctx); +} + /* Write cell data. */ void screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc) { struct screen *s = ctx->s; struct grid *gd = s->grid; - struct tty_ctx ttyctx; - u_int width, xx, last; - u_int sx = screen_size_x(s), sy = screen_size_y(s); struct grid_line *gl; - struct grid_cell tmp_gc, now_gc; struct grid_cell_entry *gce; - int insert, skip, selected; + struct grid_cell tmp_gc, now_gc; + struct tty_ctx ttyctx; + u_int sx = screen_size_x(s), sy = screen_size_y(s); + u_int width = gc->data.width, xx, last; + int selected, skip = 1; - ctx->cells++; - - /* Ignore padding. */ + /* Ignore padding cells. */ if (gc->flags & GRID_FLAG_PADDING) return; - width = gc->data.width; + ctx->cells++; - /* - * If this is a wide character and there is no room on the screen for - * the entire character, don't print it. - */ - if (!(s->mode & MODE_WRAP) && (width > 1 && - (width > sx || (s->cx != sx && s->cx > sx - width)))) - return; - - /* - * If the width is zero, combine onto the previous character, if - * there is space. - */ + /* If the width is zero, combine onto the previous character. */ if (width == 0) { if ((gc = screen_write_combine(ctx, &gc->data, &xx)) != 0) { screen_write_cursormove(ctx, xx, s->cy); @@ -1051,29 +1162,27 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc) return; } + /* If this character doesn't fit, ignore it. */ + if ((~s->mode & MODE_WRAP) && + width > 1 && + (width > sx || (s->cx != sx && s->cx > sx - width))) + return; + /* If in insert mode, make space for the cells. */ if (s->mode & MODE_INSERT) { - if (s->cx <= sx - width) { - screen_write_flush(ctx); - xx = sx - s->cx - width; - grid_view_insert_cells(s->grid, s->cx, s->cy, xx, 8); - } - insert = 1; - } else - insert = 0; - skip = !insert; + grid_view_insert_cells(s->grid, s->cx, s->cy, width, 8); + skip = 0; + } /* Check this will fit on the current line and wrap if not. */ if ((s->mode & MODE_WRAP) && s->cx > sx - width) { screen_write_linefeed(ctx, 1); - s->cx = 0; /* carriage return */ + s->cx = 0; } /* Sanity check cursor position. */ if (s->cx > sx - width || s->cy > sy - 1) return; - - /* Initialise the redraw context. */ screen_write_initctx(ctx, &ttyctx); /* Handle overwriting of UTF-8 characters. */ @@ -1111,6 +1220,8 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc) skip = 0; else if (gc->data.width != 1) skip = 0; + else if (gc->data.size != 1) + skip = 0; else if (gce->data.data != gc->data.data[0]) skip = 0; } @@ -1118,18 +1229,18 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc) /* Update the selection the flag and set the cell. */ selected = screen_check_selection(s, s->cx, s->cy); - if (selected && ~gc->flags & GRID_FLAG_SELECTED) { - skip = 0; + if (selected && (~gc->flags & GRID_FLAG_SELECTED)) { memcpy(&tmp_gc, gc, sizeof tmp_gc); tmp_gc.flags |= GRID_FLAG_SELECTED; grid_view_set_cell(gd, s->cx, s->cy, &tmp_gc); - } else if (!selected && gc->flags & GRID_FLAG_SELECTED) { - skip = 0; + } else if (!selected && (gc->flags & GRID_FLAG_SELECTED)) { memcpy(&tmp_gc, gc, sizeof tmp_gc); tmp_gc.flags &= ~GRID_FLAG_SELECTED; grid_view_set_cell(gd, s->cx, s->cy, &tmp_gc); } else if (!skip) grid_view_set_cell(gd, s->cx, s->cy, gc); + if (selected) + skip = 0; /* * Move the cursor. If not wrapping, stick at the last character and @@ -1142,34 +1253,21 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc) s->cx = sx - last; /* Create space for character in insert mode. */ - if (insert) { + if (s->mode & MODE_INSERT) { + screen_write_collect_flush(ctx); ttyctx.num = width; tty_write(tty_cmd_insertcharacter, &ttyctx); } /* Write to the screen. */ - if (selected) { - screen_write_flush(ctx); - screen_select_cell(s, &tmp_gc, gc); - ttyctx.cell = &tmp_gc; + if (!skip) { + if (selected) { + screen_select_cell(s, &tmp_gc, gc); + ttyctx.cell = &tmp_gc; + } else + ttyctx.cell = gc; tty_write(tty_cmd_cell, &ttyctx); ctx->written++; - } else if (!skip) { - /* - * If wp is NULL, we are not updating the terminal and don't - * care about actually writing the cells (tty_write will just - * return). So don't even bother allocating the dirty array. - */ - if (ctx->wp != NULL && s->dirty == NULL) { - log_debug("%s: allocating %u bits", __func__, - s->dirtysize); - s->dirty = bit_alloc(s->dirtysize); - } - if (s->dirty != NULL) { - bit_set(s->dirty, screen_dirty_bit(s, - ttyctx.ocx, ttyctx.ocy)); - ctx->dirty++; - } } else ctx->skipped++; } @@ -1277,6 +1375,7 @@ screen_write_overwrite(struct screen_write_ctx *ctx, struct grid_cell *gc, return (done); } +/* Set external clipboard. */ void screen_write_setselection(struct screen_write_ctx *ctx, u_char *str, u_int len) { @@ -1289,6 +1388,7 @@ screen_write_setselection(struct screen_write_ctx *ctx, u_char *str, u_int len) tty_write(tty_cmd_setselection, &ttyctx); } +/* Write unmodified string. */ void screen_write_rawstring(struct screen_write_ctx *ctx, u_char *str, u_int len) { diff --git a/screen.c b/screen.c index d6fbfecd..087611ae 100644 --- a/screen.c +++ b/screen.c @@ -40,9 +40,6 @@ screen_init(struct screen *s, u_int sx, u_int sy, u_int hlimit) s->ccolour = xstrdup(""); s->tabs = NULL; - s->dirty = NULL; - s->dirtysize = 0; - screen_reinit(s); } @@ -69,7 +66,6 @@ screen_reinit(struct screen *s) void screen_free(struct screen *s) { - free(s->dirty); free(s->tabs); free(s->title); free(s->ccolour); @@ -358,7 +354,7 @@ screen_check_selection(struct screen *s, u_int px, u_int py) xx = sel->sx - 1; else xx = sel->sx; - if (py == sel->sy && px > xx) + if (py == sel->sy && (sel->sx == 0 || px > xx)) return (0); } else { /* starting line == ending line. */ diff --git a/server-client.c b/server-client.c index 856bc04d..ccddee7a 100644 --- a/server-client.c +++ b/server-client.c @@ -961,6 +961,7 @@ server_client_loop(void) } } +/* Resize timer event. */ static void server_client_resize_event(__unused int fd, __unused short events, void *data) { diff --git a/tmux.h b/tmux.h index ea966acb..a7e1fb6a 100644 --- a/tmux.h +++ b/tmux.h @@ -243,6 +243,7 @@ enum tty_code_code { TTYC_ICH1, /* insert_character, ic */ TTYC_IL, /* parm_insert_line, IL */ TTYC_IL1, /* insert_line, il */ + TTYC_INDN, /* parm_index, indn */ TTYC_INVIS, /* enter_secure_mode, mk */ TTYC_IS1, /* init_1string, i1 */ TTYC_IS2, /* init_2string, i2 */ @@ -653,17 +654,19 @@ struct screen { bitstr_t *tabs; - bitstr_t *dirty; - u_int dirtysize; - struct screen_sel sel; }; /* Screen write context. */ +struct screen_write_collect_item; +struct screen_write_collect_line; struct screen_write_ctx { struct window_pane *wp; struct screen *s; - u_int dirty; + + struct screen_write_collect_item *item; + struct screen_write_collect_line *list; + u_int scrolled; u_int cells; u_int written; @@ -1044,7 +1047,7 @@ struct tty { struct grid_cell cell; - int last_wp; + int last_wp; struct grid_cell last_cell; #define TTY_NOCURSOR 0x1 @@ -1645,6 +1648,7 @@ void tty_write(void (*)(struct tty *, const struct tty_ctx *), struct tty_ctx *); void tty_cmd_alignmenttest(struct tty *, const struct tty_ctx *); void tty_cmd_cell(struct tty *, const struct tty_ctx *); +void tty_cmd_cells(struct tty *, const struct tty_ctx *); void tty_cmd_clearendofline(struct tty *, const struct tty_ctx *); void tty_cmd_clearendofscreen(struct tty *, const struct tty_ctx *); void tty_cmd_clearline(struct tty *, const struct tty_ctx *); @@ -1658,6 +1662,7 @@ void tty_cmd_erasecharacter(struct tty *, const struct tty_ctx *); void tty_cmd_insertcharacter(struct tty *, const struct tty_ctx *); void tty_cmd_insertline(struct tty *, const struct tty_ctx *); void tty_cmd_linefeed(struct tty *, const struct tty_ctx *); +void tty_cmd_scrollup(struct tty *, const struct tty_ctx *); void tty_cmd_reverseindex(struct tty *, const struct tty_ctx *); void tty_cmd_setselection(struct tty *, const struct tty_ctx *); void tty_cmd_rawstring(struct tty *, const struct tty_ctx *); @@ -1913,6 +1918,8 @@ void grid_clear_history(struct grid *); const struct grid_line *grid_peek_line(struct grid *, u_int); void grid_get_cell(struct grid *, u_int, u_int, struct grid_cell *); void grid_set_cell(struct grid *, u_int, u_int, const struct grid_cell *); +void grid_set_cells(struct grid *, u_int, u_int, const struct grid_cell *, + const char *, size_t); void grid_clear(struct grid *, u_int, u_int, u_int, u_int, u_int); void grid_clear_lines(struct grid *, u_int, u_int, u_int); void grid_move_lines(struct grid *, u_int, u_int, u_int, u_int); @@ -1927,6 +1934,8 @@ u_int grid_reflow(struct grid *, struct grid *, u_int); void grid_view_get_cell(struct grid *, u_int, u_int, struct grid_cell *); void grid_view_set_cell(struct grid *, u_int, u_int, const struct grid_cell *); +void grid_view_set_cells(struct grid *, u_int, u_int, + const struct grid_cell *, const char *, size_t); void grid_view_clear_history(struct grid *, u_int); void grid_view_clear(struct grid *, u_int, u_int, u_int, u_int, u_int); void grid_view_scroll_region_up(struct grid *, u_int, u_int); @@ -1980,11 +1989,15 @@ void screen_write_cursormove(struct screen_write_ctx *, u_int, u_int); void screen_write_reverseindex(struct screen_write_ctx *); void screen_write_scrollregion(struct screen_write_ctx *, u_int, u_int); void screen_write_linefeed(struct screen_write_ctx *, int); +void screen_write_scrollup(struct screen_write_ctx *, u_int); void screen_write_carriagereturn(struct screen_write_ctx *); void screen_write_clearendofscreen(struct screen_write_ctx *, u_int); void screen_write_clearstartofscreen(struct screen_write_ctx *, u_int); void screen_write_clearscreen(struct screen_write_ctx *, u_int); void screen_write_clearhistory(struct screen_write_ctx *); +void screen_write_collect_end(struct screen_write_ctx *); +void screen_write_collect_add(struct screen_write_ctx *, + const struct grid_cell *); void screen_write_cell(struct screen_write_ctx *, const struct grid_cell *); void screen_write_setselection(struct screen_write_ctx *, u_char *, u_int); void screen_write_rawstring(struct screen_write_ctx *, u_char *, u_int); diff --git a/tty-term.c b/tty-term.c index 10475d04..3741f1f6 100644 --- a/tty-term.c +++ b/tty-term.c @@ -98,6 +98,7 @@ static const struct tty_term_code_entry tty_term_codes[] = { [TTYC_ICH1] = { TTYCODE_STRING, "ich1" }, [TTYC_IL] = { TTYCODE_STRING, "il" }, [TTYC_IL1] = { TTYCODE_STRING, "il1" }, + [TTYC_INDN] = { TTYCODE_STRING, "indn" }, [TTYC_INVIS] = { TTYCODE_STRING, "invis" }, [TTYC_IS1] = { TTYCODE_STRING, "is1" }, [TTYC_IS2] = { TTYCODE_STRING, "is2" }, diff --git a/tty.c b/tty.c index 1b1ae480..40bd7a40 100644 --- a/tty.c +++ b/tty.c @@ -954,14 +954,15 @@ tty_cmd_clearstartofline(struct tty *tty, const struct tty_ctx *ctx) tty_default_attributes(tty, wp, ctx->bg); - tty_cursor_pane(tty, ctx, 0, ctx->ocy); - if (ctx->xoff == 0 && tty_term_has(tty->term, TTYC_EL1) && - !tty_fake_bce(tty, ctx->wp, ctx->bg)) + !tty_fake_bce(tty, ctx->wp, ctx->bg)) { + tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); tty_putcode(tty, TTYC_EL1); - else + } else { + tty_cursor_pane(tty, ctx, 0, ctx->ocy); tty_repeat_space(tty, ctx->ocx + 1); + } } void @@ -1020,6 +1021,32 @@ tty_cmd_linefeed(struct tty *tty, const struct tty_ctx *ctx) tty_putc(tty, '\n'); } +void +tty_cmd_scrollup(struct tty *tty, const struct tty_ctx *ctx) +{ + struct window_pane *wp = ctx->wp; + u_int i; + + if ((!tty_pane_full_width(tty, ctx) && !tty_use_margin(tty)) || + tty_fake_bce(tty, wp, ctx->bg) || + !tty_term_has(tty->term, TTYC_CSR)) { + tty_redraw_region(tty, ctx); + return; + } + + tty_attributes(tty, &grid_default_cell, wp); + + tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); + tty_margin_pane(tty, ctx); + + if (ctx->num == 1 || !tty_term_has(tty->term, TTYC_INDN)) { + tty_cursor(tty, tty->rright, ctx->yoff + tty->rlower); + for (i = 0; i < ctx->num; i++) + tty_putc(tty, '\n'); + } else + tty_putcode1(tty, TTYC_INDN, ctx->num); +} + void tty_cmd_clearendofscreen(struct tty *tty, const struct tty_ctx *ctx) { @@ -1156,8 +1183,6 @@ tty_cmd_alignmenttest(struct tty *tty, const struct tty_ctx *ctx) void tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx) { - struct window_pane *wp = ctx->wp; - if (ctx->xoff + ctx->ocx > tty->sx - 1 && ctx->ocy == ctx->orlower) { if (tty_pane_full_width(tty, ctx)) tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); @@ -1167,7 +1192,16 @@ tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx) tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); - tty_cell(tty, ctx->cell, wp); + tty_cell(tty, ctx->cell, ctx->wp); +} + +void +tty_cmd_cells(struct tty *tty, const struct tty_ctx *ctx) +{ + tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); + + tty_attributes(tty, ctx->cell, ctx->wp); + tty_putn(tty, ctx->ptr, ctx->num, ctx->num); } void diff --git a/window-copy.c b/window-copy.c index f808e7af..cb6243db 100644 --- a/window-copy.c +++ b/window-copy.c @@ -29,7 +29,7 @@ static void window_copy_command(struct window_pane *, struct client *, struct session *, struct args *, struct mouse_event *); static struct screen *window_copy_init(struct window_pane *); static void window_copy_free(struct window_pane *); -static void window_copy_pagedown(struct window_pane *, int); +static int window_copy_pagedown(struct window_pane *, int); static void window_copy_next_paragraph(struct window_pane *); static void window_copy_previous_paragraph(struct window_pane *); static void window_copy_resize(struct window_pane *, u_int, u_int); @@ -380,7 +380,7 @@ window_copy_pageup(struct window_pane *wp, int half_page) window_copy_redraw_screen(wp); } -static void +static int window_copy_pagedown(struct window_pane *wp, int half_page) { struct window_copy_mode_data *data = wp->modedata; @@ -420,13 +420,11 @@ window_copy_pagedown(struct window_pane *wp, int half_page) window_copy_cursor_end_of_line(wp); } - if (data->scroll_exit && data->oy == 0) { - window_pane_reset_mode(wp); - return; - } - + if (data->scroll_exit && data->oy == 0) + return (1); window_copy_update_selection(wp, 1); window_copy_redraw_screen(wp); + return (0); } static void @@ -621,8 +619,12 @@ window_copy_command(struct window_pane *wp, struct client *c, struct session *s, if (strcmp(command, "end-of-line") == 0) window_copy_cursor_end_of_line(wp); if (strcmp(command, "halfpage-down") == 0) { - for (; np != 0; np--) - window_copy_pagedown(wp, 1); + for (; np != 0; np--) { + if (window_copy_pagedown(wp, 1)) { + cancel = 1; + break; + } + } } if (strcmp(command, "halfpage-up") == 0) { for (; np != 0; np--) @@ -715,8 +717,12 @@ window_copy_command(struct window_pane *wp, struct client *c, struct session *s, window_copy_other_end(wp); } if (strcmp(command, "page-down") == 0) { - for (; np != 0; np--) - window_copy_pagedown(wp, 0); + for (; np != 0; np--) { + if (window_copy_pagedown(wp, 0)) { + cancel = 1; + break; + } + } } if (strcmp(command, "page-up") == 0) { for (; np != 0; np--)