This one works quite well.

$ rm -f f; for i in `seq 1000`; do cat tmux.h >>f; done
$ time cat f
outside = 2.714
tmux    = 9.133
screen  = 11.416


diff --git a/input.c b/input.c
index 17f81dd7..4653c992 100644
--- a/input.c
+++ b/input.c
@@ -871,9 +871,10 @@ input_parse(struct window_pane *wp)
 
        buf = EVBUFFER_DATA(evb);
        len = EVBUFFER_LENGTH(evb);
-       notify_input(wp, evb);
        off = 0;
 
+       notify_input(wp, evb);
+
        log_debug("%s: %%%u %s, %zu bytes: %.*s", __func__, wp->id,
            ictx->state->name, len, (int)len, buf);
 
diff --git a/screen-write.c b/screen-write.c
index 227316cb..c8ea1be5 100644
--- a/screen-write.c
+++ b/screen-write.c
@@ -38,47 +38,111 @@ 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_dirty_item {
+       struct grid_cell        gc;
+       u_int                   x;
+
+       TAILQ_ENTRY (screen_write_dirty_item) entry;
+};
+static struct screen_write_dirty_item **screen_write_dirty;
+static u_int                           screen_write_dirty_x;
+static u_int                           screen_write_dirty_y;
+
+/* Set a dirty bit. */
+static void
+screen_write_dirty_set(struct screen_write_ctx *ctx, u_int x, u_int y,
+    const struct grid_cell *gc)
+{
+       struct screen_write_dirty_item  *di;
+
+       di = &screen_write_dirty[y][x];
+       if (di->gc.data.size == 0) {
+               TAILQ_INSERT_TAIL(&ctx->dirtylist[y], di, entry);
+               ctx->dirty++;
+       }
+
+       di->x = x;
+       memcpy(&di->gc, gc, sizeof di->gc);
+}
+
+/* Clear a dirty region. */
+static void
+screen_write_dirty_clear(struct screen_write_ctx *ctx, u_int sx, u_int sy,
+    u_int ex, u_int ey)
+{
+       struct screen_write_dirty_item  *di, *di1;
+       u_int                            y;
+
+       for (y = sy; y < ey + 1; y++) {
+               if (TAILQ_EMPTY (&ctx->dirtylist[y]))
+                       continue;
+               TAILQ_FOREACH_SAFE(di, &ctx->dirtylist[y], entry, di1) {
+                       if (di->x < sx || di->x > ex)
+                               continue;
+                       di->gc.data.size = 0;
+                       TAILQ_REMOVE(&ctx->dirtylist[y], di, entry);
+               }
+       }
+}
+
+/* Allocate dirty table. */
+static void
+screen_write_dirty_allocate(struct screen_write_ctx *ctx)
+{
+       u_int   sx, sy, y;
+       size_t  size;
+
+       sx = screen_size_x(ctx->s);
+       sy = screen_size_y(ctx->s);
+
+       if (screen_write_dirty_x >= sx && screen_write_dirty_y >= sy)
+               return;
+       size = 0;
+
+       for (y = 0; y < screen_write_dirty_y; y++)
+               free(screen_write_dirty[y]);
+       free(screen_write_dirty);
+
+       screen_write_dirty = xcalloc(sy, sizeof *screen_write_dirty);
+       size += (sy * sizeof *screen_write_dirty);
+       for (y = 0; y < sy; y++) {
+               screen_write_dirty[y] = xcalloc(sx,
+                   sizeof **screen_write_dirty);
+               size += (sx * sizeof **screen_write_dirty);
+       }
+
+       screen_write_dirty_x = sx;
+       screen_write_dirty_y = sy;
+
+       log_debug("%s: allocated %zu bytes", __func__, size);
+}
 
 /* Initialize writing with a window. */
 void
 screen_write_start(struct screen_write_ctx *ctx, struct window_pane *wp,
     struct screen *s)
 {
-       u_int            size;
-       char             tmp[16];
-       const char      *cp = tmp;
+       char    tmp[16];
+       u_int   y;
 
        ctx->wp = wp;
        if (wp != NULL && s == NULL)
                ctx->s = wp->screen;
        else
                ctx->s = s;
+       screen_write_dirty_allocate (ctx);
 
-       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->dirtylist = xcalloc(screen_size_y(ctx->s), sizeof *ctx->dirtylist);
+       for (y = 0; y < screen_size_y(ctx->s); y++)
+               TAILQ_INIT(&ctx->dirtylist[y]);
        ctx->dirty = 0;
 
        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. */
@@ -89,49 +153,45 @@ screen_write_stop(struct screen_write_ctx *ctx)
 
        log_debug("%s: %u of %u written (dirty %u, skipped %u)", __func__,
            ctx->written, ctx->cells, ctx->cells - ctx->written, ctx->skipped);
+
+       free(ctx->dirtylist);
 }
 
 /* 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;
+       struct screen                   *s = ctx->s;
+       struct screen_write_dirty_item  *di;
+       u_int                            cx, cy, y;
+       struct tty_ctx                   ttyctx;
 
        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);
-
+       for (y = 0; y < screen_size_y(ctx->s); y++) {
+               if (TAILQ_EMPTY(&ctx->dirtylist[y]))
+                       continue;
+               TAILQ_FOREACH(di, &ctx->dirtylist[y], entry) {
+                       screen_write_cursormove(ctx, di->x, y);
                        screen_write_initctx(ctx, &ttyctx);
-                       ttyctx.cell = &gc;
+
+                       ttyctx.cell = &di->gc;
                        tty_write(tty_cmd_cell, &ttyctx);
                        ctx->written++;
 
-                       if (++dirty == ctx->dirty)
-                               break;
+                       di->gc.data.size = 0;
                }
-               if (dirty == ctx->dirty)
-                       break;
+               TAILQ_INIT(&ctx->dirtylist[y]);
        }
        ctx->dirty = 0;
 
+       ctx->scrolled = 0;
+
        s->cx = cx;
        s->cy = cy;
 }
@@ -590,7 +650,7 @@ screen_write_alignmenttest(struct screen_write_ctx *ctx)
 
        screen_write_initctx(ctx, &ttyctx);
 
-       screen_dirty_clear(s, 0, 0, sx - 1, sy  - 1);
+       screen_write_dirty_clear(ctx, 0, 0, sx - 1, sy  - 1);
 
        memcpy(&gc, &grid_default_cell, sizeof gc);
        utf8_set(&gc.data, 'E');
@@ -679,7 +739,8 @@ screen_write_clearcharacter(struct screen_write_ctx *ctx, 
u_int nx)
        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);
+               screen_write_dirty_clear(ctx, s->cx, s->cy, s->cx + nx - 1,
+                   s->cy);
                grid_view_clear(s->grid, s->cx, s->cy, nx, 1, 8);
        } else
                return;
@@ -798,7 +859,7 @@ screen_write_clearline(struct screen_write_ctx *ctx, u_int 
bg)
        if (gl->cellsize == 0 && bg == 8)
                return;
 
-       screen_dirty_clear(s, 0, s->cy, sx - 1, s->cy);
+       screen_write_dirty_clear(ctx, 0, s->cy, sx - 1, s->cy);
        grid_view_clear(s->grid, 0, s->cy, sx, 1, bg);
 
        tty_write(tty_cmd_clearline, &ttyctx);
@@ -820,7 +881,7 @@ screen_write_clearendofline(struct screen_write_ctx *ctx, 
u_int bg)
        if (s->cx > sx - 1 || (s->cx >= gl->cellsize && bg == 8))
                return;
 
-       screen_dirty_clear(s, s->cx, s->cy, sx - 1, s->cy);
+       screen_write_dirty_clear(ctx, s->cx, s->cy, sx - 1, s->cy);
        grid_view_clear(s->grid, s->cx, s->cy, sx - s->cx, 1, bg);
 
        tty_write(tty_cmd_clearendofline, &ttyctx);
@@ -838,10 +899,10 @@ screen_write_clearstartofline(struct screen_write_ctx 
*ctx, u_int bg)
        ttyctx.bg = bg;
 
        if (s->cx > sx - 1) {
-               screen_dirty_clear(s, 0, s->cy, sx - 1, s->cy);
+               screen_write_dirty_clear(ctx, 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);
+               screen_write_dirty_clear(ctx, 0, s->cy, s->cx, s->cy);
                grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1, bg);
        }
 
@@ -901,6 +962,8 @@ screen_write_scrollregion(struct screen_write_ctx *ctx, 
u_int rupper,
 
        s->rupper = rupper;
        s->rlower = rlower;
+
+       ctx->scrolled = 0;
 }
 
 /* Line feed. */
