1
0
mirror of https://github.com/tmux/tmux.git synced 2025-03-29 10:18:49 +00:00

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).
This commit is contained in:
nicm 2017-02-08 16:45:18 +00:00
parent d4b006b9fa
commit 13a0b6bb3f
7 changed files with 319 additions and 187 deletions

View File

@ -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); 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. */ /* Clear into history. */
void void
grid_view_clear_history(struct grid *gd, u_int bg) grid_view_clear_history(struct grid *gd, u_int bg)

30
grid.c
View File

@ -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]); 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. */ /* Clear area. */
void void
grid_clear(struct grid *gd, u_int px, u_int py, u_int nx, u_int ny, u_int bg) grid_clear(struct grid *gd, u_int px, u_int py, u_int nx, u_int ny, u_int bg)

12
input.c
View File

@ -895,6 +895,16 @@ input_parse(struct window_pane *wp)
fatalx("no transition from state"); 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 * Execute the handler, if any. Don't switch state if it
* returns non-zero. * returns non-zero.
@ -1020,7 +1030,7 @@ input_print(struct input_ctx *ictx)
ictx->cell.cell.attr &= ~GRID_ATTR_CHARSET; ictx->cell.cell.attr &= ~GRID_ATTR_CHARSET;
utf8_set(&ictx->cell.cell.data, ictx->ch); 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; ictx->cell.cell.attr &= ~GRID_ATTR_CHARSET;

View File

