mirror of
				https://github.com/tmux/tmux.git
				synced 2025-11-04 00:56:10 +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:
		
							
								
								
									
										421
									
								
								screen-write.c
									
									
									
									
									
								
							
							
						
						
									
										421
									
								
								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++;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user