@@ -909,8 +972,11 @@ screen_write_linefeed(struct screen_write_ctx *ctx, int 
wrapped)
 {
        struct screen           *s = ctx->s;
        struct grid_line        *gl;
-       struct tty_ctx           ttyctx;
-       u_int                    sx = screen_size_x(s), sy = screen_size_y(s);
+       struct tty_ctx           ttyctx;
+       u_int                    y, sx, sy;
+
+       sx = screen_size_x(s);
+       sy = screen_size_y(s);
 
        screen_write_initctx(ctx, &ttyctx);
 
@@ -921,14 +987,38 @@ screen_write_linefeed(struct screen_write_ctx *ctx, int 
wrapped)
                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);
+               /*
+                * Clear the dirty cells for th etop line of the region and
+                * move all the dirty cells in the region up by one, so they
+                * match the cells on screen after the scroll.
+                */
+               TAILQ_INIT(&ctx->dirtylist[s->rupper]);
+               free(screen_write_dirty[s->rupper]);
+
+               for (y = s->rupper; y < s->rlower; y++) {
+                       screen_write_dirty[y] = screen_write_dirty[y + 1];
+                       memcpy(&ctx->dirtylist[y], &ctx->dirtylist[y + 1],
+                           sizeof *ctx->dirtylist);
+               }
+
+               TAILQ_INIT(&ctx->dirtylist[s->rlower]);
+               screen_write_dirty[s->rlower] = xcalloc(sx,
+                   sizeof **screen_write_dirty);
+
+               /*
+                * Scroll the grid. Also scroll the terminal, unless we have
+                * already scrolled more than the region, in which case we
+                * would just be scrolling in more blank lines.
+                */
                grid_view_scroll_region_up(s->grid, s->rupper, s->rlower);