@ -25,7 +25,10 @@
static void screen_write_initctx(struct screen_write_ctx *, static void screen_write_initctx(struct screen_write_ctx *,
struct tty_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 *, static int screen_write_overwrite(struct screen_write_ctx *,
struct grid_cell *, u_int); 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 } GRID_FLAG_PADDING, 0, 8, 8, { { 0 }, 0, 0, 0 }
}; };
#define screen_dirty_bit(s, x, y) (((y) * screen_size_x(s)) + (x)) struct screen_write_collect_item {
#define screen_dirty_clear(s, sx, sy, ex, ey) \ u_int x;
do { \
if (s->dirty != NULL) { \ u_int used;
bit_nclear(s->dirty, \ char data[256];
screen_dirty_bit(s, sx, sy), \
screen_dirty_bit(s, ex, ey)); \ struct grid_cell gc;
} \
} while (0) TAILQ_ENTRY (screen_write_collect_item) entry;
};
struct screen_write_collect_line {
TAILQ_HEAD(, screen_write_collect_item) items;
};
/* Initialize writing with a window. */ /* Initialize writing with a window. */
void void
screen_write_start(struct screen_write_ctx *ctx, struct window_pane *wp, screen_write_start(struct screen_write_ctx *ctx, struct window_pane *wp,
struct screen *s) struct screen *s)
{ {
u_int size;
char tmp[16]; char tmp[16];
u_int y;
memset(ctx, 0, sizeof *ctx);
ctx->wp = wp; ctx->wp = wp;
if (wp != NULL && s == NULL) if (wp != NULL && s == NULL)
@ -60,15 +69,10 @@ screen_write_start(struct screen_write_ctx *ctx, struct window_pane *wp,
else else
ctx->s = s; ctx->s = s;
size = screen_size_x(ctx->s) * screen_size_y(ctx->s); ctx->list = xcalloc(screen_size_y(ctx->s), sizeof *ctx->list);
if (ctx->s->dirtysize != size) { for (y = 0; y < screen_size_y(ctx->s); y++)
free(ctx->s->dirty); TAILQ_INIT(&ctx->list[y].items);
ctx->s->dirty = NULL; ctx->item = xcalloc(1, sizeof *ctx->item);
ctx->s->dirtysize = size;
}
ctx->dirty = 0;
ctx->cells = ctx->written = ctx->skipped = 0;
if (wp != NULL) if (wp != NULL)
snprintf(tmp, sizeof tmp, "pane %%%u", wp->id); 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 void
screen_write_stop(struct screen_write_ctx *ctx) 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__, log_debug("%s: %u cells (%u written, %u skipped)", __func__,
ctx->written, ctx->cells, ctx->cells - ctx->written, ctx->skipped); ctx->cells, ctx->written, ctx->skipped);
}
/* Flush outstanding cell writes. */ free(ctx->item);
static void free(ctx->list); /* flush will have emptied */
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;
} }
/* Reset screen state. */ /* Reset screen state. */
@ -563,12 +526,9 @@ screen_write_alignmenttest(struct screen_write_ctx *ctx)
struct tty_ctx ttyctx; struct tty_ctx ttyctx;
struct grid_cell gc; struct grid_cell gc;
u_int xx, yy; u_int xx, yy;
u_int sx = screen_size_x(s), sy = screen_size_y(s);
screen_write_initctx(ctx, &ttyctx); screen_write_initctx(ctx, &ttyctx);
screen_dirty_clear(s, 0, 0, sx - 1, sy - 1);
memcpy(&gc, &grid_default_cell, sizeof gc); memcpy(&gc, &grid_default_cell, sizeof gc);
utf8_set(&gc.data, 'E'); utf8_set(&gc.data, 'E');
@ -583,6 +543,7 @@ screen_write_alignmenttest(struct screen_write_ctx *ctx)
s->rupper = 0; s->rupper = 0;
s->rlower = screen_size_y(s) - 1; s->rlower = screen_size_y(s) - 1;
screen_write_collect_clear(ctx, 0, screen_size_y(s) - 1);
tty_write(tty_cmd_alignmenttest, &ttyctx); 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) if (s->cx > screen_size_x(s) - 1)
return; return;
screen_write_flush(ctx);
screen_write_initctx(ctx, &ttyctx); screen_write_initctx(ctx, &ttyctx);
ttyctx.bg = bg; ttyctx.bg = bg;
grid_view_insert_cells(s->grid, s->cx, s->cy, nx, bg); grid_view_insert_cells(s->grid, s->cx, s->cy, nx, bg);
screen_write_collect_flush(ctx);
ttyctx.num = nx; ttyctx.num = nx;
tty_write(tty_cmd_insertcharacter, &ttyctx); 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) if (s->cx > screen_size_x(s) - 1)
return; return;
screen_write_flush(ctx);
screen_write_initctx(ctx, &ttyctx); screen_write_initctx(ctx, &ttyctx);
ttyctx.bg = bg; ttyctx.bg = bg;
grid_view_delete_cells(s->grid, s->cx, s->cy, nx, bg); grid_view_delete_cells(s->grid, s->cx, s->cy, nx, bg);
screen_write_collect_flush(ctx);
ttyctx.num = nx; ttyctx.num = nx;
tty_write(tty_cmd_deletecharacter, &ttyctx); 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_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); grid_view_clear(s->grid, s->cx, s->cy, nx, 1, 8);
screen_write_collect_flush(ctx);
ttyctx.num = nx; ttyctx.num = nx;
tty_write(tty_cmd_clearcharacter, &ttyctx); 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) if (ny == 0)
return; return;
screen_write_flush(ctx);
screen_write_initctx(ctx, &ttyctx); screen_write_initctx(ctx, &ttyctx);
ttyctx.bg = bg; ttyctx.bg = bg;
grid_view_insert_lines(gd, s->cy, ny, bg); grid_view_insert_lines(gd, s->cy, ny, bg);
screen_write_collect_flush(ctx);
ttyctx.num = ny; ttyctx.num = ny;
tty_write(tty_cmd_insertline, &ttyctx); tty_write(tty_cmd_insertline, &ttyctx);
return; return;
@ -702,7 +663,6 @@ screen_write_insertline(struct screen_write_ctx *ctx, u_int ny, u_int bg)
if (ny == 0) if (ny == 0)
return; return;
screen_write_flush(ctx);
screen_write_initctx(ctx, &ttyctx); screen_write_initctx(ctx, &ttyctx);
ttyctx.bg = bg; ttyctx.bg = bg;
@ -711,6 +671,7 @@ screen_write_insertline(struct screen_write_ctx *ctx, u_int ny, u_int bg)
else else
grid_view_insert_lines_region(gd, s->rlower, s->cy, ny, bg); grid_view_insert_lines_region(gd, s->rlower, s->cy, ny, bg);
screen_write_collect_flush(ctx);
ttyctx.num = ny; ttyctx.num = ny;
tty_write(tty_cmd_insertline, &ttyctx); 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) if (ny == 0)
return; return;
screen_write_flush(ctx);
screen_write_initctx(ctx, &ttyctx); screen_write_initctx(ctx, &ttyctx);
ttyctx.bg = bg; ttyctx.bg = bg;
grid_view_delete_lines(gd, s->cy, ny, bg); grid_view_delete_lines(gd, s->cy, ny, bg);
screen_write_collect_flush(ctx);
ttyctx.num = ny; ttyctx.num = ny;
tty_write(tty_cmd_deleteline, &ttyctx); tty_write(tty_cmd_deleteline, &ttyctx);
return; return;
@ -748,7 +709,6 @@ screen_write_deleteline(struct screen_write_ctx *ctx, u_int ny, u_int bg)
if (ny == 0) if (ny == 0)
return; return;
screen_write_flush(ctx);
screen_write_initctx(ctx, &ttyctx); screen_write_initctx(ctx, &ttyctx);
ttyctx.bg = bg; ttyctx.bg = bg;
@ -757,6 +717,7 @@ screen_write_deleteline(struct screen_write_ctx *ctx, u_int ny, u_int bg)
else else
grid_view_delete_lines_region(gd, s->rlower, s->cy, ny, bg); grid_view_delete_lines_region(gd, s->rlower, s->cy, ny, bg);
screen_write_collect_flush(ctx);
ttyctx.num = ny; ttyctx.num = ny;
tty_write(tty_cmd_deleteline, &ttyctx); 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); screen_write_initctx(ctx, &ttyctx);
ttyctx.bg = bg; 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); 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); 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); screen_write_initctx(ctx, &ttyctx);
ttyctx.bg = bg; 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); 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); 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); screen_write_initctx(ctx, &ttyctx);
ttyctx.bg = bg; ttyctx.bg = bg;
if (s->cx > sx - 1) { 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); grid_view_clear(s->grid, 0, s->cy, sx, 1, bg);
} else { 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); 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); tty_write(tty_cmd_clearstartofline, &ttyctx);
} }
@ -851,12 +816,12 @@ screen_write_reverseindex(struct screen_write_ctx *ctx)
screen_write_initctx(ctx, &ttyctx); screen_write_initctx(ctx, &ttyctx);
if (s->cy == s->rupper) { if (s->cy == s->rupper)
screen_write_flush(ctx);
grid_view_scroll_region_down(s->grid, s->rupper, s->rlower); grid_view_scroll_region_down(s->grid, s->rupper, s->rlower);
} else if (s->cy > 0) else if (s->cy > 0)
s->cy--; s->cy--;
screen_write_collect_flush(ctx);
tty_write(tty_cmd_reverseindex, &ttyctx); tty_write(tty_cmd_reverseindex, &ttyctx);
} }
@ -887,27 +852,24 @@ void
screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped) screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped)
{ {
struct screen *s = ctx->s; struct screen *s = ctx->s;
struct grid *gd = s->grid;
struct grid_line *gl; struct grid_line *gl;
struct tty_ctx ttyctx; struct tty_ctx ttyctx;
u_int sx = screen_size_x(s), sy = screen_size_y(s);
screen_write_initctx(ctx, &ttyctx); gl = &gd->linedata[gd->hsize + s->cy];
gl = &s->grid->linedata[s->grid->hsize + s->cy];
if (wrapped) if (wrapped)
gl->flags |= GRID_LINE_WRAPPED; gl->flags |= GRID_LINE_WRAPPED;
else else
gl->flags &= ~GRID_LINE_WRAPPED; gl->flags &= ~GRID_LINE_WRAPPED;
if (s->cy == s->rlower) { if (s->cy == s->rlower) {
screen_dirty_clear(s, 0, s->rupper, sx - 1, s->rupper); grid_view_scroll_region_up(gd, s->rupper, s->rlower);
screen_write_flush(ctx);
grid_view_scroll_region_up(s->grid, s->rupper, s->rlower);
} else if (s->cy < sy - 1)
s->cy++;
ttyctx.num = wrapped; screen_write_collect_scroll(ctx);
tty_write(tty_cmd_linefeed, &ttyctx); 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). */ /* 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; ttyctx.bg = bg;
/* Scroll into history if it is enabled and clearing entire screen. */ /* Scroll into history if it is enabled and clearing entire screen. */
if (s->cx == 0 && s->cy == 0 && gd->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(gd, bg); grid_view_clear_history(gd, bg);
} else { else {
if (s->cx <= sx - 1) { if (s->cx <= sx - 1)
screen_dirty_clear(s, s->cx, s->cy, sx - 1, s->cy);
grid_view_clear(gd, 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(gd, 0, s->cy + 1, sx, sy - (s->cy + 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); 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); screen_write_initctx(ctx, &ttyctx);
ttyctx.bg = bg; ttyctx.bg = bg;
if (s->cy > 0) { if (s->cy > 0)
screen_dirty_clear(s, 0, 0, sx - 1, s->cy);
grid_view_clear(s->grid, 0, 0, sx, s->cy, bg); grid_view_clear(s->grid, 0, 0, sx, s->cy, bg);
} if (s->cx > sx - 1)
if (s->cx > sx - 1) { grid_view_clear(s->grid, 0, s->cy, sx, 1, 8);
screen_dirty_clear(s, 0, s->cy, sx - 1, s->cy); else
grid_view_clear(s->grid, 0, s->cy, sx, 1, bg); grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1, 8);
} 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);
}
screen_write_collect_clear(ctx, 0, s->cy);
screen_write_collect_flush(ctx);
tty_write(tty_cmd_clearstartofscreen, &ttyctx); 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); screen_write_initctx(ctx, &ttyctx);
ttyctx.bg = bg; ttyctx.bg = bg;
screen_dirty_clear(s, 0, 0, sx - 1, sy - 1);
/* Scroll into history if it is enabled. */ /* Scroll into history if it is enabled. */
if (s->grid->flags & GRID_HISTORY) if (s->grid->flags & GRID_HISTORY)
grid_view_clear_history(s->grid, bg); grid_view_clear_history(s->grid, bg);
else else
grid_view_clear(s->grid, 0, 0, sx, sy, bg); grid_view_clear(s->grid, 0, 0, sx, sy, bg);
screen_write_collect_clear(ctx, 0, sy);
tty_write(tty_cmd_clearscreen, &ttyctx); tty_write(tty_cmd_clearscreen, &ttyctx);
} }
@ -1006,39 +962,165 @@ screen_write_clearhistory(struct screen_write_ctx *ctx)
gd->hscrolled = gd->hsize = 0; 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. */ /* Write cell data. */
void void
screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc) screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc)
{ {
struct screen *s = ctx->s; struct screen *s = ctx->s;
struct grid *gd = s->grid; 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_line *gl;
struct grid_cell tmp_gc, now_gc;
struct grid_cell_entry *gce; 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 cells. */
/* Ignore padding. */
if (gc->flags & GRID_FLAG_PADDING) if (gc->flags & GRID_FLAG_PADDING)
return; return;
width = gc->data.width; ctx->cells++;
/* /* If the width is zero, combine onto the previous character. */
* 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 (width == 0) { if (width == 0) {
if ((gc = screen_write_combine(ctx, &gc->data, &xx)) != 0) { if ((gc = screen_write_combine(ctx, &gc->data, &xx)) != 0) {
screen_write_cursormove(ctx, xx, s->cy); 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; 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 in insert mode, make space for the cells. */
if (s->mode & MODE_INSERT) { if (s->mode & MODE_INSERT) {
if (s->cx <= sx - width) { grid_view_insert_cells(s->grid, s->cx, s->cy, width, 8);
screen_write_flush(ctx); skip = 0;
xx = sx - s->cx - width; }
grid_view_insert_cells(s->grid, s->cx, s->cy, xx, 8);
}
insert = 1;
} else
insert = 0;
skip = !insert;
/* Check this will fit on the current line and wrap if not. */ /* Check this will fit on the current line and wrap if not. */
if ((s->mode & MODE_WRAP) && s->cx > sx - width) { if ((s->mode & MODE_WRAP) && s->cx > sx - width) {
screen_write_linefeed(ctx, 1); screen_write_linefeed(ctx, 1);
s->cx = 0; /* carriage return */ s->cx = 0;
} }
/* Sanity check cursor position. */ /* Sanity check cursor position. */
if (s->cx > sx - width || s->cy > sy - 1) if (s->cx > sx - width || s->cy > sy - 1)
return; return;
/* Initialise the redraw context. */
screen_write_initctx(ctx, &ttyctx); screen_write_initctx(ctx, &ttyctx);
/* Handle overwriting of UTF-8 characters. */ /* 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; skip = 0;
else if (gc->data.width != 1) else if (gc->data.width != 1)
skip = 0; skip = 0;
else if (gc->data.size != 1)
skip = 0;
else if (gce->data.data != gc->data.data[0]) else if (gce->data.data != gc->data.data[0])
skip = 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. */ /* Update the selection the flag and set the cell. */
selected = screen_check_selection(s, s->cx, s->cy); selected = screen_check_selection(s, s->cx, s->cy);
if (selected && ~gc->flags & GRID_FLAG_SELECTED) { if (selected && (~gc->flags & GRID_FLAG_SELECTED)) {
skip = 0;
memcpy(&tmp_gc, gc, sizeof tmp_gc); memcpy(&tmp_gc, gc, sizeof tmp_gc);
tmp_gc.flags |= GRID_FLAG_SELECTED; tmp_gc.flags |= GRID_FLAG_SELECTED;
grid_view_set_cell(gd, s->cx, s->cy, &tmp_gc); grid_view_set_cell(gd, s->cx, s->cy, &tmp_gc);
} else if (!selected && gc->flags & GRID_FLAG_SELECTED) { } else if (!selected && (gc->flags & GRID_FLAG_SELECTED)) {
skip = 0;
memcpy(&tmp_gc, gc, sizeof tmp_gc); memcpy(&tmp_gc, gc, sizeof tmp_gc);
tmp_gc.flags &= ~GRID_FLAG_SELECTED; tmp_gc.flags &= ~GRID_FLAG_SELECTED;
grid_view_set_cell(gd, s->cx, s->cy, &tmp_gc); grid_view_set_cell(gd, s->cx, s->cy, &tmp_gc);
} else if (!skip) } else if (!skip)
grid_view_set_cell(gd, s->cx, s->cy, gc); 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 * 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; s->cx = sx - last;
/* Create space for character in insert mode. */ /* Create space for character in insert mode. */
if (insert) { if (s->mode & MODE_INSERT) {
screen_write_collect_flush(ctx);
ttyctx.num = width; ttyctx.num = width;
tty_write(tty_cmd_insertcharacter, &ttyctx); tty_write(tty_cmd_insertcharacter, &ttyctx);
} }
/* Write to the screen. */ /* Write to the screen. */
if (selected) { if (!skip) {
screen_write_flush(ctx); if (selected) {
screen_select_cell(s, &tmp_gc, gc); screen_select_cell(s, &tmp_gc, gc);
ttyctx.cell = &tmp_gc; ttyctx.cell = &tmp_gc;
} else
ttyctx.cell = gc;
tty_write(tty_cmd_cell, &ttyctx); tty_write(tty_cmd_cell, &ttyctx);
ctx->written++; 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 } else
ctx->skipped++; ctx->skipped++;
} }

View File

@ -40,9 +40,6 @@ screen_init(struct screen *s, u_int sx, u_int sy, u_int hlimit)
s->ccolour = xstrdup(""); s->ccolour = xstrdup("");
s->tabs = NULL; s->tabs = NULL;
s->dirty = NULL;
s->dirtysize = 0;
screen_reinit(s); screen_reinit(s);
} }
@ -69,7 +66,6 @@ screen_reinit(struct screen *s)
void void
screen_free(struct screen *s) screen_free(struct screen *s)
{ {
free(s->dirty);
free(s->tabs); free(s->tabs);
free(s->title); free(s->title);
free(s->ccolour); free(s->ccolour);
@ -358,7 +354,7 @@ screen_check_selection(struct screen *s, u_int px, u_int py)
xx = sel->sx - 1; xx = sel->sx - 1;
else else
xx = sel->sx; xx = sel->sx;
if (py == sel->sy && px > xx) if (py == sel->sy && (sel->sx == 0 || px > xx))
return (0); return (0);
} else { } else {
/* starting line == ending line. */ /* starting line == ending line. */

19
tmux.h
View File

@ -651,17 +651,18 @@ struct screen {
bitstr_t *tabs; bitstr_t *tabs;
bitstr_t *dirty;
u_int dirtysize;
struct screen_sel sel; struct screen_sel sel;
}; };
/* Screen write context. */ /* Screen write context. */
struct screen_write_collect_item;
struct screen_write_collect_line;
struct screen_write_ctx { struct screen_write_ctx {
struct window_pane *wp; struct window_pane *wp;
struct screen *s; struct screen *s;
u_int dirty;
struct screen_write_collect_item *item;
struct screen_write_collect_line *list;
u_int cells; u_int cells;
u_int written; u_int written;
@ -1042,7 +1043,7 @@ struct tty {
struct grid_cell cell; struct grid_cell cell;
int last_wp; int last_wp;
struct grid_cell last_cell; struct grid_cell last_cell;
#define TTY_NOCURSOR 0x1 #define TTY_NOCURSOR 0x1
@ -1643,6 +1644,7 @@ void tty_write(void (*)(struct tty *, const struct tty_ctx *),
struct tty_ctx *); struct tty_ctx *);
void tty_cmd_alignmenttest(struct tty *, const 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_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_clearendofline(struct tty *, const struct tty_ctx *);
void tty_cmd_clearendofscreen(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 *); 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); 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_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_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(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_clear_lines(struct grid *, u_int, u_int, u_int);
void grid_move_lines(struct grid *, u_int, 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_get_cell(struct grid *, u_int, u_int, struct grid_cell *);
void grid_view_set_cell(struct grid *, u_int, u_int, void grid_view_set_cell(struct grid *, u_int, u_int,
const struct grid_cell *); 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_history(struct grid *, u_int);
void grid_view_clear(struct grid *, u_int, u_int, u_int, u_int, 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); 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_clearstartofscreen(struct screen_write_ctx *, u_int);
void screen_write_clearscreen(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_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_cell(struct screen_write_ctx *, const struct grid_cell *);
void screen_write_setselection(struct screen_write_ctx *, u_char *, u_int); void screen_write_setselection(struct screen_write_ctx *, u_char *, u_int);
void screen_write_rawstring(struct screen_write_ctx *, u_char *, u_int); void screen_write_rawstring(struct screen_write_ctx *, u_char *, u_int);

9
tty.c
View File

@ -1169,6 +1169,15 @@ tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx)
tty_cell(tty, ctx->cell, ctx->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 void
tty_cmd_setselection(struct tty *tty, const struct tty_ctx *ctx) tty_cmd_setselection(struct tty *tty, const struct tty_ctx *ctx)
{ {