From ac1f294bb930a0c05f96197b6a53b883ebc483f2 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 8 Feb 2017 15:41:41 +0000 Subject: [PATCH 1/6] Add a helper to store a cell, and some tidying. --- grid.c | 38 +++++++++++++++++++++++--------------- screen-redraw.c | 3 +++ 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/grid.c b/grid.c index 7fcddf61..741e9bf3 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,10 @@ 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; - } - - 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]; + else + grid_store_cell(gce, gc, gc->data.data[0]); } /* Clear area. */ 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); From 7475165cd87d443da34da11298e638d3d17f43e9 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 8 Feb 2017 15:49:29 +0000 Subject: [PATCH 2/6] Some other tidying bits. --- input.c | 1 - screen-write.c | 92 ++++++++++++++++++++++++------------------------- server-client.c | 1 + tty.c | 4 +-- 4 files changed, 48 insertions(+), 50 deletions(-) diff --git a/input.c b/input.c index 26eb1686..04dc8ce1 100644 --- a/input.c +++ b/input.c @@ -920,7 +920,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; diff --git a/screen-write.c b/screen-write.c index eff0195b..8072981f 100644 --- a/screen-write.c +++ b/screen-write.c @@ -51,9 +51,8 @@ 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; + u_int size; + char tmp[16]; ctx->wp = wp; if (wp != NULL && s == NULL) @@ -71,12 +70,10 @@ screen_write_start(struct screen_write_ctx *ctx, struct window_pane *wp, 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. */ @@ -604,14 +601,16 @@ screen_write_insertcharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg) if (nx == 0) return; + if (s->cx > screen_size_x(s) - 1) + return; + screen_write_flush(ctx); screen_write_initctx(ctx, &ttyctx); + ttyctx.bg = bg; - if (s->cx <= screen_size_x(s) - 1) - grid_view_insert_cells(s->grid, s->cx, s->cy, nx, bg); + grid_view_insert_cells(s->grid, s->cx, s->cy, nx, bg); ttyctx.num = nx; - ttyctx.bg = bg; tty_write(tty_cmd_insertcharacter, &ttyctx); } @@ -630,14 +629,16 @@ screen_write_deletecharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg) if (nx == 0) return; + if (s->cx > screen_size_x(s) - 1) + return; + screen_write_flush(ctx); screen_write_initctx(ctx, &ttyctx); + ttyctx.bg = bg; - if (s->cx <= screen_size_x(s) - 1) - grid_view_delete_cells(s->grid, s->cx, s->cy, nx, bg); + grid_view_delete_cells(s->grid, s->cx, s->cy, nx, bg); ttyctx.num = nx; - ttyctx.bg = bg; tty_write(tty_cmd_deletecharacter, &ttyctx); } @@ -656,13 +657,13 @@ screen_write_clearcharacter(struct screen_write_ctx *ctx, u_int nx) if (nx == 0) return; + if (s->cx > screen_size_x(s) - 1) + 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 - return; + 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); ttyctx.num = nx; tty_write(tty_cmd_clearcharacter, &ttyctx); @@ -673,6 +674,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) @@ -686,11 +688,11 @@ screen_write_insertline(struct screen_write_ctx *ctx, u_int ny, u_int bg) screen_write_flush(ctx); screen_write_initctx(ctx, &ttyctx); + ttyctx.bg = bg; - grid_view_insert_lines(s->grid, s->cy, ny, bg); + grid_view_insert_lines(gd, s->cy, ny, bg); ttyctx.num = ny; - ttyctx.bg = bg; tty_write(tty_cmd_insertline, &ttyctx); return; } @@ -702,16 +704,14 @@ screen_write_insertline(struct screen_write_ctx *ctx, u_int ny, u_int bg) 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); ttyctx.num = ny; - ttyctx.bg = bg; tty_write(tty_cmd_insertline, &ttyctx); } @@ -720,6 +720,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) @@ -733,11 +734,11 @@ screen_write_deleteline(struct screen_write_ctx *ctx, u_int ny, u_int bg) screen_write_flush(ctx); screen_write_initctx(ctx, &ttyctx); + ttyctx.bg = bg; - grid_view_delete_lines(s->grid, s->cy, ny, bg); + grid_view_delete_lines(gd, s->cy, ny, bg); ttyctx.num = ny; - ttyctx.bg = bg; tty_write(tty_cmd_deleteline, &ttyctx); return; } @@ -749,16 +750,14 @@ screen_write_deleteline(struct screen_write_ctx *ctx, u_int ny, u_int bg) 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); ttyctx.num = ny; - ttyctx.bg = bg; tty_write(tty_cmd_deleteline, &ttyctx); } @@ -771,13 +770,13 @@ 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_write_initctx(ctx, &ttyctx); + ttyctx.bg = bg; + screen_dirty_clear(s, 0, s->cy, sx - 1, s->cy); grid_view_clear(s->grid, 0, s->cy, sx, 1, bg); @@ -793,13 +792,13 @@ 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_write_initctx(ctx, &ttyctx); + ttyctx.bg = bg; + 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); @@ -925,6 +924,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,18 +932,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) { + if (s->cx == 0 && s->cy == 0 && gd->flags & GRID_HISTORY) { screen_dirty_clear(s, 0, 0, sx - 1, sy - 1); - grid_view_clear_history(s->grid, bg); + grid_view_clear_history(gd, 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); + grid_view_clear(gd, 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); + grid_view_clear(gd, 0, s->cy + 1, sx, sy - (s->cy + 1), bg); } tty_write(tty_cmd_clearendofscreen, &ttyctx); @@ -1277,6 +1275,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 +1288,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/server-client.c b/server-client.c index b304234d..a469cdbc 100644 --- a/server-client.c +++ b/server-client.c @@ -963,6 +963,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/tty.c b/tty.c index 1b1ae480..02143f97 100644 --- a/tty.c +++ b/tty.c @@ -1156,8 +1156,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 +1165,7 @@ 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 From d4b006b9faf001de41560db2e8fb890c654598e2 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 8 Feb 2017 16:18:20 +0000 Subject: [PATCH 3/6] Fix clear start of line. --- tty.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tty.c b/tty.c index 02143f97..1b7fb55f 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 From 13a0b6bb3fe05454cace81f5ec7624f6fd9021a5 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 8 Feb 2017 16:45:18 +0000 Subject: [PATCH 4/6] Collect sequences of printable ASCII characters and process them together instead of handling them one by one. This is significantly faster. Sequences are terminated when we reach the end of the line, fill the internal buffer, or a different character is seen by the input parser (an escape sequence, or UTF-8). Rather than writing collected sequences out immediately, hold them until it is necessary (another screen modification, or we consume all available data). This means we can discard changes that would have no effect (for example, lines that would just be scrolled off the screen or cleared). This reduces the total amount of data we write out to the terminal - not important for fast terminals, but a big help with slow (like xterm). --- grid-view.c | 9 ++ grid.c | 30 ++++ input.c | 12 +- screen-write.c | 421 ++++++++++++++++++++++++++++--------------------- screen.c | 6 +- tmux.h | 19 ++- tty.c | 9 ++ 7 files changed, 319 insertions(+), 187 deletions(-) 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 741e9bf3..388a2c1b 100644 --- a/grid.c +++ b/grid.c @@ -407,6 +407,36 @@ grid_set_cell(struct grid *gd, u_int px, u_int py, const struct grid_cell *gc) grid_store_cell(gce, gc, 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. */ void grid_clear(struct grid *gd, u_int px, u_int py, u_int nx, u_int ny, u_int bg) diff --git a/input.c b/input.c index 04dc8ce1..d5b8737d 100644 --- a/input.c +++ b/input.c @@ -895,6 +895,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. @@ -1020,7 +1030,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; diff --git a/screen-write.c b/screen-write.c index 8072981f..761ec99f 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,23 +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]; + u_int y; + + memset(ctx, 0, sizeof *ctx); ctx->wp = wp; if (wp != NULL && s == NULL) @@ -60,15 +69,10 @@ 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->cells = ctx->written = ctx->skipped = 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); if (wp != NULL) snprintf(tmp, sizeof tmp, "pane %%%u", wp->id); @@ -80,55 +84,14 @@ screen_write_start(struct screen_write_ctx *ctx, struct window_pane *wp, 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. */ @@ -563,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'); @@ -583,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,12 +565,12 @@ screen_write_insertcharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg) if (s->cx > screen_size_x(s) - 1) return; - screen_write_flush(ctx); screen_write_initctx(ctx, &ttyctx); 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); } @@ -632,12 +593,12 @@ screen_write_deletecharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg) if (s->cx > screen_size_x(s) - 1) return; - screen_write_flush(ctx); screen_write_initctx(ctx, &ttyctx); 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); } @@ -662,9 +623,9 @@ screen_write_clearcharacter(struct screen_write_ctx *ctx, u_int nx) screen_write_initctx(ctx, &ttyctx); - 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); + screen_write_collect_flush(ctx); ttyctx.num = nx; tty_write(tty_cmd_clearcharacter, &ttyctx); } @@ -686,12 +647,12 @@ 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; grid_view_insert_lines(gd, s->cy, ny, bg); + screen_write_collect_flush(ctx); ttyctx.num = ny; tty_write(tty_cmd_insertline, &ttyctx); return; @@ -702,7 +663,6 @@ 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; @@ -711,6 +671,7 @@ screen_write_insertline(struct screen_write_ctx *ctx, u_int ny, u_int bg) else grid_view_insert_lines_region(gd, s->rlower, s->cy, ny, bg); + screen_write_collect_flush(ctx); ttyctx.num = ny; tty_write(tty_cmd_insertline, &ttyctx); } @@ -732,12 +693,12 @@ 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; grid_view_delete_lines(gd, s->cy, ny, bg); + screen_write_collect_flush(ctx); ttyctx.num = ny; tty_write(tty_cmd_deleteline, &ttyctx); return; @@ -748,7 +709,6 @@ 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; @@ -757,6 +717,7 @@ screen_write_deleteline(struct screen_write_ctx *ctx, u_int ny, u_int bg) else grid_view_delete_lines_region(gd, s->rlower, s->cy, ny, bg); + screen_write_collect_flush(ctx); ttyctx.num = ny; tty_write(tty_cmd_deleteline, &ttyctx); } @@ -777,9 +738,9 @@ screen_write_clearline(struct screen_write_ctx *ctx, u_int bg) screen_write_initctx(ctx, &ttyctx); ttyctx.bg = bg; - screen_dirty_clear(s, 0, s->cy, sx - 1, s->cy); 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); } @@ -799,9 +760,12 @@ screen_write_clearendofline(struct screen_write_ctx *ctx, u_int bg) screen_write_initctx(ctx, &ttyctx); ttyctx.bg = bg; - 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); + if (s->cx == 0) + screen_write_collect_clear(ctx, s->cy, 1); + else + screen_write_collect_flush(ctx); tty_write(tty_cmd_clearendofline, &ttyctx); } @@ -816,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); } @@ -851,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); } @@ -887,27 +852,24 @@ 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); + struct tty_ctx ttyctx; - 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) - s->cy++; + grid_view_scroll_region_up(gd, s->rupper, s->rlower); - ttyctx.num = wrapped; - tty_write(tty_cmd_linefeed, &ttyctx); + screen_write_collect_scroll(ctx); + screen_write_initctx(ctx, &ttyctx); + tty_write(tty_cmd_linefeed, &ttyctx); + } else if (s->cy < screen_size_y(s) - 1) + s->cy++; } /* Carriage return (cursor to start of line). */ @@ -932,18 +894,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 && gd->flags & GRID_HISTORY) { - screen_dirty_clear(s, 0, 0, sx - 1, sy - 1); + if (s->cx == 0 && s->cy == 0 && (gd->flags & GRID_HISTORY)) grid_view_clear_history(gd, bg); - } else { - if (s->cx <= sx - 1) { - screen_dirty_clear(s, s->cx, s->cy, sx - 1, s->cy); + else { + if (s->cx <= sx - 1) grid_view_clear(gd, s->cx, s->cy, sx - s->cx, 1, bg); - } - screen_dirty_clear(s, 0, s->cy + 1, sx - 1, sy - 1); 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); } @@ -958,18 +918,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); } @@ -984,14 +941,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); } @@ -1006,39 +962,165 @@ 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; + + 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); @@ -1049,29 +1131,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. */ @@ -1109,6 +1189,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; } @@ -1116,18 +1198,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 @@ -1140,34 +1222,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++; } 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/tmux.h b/tmux.h index f9f60e6a..06142164 100644 --- a/tmux.h +++ b/tmux.h @@ -651,17 +651,18 @@ 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 cells; u_int written; @@ -1042,7 +1043,7 @@ struct tty { struct grid_cell cell; - int last_wp; + int last_wp; struct grid_cell last_cell; #define TTY_NOCURSOR 0x1 @@ -1643,6 +1644,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 *); @@ -1911,6 +1913,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); @@ -1925,6 +1929,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); @@ -1983,6 +1989,9 @@ 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.c b/tty.c index 1b7fb55f..508e4bac 100644 --- a/tty.c +++ b/tty.c @@ -1169,6 +1169,15 @@ tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx) 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 tty_cmd_setselection(struct tty *tty, const struct tty_ctx *ctx) { From e100d465daefd20da0bb5eea87c4b4896badff16 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 8 Feb 2017 17:31:09 +0000 Subject: [PATCH 5/6] Add support for scroll up escape sequence (CSI S) and use it when possible instead of sending individual line feeds. --- input.c | 5 +++++ screen-write.c | 39 +++++++++++++++++++++++++++++++++++---- tmux.h | 4 ++++ tty-term.c | 1 + tty.c | 26 ++++++++++++++++++++++++++ 5 files changed, 71 insertions(+), 4 deletions(-) diff --git a/input.c b/input.c index d5b8737d..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 }, @@ -1413,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-write.c b/screen-write.c index 761ec99f..bd615a09 100644 --- a/screen-write.c +++ b/screen-write.c @@ -839,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; @@ -854,7 +856,6 @@ 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; gl = &gd->linedata[gd->hsize + s->cy]; if (wrapped) @@ -864,14 +865,32 @@ screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped) if (s->cy == s->rlower) { grid_view_scroll_region_up(gd, s->rupper, s->rlower); - screen_write_collect_scroll(ctx); - screen_write_initctx(ctx, &ttyctx); - tty_write(tty_cmd_linefeed, &ttyctx); + ctx->scrolled++; } else if (s->cy < screen_size_y(s) - 1) s->cy++; } +/* 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). */ void screen_write_carriagereturn(struct screen_write_ctx *ctx) @@ -1009,6 +1028,18 @@ screen_write_collect_flush(struct screen_write_ctx *ctx) 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) { diff --git a/tmux.h b/tmux.h index 06142164..fe5c4ca6 100644 --- a/tmux.h +++ b/tmux.h @@ -241,6 +241,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 */ @@ -663,6 +664,7 @@ struct screen_write_ctx { struct screen_write_collect_item *item; struct screen_write_collect_line *list; + u_int scrolled; u_int cells; u_int written; @@ -1658,6 +1660,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 *); @@ -1984,6 +1987,7 @@ 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); diff --git a/tty-term.c b/tty-term.c index 092f80f2..7707a576 100644 --- a/tty-term.c +++ b/tty-term.c @@ -95,6 +95,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 508e4bac..40bd7a40 100644 --- a/tty.c +++ b/tty.c @@ -1021,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) { From 05802a6fe309e3b4559286ca5ce3c51f7367d661 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 8 Feb 2017 17:33:51 +0000 Subject: [PATCH 6/6] window_copy_pagedown shouldn't reset the mode anymore, instead let the caller do it so it can free the marks. Problem reported by attila at stalphonsos dot com. --- window-copy.c | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) 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--)