+               if (ctx->scrolled < s->rlower - s->rupper) {
+                       ttyctx.num = wrapped;
+                       tty_write(tty_cmd_linefeed, &ttyctx);
+               } else
+                       log_debug("ignoring line feed %u", ctx->scrolled);
+               ctx->scrolled++;
        } else if (s->cy < sy - 1)
                s->cy++;
-
-       ttyctx.num = wrapped;
-       tty_write(tty_cmd_linefeed, &ttyctx);
 }
 
 /* Carriage return (cursor to start of line). */
@@ -953,15 +1043,16 @@ screen_write_clearendofscreen(struct screen_write_ctx 
*ctx, u_int bg)
 
        /* Scroll into history if it is enabled and clearing entire screen. */
        if (s->cx == 0 && s->cy == 0 && s->grid->flags & GRID_HISTORY) {
-               screen_dirty_clear(s, 0, 0, sx - 1, sy  - 1);
+               screen_write_dirty_clear(ctx, 0, 0, sx - 1, sy  - 1);
                grid_view_clear_history(s->grid, bg);
        } else {
                if (s->cx <= sx - 1) {
-                       screen_dirty_clear(s, s->cx, s->cy, sx - 1, s->cy);
+                       screen_write_dirty_clear(ctx, s->cx, s->cy, sx - 1,
+                           s->cy);
                        grid_view_clear(s->grid, s->cx, s->cy, sx - s->cx, 1,
                            bg);
                }
-               screen_dirty_clear(s, 0, s->cy + 1, sx - 1, sy - 1);
+               screen_write_dirty_clear(ctx, 0, s->cy + 1, sx - 1, sy - 1);
                grid_view_clear(s->grid, 0, s->cy + 1, sx, sy - (s->cy + 1),
                    bg);
        }
@@ -980,14 +1071,14 @@ screen_write_clearstartofscreen(struct screen_write_ctx 
*ctx)
        screen_write_initctx(ctx, &ttyctx);
 
        if (s->cy > 0) {
-               screen_dirty_clear(s, 0, 0, sx - 1, s->cy);
+               screen_write_dirty_clear(ctx, 0, 0, sx - 1, s->cy);
                grid_view_clear(s->grid, 0, 0, sx, s->cy, 8);
        }
        if (s->cx > sx - 1) {
-               screen_dirty_clear(s, 0, s->cy, sx - 1, s->cy);
+               screen_write_dirty_clear(ctx, 0, s->cy, sx - 1, s->cy);
                grid_view_clear(s->grid, 0, s->cy, sx, 1, 8);
        } else {
-               screen_dirty_clear(s, 0, s->cy, s->cx, s->cy);
+               screen_write_dirty_clear(ctx, 0, s->cy, s->cx, s->cy);
                grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1, 8);
        }
 
@@ -1005,7 +1096,7 @@ 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);
+       screen_write_dirty_clear(ctx, 0, 0, sx - 1, sy  - 1);
 
        /* Scroll into history if it is enabled. */
        if (s->grid->flags & GRID_HISTORY)
@@ -1180,24 +1271,8 @@ screen_write_cell(struct screen_write_ctx *ctx, const 
struct grid_cell *gc)
                        ttyctx.cell = gc;
                        tty_write(tty_cmd_cell, &ttyctx);
                        ctx->written++;
-               } else {
-                       /*
-                        * 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 if (ctx->wp != NULL)
+                       screen_write_dirty_set(ctx, ttyctx.ocx, ttyctx.ocy, gc);
        } else
                ctx->skipped++;
 }
diff --git a/screen.c b/screen.c
index d6fbfecd..6088c14a 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);
diff --git a/tmux.h b/tmux.h
index 9dee7a1a..24402e8f 100644
--- a/tmux.h
+++ b/tmux.h
@@ -72,9 +72,9 @@ struct tmuxproc;
  * of times.
  */
 #define READ_FAST_SIZE 16384
-#define READ_SLOW_SIZE 128
+#define READ_SLOW_SIZE 4096
 
-#define READ_FULL_SIZE (4096 - 16)
+#define READ_FULL_SIZE (READ_FAST_SIZE - 16)
 #define READ_EMPTY_SIZE 16
 
 #define READ_CHANGE_HITS 3
@@ -662,17 +662,18 @@ struct screen {
 
        bitstr_t                *tabs;
 
-       bitstr_t                *dirty;
-       u_int                    dirtysize;
-
        struct screen_sel        sel;
 };
 
 /* Screen write context. */
+struct screen_write_dirty_item;
 struct screen_write_ctx {
        struct window_pane      *wp;
        struct screen           *s;
+
+       TAILQ_HEAD(, screen_write_dirty_item) *dirtylist;
        u_int                    dirty;
+       u_int                    scrolled;
 
        u_int                    cells;
        u_int                    written;


-- 
You received this message because you are subscribed to the Google Groups 
"tmux-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send an email to [email protected].
For more options, visit https://groups.google.com/d/optout.

Reply via email to