diff --git a/cmd-display-panes.c b/cmd-display-panes.c index 5a1782ff..054c3c35 100644 --- a/cmd-display-panes.c +++ b/cmd-display-panes.c @@ -84,7 +84,7 @@ cmd_display_panes_draw_format(struct screen_redraw_ctx *ctx, struct client *c = ctx->c; struct tty *tty = &c->tty; struct session *s = c->session; - struct screen screen; + struct screen *sc = wp->screen, screen; struct screen_write_ctx sctx; struct visible_ranges *r; struct visible_range *ri; @@ -97,6 +97,8 @@ cmd_display_panes_draw_format(struct screen_redraw_ctx *ctx, screen_init(&screen, sx, 1, 0); screen_write_start(&sctx, &screen); + screen_write_fast_copy(&sctx, sc, 0, sc->grid->hsize, sx, 1); + screen_write_cursormove(&sctx, 0, 0, 0); format_draw(&sctx, gc, sx, expanded, NULL, 0); screen_write_stop(&sctx); free(expanded); @@ -105,7 +107,7 @@ cmd_display_panes_draw_format(struct screen_redraw_ctx *ctx, for (i = 0; i < r->used; i++) { ri = &r->ranges[i]; tty_draw_line(tty, &screen, ri->px - px, 0, ri->nx, - ri->px - ctx->ox, yoff, gc, NULL); + ri->px - ctx->ox, yoff, NULL); } screen_free(&screen); } @@ -199,7 +201,7 @@ cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx, llen = 0; if (sx < len * 6 || sy < 5) { - tty_attributes(tty, &fgc, &grid_default_cell, NULL, NULL); + tty_attributes(tty, &fgc, NULL); if (sx >= len + llen + 1) { len += llen + 1; cx = xoff + px - len / 2; @@ -220,7 +222,7 @@ cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx, px -= len * 3; py -= 2; - tty_attributes(tty, &bgc, &grid_default_cell, NULL, NULL); + tty_attributes(tty, &bgc, NULL); for (ptr = buf; *ptr != '\0'; ptr++) { if (*ptr < '0' || *ptr > '9') continue; @@ -242,7 +244,7 @@ cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx, goto out; cmd_display_panes_draw_format(ctx, wp, xoff, yoff, sx, &fgc); if (llen != 0) { - tty_attributes(tty, &fgc, &grid_default_cell, NULL, NULL); + tty_attributes(tty, &fgc, NULL); cx = xoff + sx / 2 + len * 3 - llen - 1; cy = yoff + py + 5; cmd_display_panes_put(ctx, wp, cx, cy, lbuf, llen); diff --git a/cmd-find.c b/cmd-find.c index 747d204a..76f2742a 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -147,6 +147,18 @@ cmd_find_session_better(struct session *s, struct session *than, int flags) return (timercmp(&s->activity_time, &than->activity_time, >)); } +/* Can this session be usefully targeted? */ +static int +cmd_find_session_valid(struct session *s) +{ + if (!session_alive(s) || + s->curw == NULL || + s->curw->window == NULL || + s->curw->window->active == NULL) + return (0); + return (1); +} + /* Find best session from a list, or all if list is NULL. */ static struct session * cmd_find_best_session(struct session **slist, u_int ssize, int flags) @@ -159,11 +171,15 @@ cmd_find_best_session(struct session **slist, u_int ssize, int flags) s = NULL; if (slist != NULL) { for (i = 0; i < ssize; i++) { + if (!cmd_find_session_valid(slist[i])) + continue; if (cmd_find_session_better(slist[i], s, flags)) s = slist[i]; } } else { RB_FOREACH(s_loop, sessions, &sessions) { + if (!cmd_find_session_valid(s_loop)) + continue; if (cmd_find_session_better(s_loop, s, flags)) s = s_loop; } diff --git a/cmd-kill-pane.c b/cmd-kill-pane.c index ed833643..836551f0 100644 --- a/cmd-kill-pane.c +++ b/cmd-kill-pane.c @@ -50,8 +50,6 @@ cmd_kill_pane_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); struct cmd_find_state *target = cmdq_get_target(item); - struct session *s = target->s; - struct winlink *wl = target->wl; struct window_pane *wp = target->wp; const char *filter = args_get(args, 'f'); diff --git a/cmd-list-keys.c b/cmd-list-keys.c index d8065259..c20768f3 100644 --- a/cmd-list-keys.c +++ b/cmd-list-keys.c @@ -237,7 +237,7 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item) cmd_list_keys_format_add_key_binding(ft, l[i], prefix); line = format_expand(ft, template); - if ((single && tc != NULL) || n == 1) + if (single && tc != NULL && (~tc->flags & CLIENT_CONTROL)) status_message_set(tc, -1, 1, 0, 0, "%s", line); else if (*line != '\0') cmdq_print(item, "%s", line); diff --git a/cmd-split-window.c b/cmd-split-window.c index 289149b9..feba61c2 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -38,8 +38,8 @@ const struct cmd_entry cmd_new_pane_entry = { .name = "new-pane", .alias = "newp", - .args = { "bBc:de:EfF:hIkl:Lm:p:PR:s:S:t:T:vx:X:y:Y:Z", 0, -1, NULL }, - .usage = "[-bdefhIklPvZ] [-c start-directory] [-e environment] " + .args = { "bc:de:EfF:hIkl:Lm:p:PR:s:S:t:T:vWx:X:y:Y:Z", 0, -1, NULL }, + .usage = "[-bdefhIklPvWZ] [-c start-directory] [-e environment] " "[-F format] [-l size] [-m message] [-p percentage] " "[-s style] [-S active-border-style] " "[-R inactive-border-style] [-T title] [-x width] [-y height] " @@ -56,8 +56,8 @@ const struct cmd_entry cmd_split_window_entry = { .name = "split-window", .alias = "splitw", - .args = { "bBc:de:EfF:hIkl:m:p:PR:s:S:t:T:vZ", 0, -1, NULL }, - .usage = "[-bdefhIklPvZ] [-c start-directory] [-e environment] " + .args = { "bc:de:EfF:hIkl:m:p:PR:s:S:t:T:vWZ", 0, -1, NULL }, + .usage = "[-bdefhIklPvWZ] [-c start-directory] [-e environment] " "[-F format] [-l size] [-m message] [-p percentage] " "[-s style] [-S active-border-style] " "[-R inactive-border-style] [-T title] " CMD_TARGET_PANE_USAGE " " @@ -245,13 +245,13 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) if (input) return (CMD_RETURN_WAIT); - if (args_has(args, 'B')) { + if (args_has(args, 'W')) { /* - * With -B, block this command queue item until the pane's - * command exits; window_pane_block_finish will be called to + * With -W, block this command queue item until the pane's + * command exits; window_pane_wait_finish will be called to * continue it. */ - new_wp->block_item = item; + new_wp->wait_item = item; return (CMD_RETURN_WAIT); } return (CMD_RETURN_NORMAL); diff --git a/grid.c b/grid.c index 36c99184..52da173a 100644 --- a/grid.c +++ b/grid.c @@ -292,7 +292,7 @@ grid_free_line(struct grid *gd, u_int py) } /* Free several lines. */ -static void +void grid_free_lines(struct grid *gd, u_int py, u_int ny) { u_int yy; @@ -320,6 +320,10 @@ grid_create(u_int sx, u_int sy, u_int hlimit) gd->hsize = 0; gd->hlimit = hlimit; + gd->scroll_added = 0; + gd->scroll_collected = 0; + gd->scroll_generation = 0; + if (gd->sy != 0) gd->linedata = xcalloc(gd->sy, sizeof *gd->linedata); else @@ -405,6 +409,7 @@ grid_collect_history(struct grid *gd, int all) grid_trim_history(gd, ny); gd->hsize -= ny; + gd->scroll_collected += ny; if (gd->hscrolled > gd->hsize) gd->hscrolled = gd->hsize; } @@ -442,6 +447,7 @@ grid_scroll_history(struct grid *gd, u_int bg) grid_compact_line(&gd->linedata[gd->hsize]); gd->linedata[gd->hsize].time = current_time; gd->hsize++; + gd->scroll_added++; } /* Clear the history. */ @@ -452,6 +458,7 @@ grid_clear_history(struct grid *gd) gd->hscrolled = 0; gd->hsize = 0; + gd->scroll_generation++; gd->linedata = xreallocarray(gd->linedata, gd->sy, sizeof *gd->linedata); @@ -489,6 +496,7 @@ grid_scroll_history_region(struct grid *gd, u_int upper, u_int lower, u_int bg) /* Move the history offset down over the line. */ gd->hscrolled++; gd->hsize++; + gd->scroll_added++; } /* Expand line to fit to cell. */ @@ -1510,6 +1518,7 @@ grid_reflow(struct grid *gd, u_int sx) free(gd->linedata); gd->linedata = target->linedata; free(target); + gd->scroll_generation++; } /* Convert to position based on wrapped lines. */ diff --git a/key-bindings.c b/key-bindings.c index 3cf6c9b5..647971a2 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -533,7 +533,7 @@ key_bindings_init(void) "bind -Tcopy-mode g { command-prompt -p'(goto line)' { send -X goto-line -- '%%' } }", "bind -Tcopy-mode n { send -X search-again }", "bind -Tcopy-mode q { send -X cancel }", - "bind -Tcopy-mode r { send -X refresh-from-pane }", + "bind -Tcopy-mode r { send -X refresh-toggle }", "bind -Tcopy-mode t { command-prompt -1p'(jump to forward)' { send -X jump-to-forward -- '%%' } }", "bind -Tcopy-mode Home { send -X start-of-line }", "bind -Tcopy-mode End { send -X end-of-line }", @@ -641,7 +641,7 @@ key_bindings_init(void) "bind -Tcopy-mode-vi n { send -X search-again }", "bind -Tcopy-mode-vi o { send -X other-end }", "bind -Tcopy-mode-vi q { send -X cancel }", - "bind -Tcopy-mode-vi r { send -X refresh-from-pane }", + "bind -Tcopy-mode-vi r { send -X refresh-toggle }", "bind -Tcopy-mode-vi t { command-prompt -1p'(jump to forward)' { send -X jump-to-forward -- '%%' } }", "bind -Tcopy-mode-vi v { send -X rectangle-toggle }", "bind -Tcopy-mode-vi w { send -X next-word }", diff --git a/menu.c b/menu.c index 4f122831..98d441fe 100644 --- a/menu.c +++ b/menu.c @@ -281,10 +281,8 @@ menu_draw_cb(struct client *c, void *data, &md->style_gc, &md->border_style_gc, &md->selected_style_gc); screen_write_stop(&ctx); - for (i = 0; i < screen_size_y(&md->s); i++) { - tty_draw_line(tty, s, 0, i, menu->width + 4, px, py + i, - &grid_default_cell, NULL); - } + for (i = 0; i < screen_size_y(&md->s); i++) + tty_draw_line(tty, s, 0, i, menu->width + 4, px, py + i, NULL); } void diff --git a/options-table.c b/options-table.c index f78f047e..c3bdca0e 100644 --- a/options-table.c +++ b/options-table.c @@ -738,7 +738,7 @@ const struct options_table_entry options_table[] = { .flags = OPTIONS_TABLE_IS_STYLE, .separator = ",", .text = "Style of the command prompt when in command mode, if " - "'mode-keys' is set to 'vi'." + "'status-keys' is set to 'vi'." }, { .name = "message-format", diff --git a/popup.c b/popup.c index cb8dbf06..ed2f4e63 100644 --- a/popup.c +++ b/popup.c @@ -208,7 +208,8 @@ popup_init_ctx_cb(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx) memcpy(&ttyctx->defaults, &pd->defaults, sizeof ttyctx->defaults); ttyctx->flags &= ~TTY_CTX_WINDOW_BIGGER; - ttyctx->palette = &pd->palette; + ttyctx->style_ctx.defaults = &ttyctx->defaults; + ttyctx->style_ctx.palette = &pd->palette; ttyctx->redraw_cb = popup_redraw_cb; ttyctx->set_client_cb = popup_set_client_cb; ttyctx->arg = pd; @@ -294,8 +295,8 @@ popup_draw_cb(struct client *c, void *data, struct screen_redraw_ctx *rctx) struct screen s; struct screen_write_ctx ctx; u_int i, px = pd->px, py = pd->py; - struct colour_palette *palette = &pd->palette; struct grid_cell defaults; + struct tty_style_ctx style_ctx; popup_reapply_styles(pd); @@ -321,9 +322,12 @@ popup_draw_cb(struct client *c, void *data, struct screen_redraw_ctx *rctx) memcpy(&defaults, &pd->defaults, sizeof defaults); if (defaults.fg == 8) - defaults.fg = palette->fg; + defaults.fg = pd->palette.fg; if (defaults.bg == 8) - defaults.bg = palette->bg; + defaults.bg = pd->palette.bg; + style_ctx.defaults = &defaults; + style_ctx.palette = &pd->palette; + style_ctx.hyperlinks = s.hyperlinks; if (pd->md != NULL) { c->overlay_check = menu_check_cb; @@ -332,10 +336,8 @@ popup_draw_cb(struct client *c, void *data, struct screen_redraw_ctx *rctx) c->overlay_check = NULL; c->overlay_data = NULL; } - for (i = 0; i < pd->sy; i++) { - tty_draw_line(tty, &s, 0, i, pd->sx, px, py + i, &defaults, - palette); - } + for (i = 0; i < pd->sy; i++) + tty_draw_line(tty, &s, 0, i, pd->sx, px, py + i, &style_ctx); screen_free(&s); if (pd->md != NULL) { c->overlay_check = NULL; diff --git a/screen-redraw.c b/screen-redraw.c index c3ffe815..80ee85a1 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -715,7 +715,7 @@ screen_redraw_draw_pane_status(struct screen_redraw_ctx *ctx) /* Right not visible. */ l = 0; x = xoff - ctx->ox; - width = size - x; + width = ctx->sx - x; } r = tty_check_overlay_range(tty, x, yoff, width); @@ -727,8 +727,7 @@ screen_redraw_draw_pane_status(struct screen_redraw_ctx *ctx) if (ri->nx == 0) continue; tty_draw_line(tty, s, l + (ri->px - x), 0, ri->nx, - ri->px, yoff - ctx->oy, - &grid_default_cell, NULL); + ri->px, yoff - ctx->oy, NULL); } } tty_cursor(tty, 0, 0); @@ -1046,7 +1045,7 @@ screen_redraw_draw_borders_cell(struct screen_redraw_ctx *ctx, u_int i, u_int j) screen_redraw_draw_border_arrows(ctx, i, j, cell_type, wp, active, &gc); - tty_cell(tty, &gc, &grid_default_cell, NULL, NULL); + tty_cell(tty, &gc, NULL); if (isolates) tty_puts(tty, START_ISOLATE); } @@ -1106,10 +1105,8 @@ screen_redraw_draw_status(struct screen_redraw_ctx *ctx) y = 0; else y = c->tty.sy - ctx->statuslines; - for (i = 0; i < ctx->statuslines; i++) { - tty_draw_line(tty, s, 0, i, UINT_MAX, 0, y + i, - &grid_default_cell, NULL); - } + for (i = 0; i < ctx->statuslines; i++) + tty_draw_line(tty, s, 0, i, UINT_MAX, 0, y + i, NULL); } /* @@ -1304,6 +1301,7 @@ screen_redraw_draw_pane(struct screen_redraw_ctx *ctx, struct window_pane *wp) struct screen *s = wp->screen; struct colour_palette *palette = &wp->palette; struct grid_cell defaults; + struct tty_style_ctx style_ctx; u_int j, k, woy, wx, wy, py, width; struct visible_ranges *r; struct visible_range *ri; @@ -1378,10 +1376,15 @@ screen_redraw_draw_pane(struct screen_redraw_ctx *ctx, struct window_pane *wp) width = ctx->sx - wx; } + /* Set up the default style. */ + tty_default_colours(&defaults, wp); + style_ctx.defaults = &defaults; + style_ctx.palette = palette; + style_ctx.hyperlinks = s->hyperlinks; + /* Get visible ranges of line before we draw it. */ r = tty_check_overlay_range(tty, wx, wy, width); r = screen_redraw_get_visible_ranges(wp, wx, wy, width, r); - tty_default_colours(&defaults, wp); for (k = 0; k < r->used; k++) { ri = &r->ranges[k]; if (ri->nx == 0) @@ -1392,7 +1395,7 @@ screen_redraw_draw_pane(struct screen_redraw_ctx *ctx, struct window_pane *wp) ri->px + (int)ctx->ox - wp->xoff, j, ri->nx, ri->px, py, ri->nx); tty_draw_line(tty, s, ri->px + (int)ctx->ox - wp->xoff, - j, ri->nx, ri->px, py, &defaults, palette); + j, ri->nx, ri->px, py, &style_ctx); } } @@ -1569,16 +1572,14 @@ screen_redraw_draw_scrollbar(struct screen_redraw_ctx *ctx, if ((sb_pos == PANE_SCROLLBARS_LEFT && i >= sb_w && i < sb_w + sb_pad) || (sb_pos == PANE_SCROLLBARS_RIGHT && - i < sb_pad)) { - tty_cell(tty, &grid_default_cell, - &grid_default_cell, NULL, NULL); - } else { + i < sb_pad)) + tty_cell(tty, &grid_default_cell, NULL); + else { if (j >= slider_y && j < slider_y + slider_h) gcp = &slgc; else gcp = &gc; - tty_cell(tty, gcp, &grid_default_cell, NULL, - NULL); + tty_cell(tty, gcp, NULL); } } } diff --git a/screen-write.c b/screen-write.c index 6c4543dc..d28c2bb4 100644 --- a/screen-write.c +++ b/screen-write.c @@ -222,7 +222,8 @@ static void screen_write_initctx(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx, int is_sync, int check_obscured) { - struct screen *s = ctx->s; + struct screen *s = ctx->s; + struct colour_palette *palette = NULL; memset(ttyctx, 0, sizeof *ttyctx); @@ -239,19 +240,23 @@ screen_write_initctx(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx, ttyctx->flags |= TTY_CTX_PANE_OBSCURED; memcpy(&ttyctx->defaults, &grid_default_cell, sizeof ttyctx->defaults); + ttyctx->style_ctx.defaults = &ttyctx->defaults; + ttyctx->style_ctx.hyperlinks = ctx->s->hyperlinks; + if (ctx->init_ctx_cb != NULL) { ctx->init_ctx_cb(ctx, ttyctx); - if (ttyctx->palette != NULL) { + if (ttyctx->style_ctx.palette != NULL) { + palette = ttyctx->style_ctx.palette; if (ttyctx->defaults.fg == 8) - ttyctx->defaults.fg = ttyctx->palette->fg; + ttyctx->defaults.fg = palette->fg; if (ttyctx->defaults.bg == 8) - ttyctx->defaults.bg = ttyctx->palette->bg; + ttyctx->defaults.bg = palette->bg; } } else { ttyctx->redraw_cb = screen_write_redraw_cb; if (ctx->wp != NULL) { tty_default_colours(&ttyctx->defaults, ctx->wp); - ttyctx->palette = &ctx->wp->palette; + ttyctx->style_ctx.palette = &ctx->wp->palette; ttyctx->set_client_cb = screen_write_set_client_cb; ttyctx->arg = ctx->wp; } diff --git a/server.c b/server.c index 7b77da73..bf556fd5 100644 --- a/server.c +++ b/server.c @@ -497,7 +497,7 @@ server_child_exited(pid_t pid, int status) log_debug("%%%u exited", wp->id); wp->flags |= PANE_EXITED; - window_pane_block_finish(wp); + window_pane_wait_finish(wp); if (window_pane_destroy_ready(wp)) server_destroy_pane(wp, 1); diff --git a/tmux.1 b/tmux.1 index 447d3575..97a0d608 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2297,11 +2297,22 @@ Toggle rectangle selection mode. .Xc Cycles the current line between centre, top, and bottom. .It Xo -.Ic refresh\-from\-pane +.Ic refresh\-on +.Xc +Turn on automatic refresh of the content from the pane, so that new output +appears while in copy mode. +Automatic refresh is off by default; it will scroll only while the cursor is at +the bottom and is paused while a selection is in progress. +.It Xo +.Ic refresh\-off +.Xc +Turn off automatic refresh of the content from the pane. +.It Xo +.Ic refresh\-toggle (vi: r) (emacs: r) .Xc -Refresh the content from the pane. +Toggle automatic refresh of the content from the pane. .It Xo .Ic scroll\-bottom .Xc @@ -3457,7 +3468,7 @@ but a different format may be specified with .Fl F . .Tg newp .It Xo Ic new\-pane -.Op Fl bBdefhIkPvZ +.Op Fl bdefhIkPvWZ .Op Fl c Ar start\-directory .Op Fl e Ar environment .Op Fl F Ar format @@ -3533,13 +3544,13 @@ but also sets the option for this pane to .Ar message . .Pp -.Fl B -blocks until +.Fl W +Waits until .Ar shell\-command exits, then returns its exit status. For example: .Bd -literal -offset indent -$ tmux new-pane -B 'vi afile' +$ tmux new-pane -W 'vi afile' $ echo $? 0 .Ed diff --git a/tmux.h b/tmux.h index 47542d0e..48b90eb9 100644 --- a/tmux.h +++ b/tmux.h @@ -871,6 +871,10 @@ struct grid { u_int hsize; u_int hlimit; + u_int scroll_added; + u_int scroll_collected; + u_int scroll_generation; + struct grid_line *linedata; }; @@ -1305,7 +1309,7 @@ struct window_pane { char tty[TTY_NAME_MAX]; int status; struct timeval dead_time; - struct cmdq_item *block_item; /* new-pane -B: waiting for pane exit */ + struct cmdq_item *wait_item; /* new-pane -W: waiting for pane exit */ int fd; struct bufferevent *event; @@ -1654,6 +1658,13 @@ struct tty_term { }; LIST_HEAD(tty_terms, tty_term); +/* Terminal style context. */ +struct tty_style_ctx { + const struct grid_cell *defaults; + struct colour_palette *palette; + struct hyperlinks *hyperlinks; +}; + /* Client terminal. */ struct tty { struct client *client; @@ -1804,7 +1815,7 @@ struct tty_ctx { /* The default colours and palette. */ struct grid_cell defaults; - struct colour_palette *palette; + struct tty_style_ctx style_ctx; /* Containing region (usually window) offset and size. */ u_int wox; @@ -2672,6 +2683,10 @@ void environ_push(struct environ *); void printflike(2, 3) environ_log(struct environ *, const char *, ...); struct environ *environ_for_session(struct session *, int); +/* tty-draw.c */ +void tty_draw_line(struct tty *, struct screen *, u_int, u_int, u_int, + u_int, u_int, const struct tty_style_ctx *); + /* tty.c */ void tty_create_log(void); int tty_window_bigger(struct tty *); @@ -2680,8 +2695,7 @@ void tty_update_window_offset(struct window *); void tty_update_client_offset(struct client *); void tty_raw(struct tty *, const char *); void tty_attributes(struct tty *, const struct grid_cell *, - const struct grid_cell *, struct colour_palette *, - struct hyperlinks *); + const struct tty_style_ctx *); void tty_reset(struct tty *); void tty_region_off(struct tty *); void tty_margin_off(struct tty *); @@ -2700,8 +2714,7 @@ void tty_puts(struct tty *, const char *); void tty_putc(struct tty *, u_char); void tty_putn(struct tty *, const void *, size_t, u_int); void tty_cell(struct tty *, const struct grid_cell *, - const struct grid_cell *, struct colour_palette *, - struct hyperlinks *); + const struct tty_style_ctx *); int tty_init(struct tty *, struct client *); void tty_resize(struct tty *); void tty_set_size(struct tty *, u_int, u_int, u_int, u_int); @@ -2713,22 +2726,13 @@ void tty_stop_tty(struct tty *); void tty_set_title(struct tty *, const char *); void tty_set_path(struct tty *, const char *); void tty_set_progress_bar(struct tty *, struct progress_bar *); -void tty_default_attributes(struct tty *, const struct grid_cell *, - struct colour_palette *, u_int, struct hyperlinks *); +void tty_default_attributes(struct tty *, u_int, + const struct tty_style_ctx *); void tty_update_mode(struct tty *, int, struct screen *); const struct grid_cell *tty_check_codeset(struct tty *, const struct grid_cell *); struct visible_ranges *tty_check_overlay_range(struct tty *, u_int, u_int, u_int); - -/* tty-draw.c */ -void tty_draw_line(struct tty *, struct screen *, u_int, u_int, u_int, - u_int, u_int, const struct grid_cell *, struct colour_palette *); - -#ifdef ENABLE_SIXEL -void tty_draw_images(struct client *, struct window_pane *, struct screen *); -#endif - void tty_sync_start(struct tty *); void tty_sync_end(struct tty *); int tty_open(struct tty *, char **); @@ -2760,6 +2764,7 @@ void tty_cmd_setselection(struct tty *, const struct tty_ctx *); void tty_cmd_rawstring(struct tty *, const struct tty_ctx *); #ifdef ENABLE_SIXEL void tty_cmd_sixelimage(struct tty *, const struct tty_ctx *); +void tty_draw_images(struct client *, struct window_pane *, struct screen *); #endif void tty_cmd_syncstart(struct tty *, const struct tty_ctx *); void tty_default_colours(struct grid_cell *, struct window_pane *); @@ -3222,6 +3227,7 @@ int grid_cells_look_equal(const struct grid_cell *, const struct grid_cell *); struct grid *grid_create(u_int, u_int, u_int); void grid_destroy(struct grid *); +void grid_free_lines(struct grid *, u_int, u_int); int grid_compare(struct grid *, struct grid *); void grid_collect_history(struct grid *, int); void grid_remove_history(struct grid *, u_int ); @@ -3436,7 +3442,7 @@ struct window *window_find_by_id(u_int); void window_update_activity(struct window *); struct window *window_create(u_int, u_int, u_int, u_int); void window_pane_set_event(struct window_pane *); -void window_pane_block_finish(struct window_pane *); +void window_pane_wait_finish(struct window_pane *); struct window_pane *window_get_active_at(struct window *, u_int, u_int); struct window_pane *window_find_string(struct window *, const char *); int window_has_floating_panes(struct window *); diff --git a/tty-draw.c b/tty-draw.c index b6178754..a2868aa4 100644 --- a/tty-draw.c +++ b/tty-draw.c @@ -22,6 +22,7 @@ #include "tmux.h" +/* Current state when drawing line. */ enum tty_draw_line_state { TTY_DRAW_LINE_FIRST, TTY_DRAW_LINE_FLUSH, @@ -91,8 +92,7 @@ tty_draw_line_clear(struct tty *tty, u_int px, u_int py, u_int nx, /* Draw a line from screen to tty. */ void tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx, - u_int atx, u_int aty, const struct grid_cell *defaults, - struct colour_palette *palette) + u_int atx, u_int aty, const struct tty_style_ctx *style_ctx) { struct grid *gd = s->grid; const struct grid_cell *gcp; @@ -104,6 +104,14 @@ tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx, char buf[1000]; size_t len; enum tty_draw_line_state current_state, next_state; + struct tty_style_ctx default_style_ctx = { 0 }; + + + if (style_ctx == NULL) { + default_style_ctx.defaults = &grid_default_cell; + default_style_ctx.hyperlinks = s->hyperlinks; + style_ctx = &default_style_ctx; + } /* * py is the line in the screen to draw. px is the start x and nx is @@ -130,8 +138,8 @@ tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx, else ex = screen_size_x(s); log_debug("%s: drawing %u-%u,%u (end %u) at %u,%u; defaults: fg=%d, " - "bg=%d", __func__, px, px + nx, py, ex, atx, aty, defaults->fg, - defaults->bg); + "bg=%d", __func__, px, px + nx, py, ex, atx, aty, + style_ctx->defaults->fg, style_ctx->defaults->bg); /* Turn off cursor while redrawing and reset region and margins. */ flags = (tty->flags & TTY_NOCURSOR); @@ -142,8 +150,8 @@ tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx, /* Start with the default cell as the last cell. */ memcpy(&last, &grid_default_cell, sizeof last); - last.bg = defaults->bg; - tty_default_attributes(tty, defaults, palette, 8, s->hyperlinks); + last.bg = style_ctx->defaults->bg; + tty_default_attributes(tty, 8, style_ctx); /* * If there is padding at the start, we must have truncated a wide @@ -164,7 +172,7 @@ tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx, break; } if (i == 0) - bg = defaults->bg; + bg = style_ctx->defaults->bg; else { bg = gc.bg; if (gc.flags & GRID_FLAG_SELECTED) { @@ -173,9 +181,9 @@ tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx, bg = ngc.bg; } } - tty_attributes(tty, &last, defaults, palette, s->hyperlinks); + tty_attributes(tty, &last, style_ctx); log_debug("%s: clearing %u padding cells", __func__, cx); - tty_draw_line_clear(tty, atx, aty, cx, defaults, bg, 0); + tty_draw_line_clear(tty, atx, aty, cx, style_ctx->defaults, bg, 0); if (cx == ex) goto out; atx += cx; @@ -274,15 +282,14 @@ tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx, /* If the state has changed, flush any collected data. */ if (next_state != current_state) { if (current_state == TTY_DRAW_LINE_EMPTY) { - tty_attributes(tty, &last, defaults, palette, - s->hyperlinks); + tty_attributes(tty, &last, style_ctx); tty_draw_line_clear(tty, atx + last_i, aty, - i - last_i, defaults, last.bg, wrapped); + i - last_i, style_ctx->defaults, last.bg, + wrapped); wrapped = 0; } else if (next_state != TTY_DRAW_LINE_SAME && len != 0) { - tty_attributes(tty, &last, defaults, palette, - s->hyperlinks); + tty_attributes(tty, &last, style_ctx); if (atx + i - width != 0 || !wrapped) tty_cursor(tty, atx + i - width, aty); if (~last.attr & GRID_ATTR_CHARSET) @@ -322,4 +329,3 @@ out: tty->flags = (tty->flags & ~TTY_NOCURSOR)|flags; tty_update_mode(tty, tty->mode, s); } - diff --git a/tty.c b/tty.c index abe9aa55..1c515d68 100644 --- a/tty.c +++ b/tty.c @@ -84,6 +84,10 @@ static void tty_write_one(void (*)(struct tty *, const struct tty_ctx *), #define TTY_QUERY_TIMEOUT 5 #define TTY_REQUEST_LIMIT 30 +static struct tty_style_ctx tty_default_style_ctx = { + &grid_default_cell, NULL, NULL +}; + void tty_create_log(void) { @@ -1415,7 +1419,7 @@ tty_draw_pane(struct tty *tty, const struct tty_ctx *ctx, u_int py) if (rr->nx == 0) continue; tty_draw_line(tty, s, rr->px - ctx->xoff, py, rr->nx, - rr->px, ctx->yoff + py, &ctx->defaults, ctx->palette); + rr->px, ctx->yoff + py, &ctx->style_ctx); } return; } @@ -1426,7 +1430,7 @@ tty_draw_pane(struct tty *tty, const struct tty_ctx *ctx, u_int py) if (rr->nx == 0) continue; tty_draw_line(tty, s, i + rr->px - x, py, rr->nx, - rr->px, ry, &ctx->defaults, ctx->palette); + rr->px, ry, &ctx->style_ctx); } } } @@ -1650,8 +1654,7 @@ tty_cmd_insertcharacter(struct tty *tty, const struct tty_ctx *ctx) return; } - tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg, - ctx->s->hyperlinks); + tty_default_attributes(tty, ctx->bg, &ctx->style_ctx); tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); @@ -1673,8 +1676,7 @@ tty_cmd_deletecharacter(struct tty *tty, const struct tty_ctx *ctx) return; } - tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg, - ctx->s->hyperlinks); + tty_default_attributes(tty, ctx->bg, &ctx->style_ctx); tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); @@ -1684,8 +1686,7 @@ tty_cmd_deletecharacter(struct tty *tty, const struct tty_ctx *ctx) void tty_cmd_clearcharacter(struct tty *tty, const struct tty_ctx *ctx) { - tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg, - ctx->s->hyperlinks); + tty_default_attributes(tty, ctx->bg, &ctx->style_ctx); tty_clear_pane_line(tty, ctx, ctx->ocy, ctx->ocx, ctx->n, ctx->bg); } @@ -1707,8 +1708,7 @@ tty_cmd_insertline(struct tty *tty, const struct tty_ctx *ctx) return; } - tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg, - ctx->s->hyperlinks); + tty_default_attributes(tty, ctx->bg, &ctx->style_ctx); tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); tty_margin_off(tty); @@ -1735,8 +1735,7 @@ tty_cmd_deleteline(struct tty *tty, const struct tty_ctx *ctx) return; } - tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg, - ctx->s->hyperlinks); + tty_default_attributes(tty, ctx->bg, &ctx->style_ctx); tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); tty_margin_off(tty); @@ -1749,8 +1748,7 @@ tty_cmd_deleteline(struct tty *tty, const struct tty_ctx *ctx) void tty_cmd_clearline(struct tty *tty, const struct tty_ctx *ctx) { - tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg, - ctx->s->hyperlinks); + tty_default_attributes(tty, ctx->bg, &ctx->style_ctx); tty_clear_pane_line(tty, ctx, ctx->ocy, 0, ctx->sx, ctx->bg); } @@ -1760,8 +1758,7 @@ tty_cmd_clearendofline(struct tty *tty, const struct tty_ctx *ctx) { u_int nx = ctx->sx - ctx->ocx; - tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg, - ctx->s->hyperlinks); + tty_default_attributes(tty, ctx->bg, &ctx->style_ctx); tty_clear_pane_line(tty, ctx, ctx->ocy, ctx->ocx, nx, ctx->bg); } @@ -1769,8 +1766,7 @@ tty_cmd_clearendofline(struct tty *tty, const struct tty_ctx *ctx) void tty_cmd_clearstartofline(struct tty *tty, const struct tty_ctx *ctx) { - tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg, - ctx->s->hyperlinks); + tty_default_attributes(tty, ctx->bg, &ctx->style_ctx); tty_clear_pane_line(tty, ctx, ctx->ocy, 0, ctx->ocx + 1, ctx->bg); } @@ -1796,8 +1792,7 @@ tty_cmd_reverseindex(struct tty *tty, const struct tty_ctx *ctx) return; } - tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg, - ctx->s->hyperlinks); + tty_default_attributes(tty, ctx->bg, &ctx->style_ctx); tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); tty_margin_pane(tty, ctx); @@ -1828,8 +1823,7 @@ tty_cmd_linefeed(struct tty *tty, const struct tty_ctx *ctx) return; } - tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg, - ctx->s->hyperlinks); + tty_default_attributes(tty, ctx->bg, &ctx->style_ctx); tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); tty_margin_pane(tty, ctx); @@ -1869,8 +1863,7 @@ tty_cmd_scrollup(struct tty *tty, const struct tty_ctx *ctx) return; } - tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg, - ctx->s->hyperlinks); + tty_default_attributes(tty, ctx->bg, &ctx->style_ctx); tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); tty_margin_pane(tty, ctx); @@ -1910,8 +1903,7 @@ tty_cmd_scrolldown(struct tty *tty, const struct tty_ctx *ctx) return; } - tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg, - ctx->s->hyperlinks); + tty_default_attributes(tty, ctx->bg, &ctx->style_ctx); tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); tty_margin_pane(tty, ctx); @@ -1930,8 +1922,7 @@ tty_cmd_clearendofscreen(struct tty *tty, const struct tty_ctx *ctx) { u_int px, py, nx, ny; - tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg, - ctx->s->hyperlinks); + tty_default_attributes(tty, ctx->bg, &ctx->style_ctx); tty_region_pane(tty, ctx, 0, ctx->sy - 1); tty_margin_off(tty); @@ -1955,8 +1946,7 @@ tty_cmd_clearstartofscreen(struct tty *tty, const struct tty_ctx *ctx) { u_int px, py, nx, ny; - tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg, - ctx->s->hyperlinks); + tty_default_attributes(tty, ctx->bg, &ctx->style_ctx); tty_region_pane(tty, ctx, 0, ctx->sy - 1); tty_margin_off(tty); @@ -1980,8 +1970,7 @@ tty_cmd_clearscreen(struct tty *tty, const struct tty_ctx *ctx) { u_int px, py, nx, ny; - tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg, - ctx->s->hyperlinks); + tty_default_attributes(tty, ctx->bg, &ctx->style_ctx); tty_region_pane(tty, ctx, 0, ctx->sy - 1); tty_margin_off(tty); @@ -2006,8 +1995,7 @@ tty_cmd_alignmenttest(struct tty *tty, const struct tty_ctx *ctx) return; } - tty_attributes(tty, &grid_default_cell, &ctx->defaults, ctx->palette, - ctx->s->hyperlinks); + tty_attributes(tty, &grid_default_cell, &ctx->style_ctx); tty_region_pane(tty, ctx, 0, ctx->sy - 1); tty_margin_off(tty); @@ -2040,7 +2028,7 @@ tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx) vis += r->ranges[i].nx; if (vis < gcp->data.width) { tty_draw_line(tty, s, s->cx, s->cy, gcp->data.width, - px, py, &ctx->defaults, ctx->palette); + px, py, &ctx->style_ctx); return; } } @@ -2055,8 +2043,7 @@ tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx) tty_invalidate(tty); tty_cursor_pane_unless_wrap(tty, ctx, ctx->ocx, ctx->ocy); - tty_cell(tty, ctx->cell, &ctx->defaults, ctx->palette, - ctx->s->hyperlinks); + tty_cell(tty, ctx->cell, &ctx->style_ctx); if (ctx->flags & TTY_CTX_CELL_INVALIDATE) tty_invalidate(tty); @@ -2092,8 +2079,7 @@ tty_cmd_cells(struct tty *tty, const struct tty_ctx *ctx) tty_margin_off(tty); tty_cursor_pane_unless_wrap(tty, ctx, ctx->ocx, ctx->ocy); - tty_attributes(tty, ctx->cell, &ctx->defaults, ctx->palette, - ctx->s->hyperlinks); + tty_attributes(tty, ctx->cell, &ctx->style_ctx); /* Get tty position from pane position for overlay check. */ px = ctx->xoff + ctx->ocx - ctx->wox; @@ -2361,8 +2347,7 @@ tty_cmd_syncstart(struct tty *tty, const struct tty_ctx *ctx) void tty_cell(struct tty *tty, const struct grid_cell *gc, - const struct grid_cell *defaults, struct colour_palette *palette, - struct hyperlinks *hl) + const struct tty_style_ctx *style_ctx) { const struct grid_cell *gcp; @@ -2382,7 +2367,7 @@ tty_cell(struct tty *tty, const struct grid_cell *gc, /* Check the output codeset and apply attributes. */ gcp = tty_check_codeset(tty, gc); - tty_attributes(tty, gcp, defaults, palette, hl); + tty_attributes(tty, gcp, style_ctx); /* If it is a single character, write with putc to handle ACS. */ if (gcp->data.size == 1) { @@ -2727,19 +2712,22 @@ tty_hyperlink(struct tty *tty, const struct grid_cell *gc, void tty_attributes(struct tty *tty, const struct grid_cell *gc, - const struct grid_cell *defaults, struct colour_palette *palette, - struct hyperlinks *hl) + const struct tty_style_ctx *style_ctx) { struct grid_cell *tc = &tty->cell, gc2; int changed; + /* Use default style if not given. */ + if (style_ctx == NULL) + style_ctx = &tty_default_style_ctx; + /* Copy cell and update default colours. */ memcpy(&gc2, gc, sizeof gc2); if (~gc->flags & GRID_FLAG_NOPALETTE) { if (gc2.fg == 8) - gc2.fg = defaults->fg; + gc2.fg = style_ctx->defaults->fg; if (gc2.bg == 8) - gc2.bg = defaults->bg; + gc2.bg = style_ctx->defaults->bg; } /* Ignore cell if it is the same as the last one. */ @@ -2766,9 +2754,9 @@ tty_attributes(struct tty *tty, const struct grid_cell *gc, } /* Fix up the colours if necessary. */ - tty_check_fg(tty, palette, &gc2); - tty_check_bg(tty, palette, &gc2); - tty_check_us(tty, palette, &gc2); + tty_check_fg(tty, style_ctx->palette, &gc2); + tty_check_bg(tty, style_ctx->palette, &gc2); + tty_check_us(tty, style_ctx->palette, &gc2); /* * If any bits are being cleared or the underline colour is now default, @@ -2824,7 +2812,7 @@ tty_attributes(struct tty *tty, const struct grid_cell *gc, tty_putcode(tty, TTYC_SMACS); /* Set hyperlink if any. */ - tty_hyperlink(tty, gc, hl); + tty_hyperlink(tty, gc, style_ctx->hyperlinks); memcpy(&tty->last_cell, &gc2, sizeof tty->last_cell); } @@ -2846,8 +2834,9 @@ tty_colours(struct tty *tty, const struct grid_cell *gc) */ if (COLOUR_DEFAULT(gc->fg) || COLOUR_DEFAULT(gc->bg)) { /* - * If don't have AX, send sgr0. This resets both colours to default. - * Otherwise, try to set the default colour only as needed. + * If don't have AX, send sgr0. This resets both colours to + * default. Otherwise, try to set the default colour only as + * needed. */ if (!tty_term_flag(tty->term, TTYC_AX)) tty_reset(tty); @@ -3217,14 +3206,14 @@ tty_default_colours(struct grid_cell *gc, struct window_pane *wp) } void -tty_default_attributes(struct tty *tty, const struct grid_cell *defaults, - struct colour_palette *palette, u_int bg, struct hyperlinks *hl) +tty_default_attributes(struct tty *tty, u_int bg, + const struct tty_style_ctx *style_ctx) { struct grid_cell gc; memcpy(&gc, &grid_default_cell, sizeof gc); gc.bg = bg; - tty_attributes(tty, &gc, defaults, palette, hl); + tty_attributes(tty, &gc, style_ctx); } static void diff --git a/window-copy.c b/window-copy.c index 39ae1881..558bd76c 100644 --- a/window-copy.c +++ b/window-copy.c @@ -51,6 +51,11 @@ static void window_copy_redraw_selection(struct window_mode_entry *, u_int); static void window_copy_redraw_lines(struct window_mode_entry *, u_int, u_int); static void window_copy_redraw_screen(struct window_mode_entry *); +static void window_copy_do_refresh(struct window_mode_entry *, int); +static void window_copy_refresh_timer(int, short, void *); +static void window_copy_refresh_arm(struct window_mode_entry *); +static void window_copy_refresh_start(struct window_mode_entry *); +static void window_copy_refresh_stop(struct window_mode_entry *); static void window_copy_style_changed(struct window_mode_entry *); static int window_copy_line_number_mode(struct window_mode_entry *); static int window_copy_line_number_is_absolute(struct window_mode_entry *); @@ -255,6 +260,10 @@ struct window_copy_mode_data { int backing_written; /* backing display started */ struct input_ctx *ictx; + u_int sync_added; /* snapshot of backing grid counters */ + u_int sync_collected; + u_int sync_generation; + int viewmode; /* view mode entered */ u_int oy; /* number of lines scrolled up */ @@ -338,6 +347,10 @@ struct window_copy_mode_data { struct event dragtimer; #define WINDOW_COPY_DRAG_REPEAT_TIME 50000 + + struct event refresh_timer; +#define WINDOW_COPY_REFRESH_INTERVAL 50000 + int refresh_active; }; static void @@ -424,6 +437,116 @@ window_copy_clone_screen(struct screen *src, struct screen *hint, u_int *cx, return (dst); } +/* + * Snapshot the source grid's monotonic scroll counters so the next incremental + * sync can tell how much history was added or collected since this point. + */ +static void +window_copy_sync_snapshot(struct window_copy_mode_data *data, struct grid *src) +{ + data->sync_added = src->scroll_added; + data->sync_collected = src->scroll_collected; + data->sync_generation = src->scroll_generation; +} + +/* + * Reconcile the backing screen with the live pane grid in place, copying only + * the history that scrolled in or was collected since the last snapshot rather + * than cloning the whole scrollback. The result is identical to a fresh + * window_copy_clone_screen, so the caller repositions and redraws the same way + * for both paths. Returns 1 on success, or 0 if the caller must fall back to a + * full clone (different source pane, geometry or generation change, or counter + * deltas that do not add up). + */ +static int +window_copy_sync_backing(struct window_mode_entry *wme) +{ + struct window_copy_mode_data *data = wme->data; + struct window_pane *wp = wme->swp; + struct screen *src = &wp->base; + struct screen *dst = data->backing; + struct grid *sg = src->grid; + struct grid *dg = dst->grid; + u_int sy = sg->sy; + u_int old_hsize = dg->hsize; + u_int new_hsize = sg->hsize; + u_int added, collected, kept; + + /* + * Only a pane's own live grid is tracked incrementally. A different + * source pane (copy-mode -s) goes through clone_screen, which also + * trims trailing blank lines that this path does not. + */ + if (data->viewmode || wme->swp != wme->wp) + return (0); + + /* Indices only line up at the same size and generation. */ + if (sg->sx != dg->sx || sg->sy != dg->sy || + sg->scroll_generation != data->sync_generation) + return (0); + + added = sg->scroll_added - data->sync_added; + collected = sg->scroll_collected - data->sync_collected; + + /* + * Reject anything that does not balance: counter wrap, a history-limit + * change that collected past the snapshot, or arithmetic that does not + * reproduce the new history size. + */ + if (added > (u_int)INT_MAX || collected > (u_int)INT_MAX || + collected > old_hsize || old_hsize + added < collected || + old_hsize + added - collected != new_hsize) + return (0); + + kept = old_hsize - collected; + + if (added == 0 && collected == 0) { + /* History is unchanged; only the viewport can have mutated. */ + grid_duplicate_lines(dg, dg->hsize, sg, sg->hsize, sy); + } else { + /* Drop the oldest lines and shift the rest down. */ + if (collected > 0) { + grid_free_lines(dg, 0, collected); + memmove(&dg->linedata[0], &dg->linedata[collected], + (old_hsize + sy - collected) * sizeof *dg->linedata); + memset(&dg->linedata[old_hsize + sy - collected], 0, + collected * sizeof *dg->linedata); + } + + /* Resize linedata to the new history plus viewport. */ + if (new_hsize + sy != old_hsize + sy - collected) { + dg->linedata = xreallocarray(dg->linedata, + new_hsize + sy, sizeof *dg->linedata); + memset(&dg->linedata[old_hsize + sy - collected], 0, + (new_hsize - kept) * sizeof *dg->linedata); + } + + /* + * Set hsize before copying so grid_duplicate_lines does not + * clamp the count to the old, smaller grid size. + */ + dg->hsize = new_hsize; + + /* Copy the newly scrolled history, then refresh the viewport. */ + if (added > 0) + grid_duplicate_lines(dg, kept, sg, kept, added); + grid_duplicate_lines(dg, new_hsize, sg, new_hsize, sy); + } + + dg->hscrolled = sg->hscrolled; + + /* Match clone_screen's backing cursor placement. */ + if (src->cy > dg->sy - 1) { + dst->cx = 0; + dst->cy = dg->sy - 1; + } else { + dst->cx = src->cx; + dst->cy = src->cy; + } + + return (1); +} + static struct window_copy_mode_data * window_copy_common_init(struct window_mode_entry *wme) { @@ -458,6 +581,7 @@ window_copy_common_init(struct window_mode_entry *wme) data->modekeys = options_get_number(wp->window->options, "mode-keys"); evtimer_set(&data->dragtimer, window_copy_scroll_timer, wme); + evtimer_set(&data->refresh_timer, window_copy_refresh_timer, wme); return (data); } @@ -475,6 +599,7 @@ window_copy_init(struct window_mode_entry *wme, data = window_copy_common_init(wme); data->backing = window_copy_clone_screen(base, &data->screen, &cx, &cy, wme->swp != wme->wp); + window_copy_sync_snapshot(data, base->grid); data->cx = cx; if (cy < screen_hsize(data->backing)) { @@ -541,6 +666,7 @@ window_copy_free(struct window_mode_entry *wme) struct window_copy_mode_data *data = wme->data; evtimer_del(&data->dragtimer); + evtimer_del(&data->refresh_timer); free(data->searchmark); free(data->searchstr); @@ -567,13 +693,8 @@ window_copy_add(struct window_pane *wp, int parse, const char *fmt, ...) static void window_copy_init_ctx_cb(__unused struct screen_write_ctx *ctx, - struct tty_ctx *ttyctx) + __unused struct tty_ctx *ttyctx) { - memcpy(&ttyctx->defaults, &grid_default_cell, sizeof ttyctx->defaults); - ttyctx->palette = NULL; - ttyctx->redraw_cb = NULL; - ttyctx->set_client_cb = NULL; - ttyctx->arg = NULL; } void @@ -2786,34 +2907,136 @@ window_copy_cmd_search_forward_incremental(struct window_copy_cmd_state *cs) return (action); } -static enum window_copy_cmd_action -window_copy_cmd_refresh_from_pane(struct window_copy_cmd_state *cs) +/* + * Reconcile the backing screen with the live pane, incrementally if possible + * and otherwise by recloning, then reposition the view. When following, jump + * to the bottom so new output stays visible; otherwise keep the same lines on + * screen. Driven by the automatic refresh timer. + */ +static void +window_copy_do_refresh(struct window_mode_entry *wme, int follow) { - struct window_mode_entry *wme = cs->wme; struct window_pane *wp = wme->swp; struct window_copy_mode_data *data = wme->data; u_int oy_from_top; - if (data->viewmode) - return (WINDOW_COPY_CMD_NOTHING); if (data->oy > screen_hsize(data->backing)) data->oy = screen_hsize(data->backing); oy_from_top = screen_hsize(data->backing) - data->oy; - screen_free(data->backing); - free(data->backing); - data->backing = window_copy_clone_screen(&wp->base, &data->screen, NULL, - NULL, wme->swp != wme->wp); + if (!window_copy_sync_backing(wme)) { + screen_free(data->backing); + free(data->backing); + data->backing = window_copy_clone_screen(&wp->base, + &data->screen, NULL, NULL, wme->swp != wme->wp); + } - if (oy_from_top <= screen_hsize(data->backing)) + if (follow) { + data->cy = screen_size_y(&data->screen) - 1; + data->cx = window_copy_cursor_limit(wme, + screen_hsize(data->backing) + data->cy, 0); + data->oy = 0; + } else if (oy_from_top <= screen_hsize(data->backing)) data->oy = screen_hsize(data->backing) - oy_from_top; else { data->cy = 0; data->oy = screen_hsize(data->backing); } + window_copy_sync_snapshot(data, wp->base.grid); window_copy_size_changed(wme); - return (WINDOW_COPY_CMD_REDRAW); +} + +static void +window_copy_refresh_arm(struct window_mode_entry *wme) +{ + struct window_copy_mode_data *data = wme->data; + struct timeval tv = { + .tv_sec = WINDOW_COPY_REFRESH_INTERVAL / 1000000, + .tv_usec = WINDOW_COPY_REFRESH_INTERVAL % 1000000 + }; + + if (data->refresh_active) + evtimer_add(&data->refresh_timer, &tv); +} + +static void +window_copy_refresh_timer(__unused int fd, __unused short events, void *arg) +{ + struct window_mode_entry *wme = arg; + struct window_pane *wp = wme->wp; + struct window_copy_mode_data *data = wme->data; + int follow; + + if (TAILQ_FIRST(&wp->modes) != wme || !data->refresh_active) + return; + + /* + * Skip the refresh while a selection is being made, otherwise it would + * move; only follow new output if the cursor is still at the bottom. + */ + if ((wp->flags & PANE_UNSEENCHANGES) && data->screen.sel == NULL && + data->cursordrag == CURSORDRAG_NONE) { + follow = (data->oy == 0 && + data->cy == screen_size_y(&data->screen) - 1); + window_copy_do_refresh(wme, follow); + window_copy_redraw_screen(wme); + /* The timer runs outside key handling, so force a repaint. */ + wp->flags |= PANE_REDRAW; + wp->flags &= ~PANE_UNSEENCHANGES; + } + + window_copy_refresh_arm(wme); +} + +static void +window_copy_refresh_start(struct window_mode_entry *wme) +{ + struct window_copy_mode_data *data = wme->data; + + /* + * Do not refresh a view of another pane (copy-mode -s): the source may + * disappear and changes are not tracked on this pane. + */ + if (data->viewmode || wme->swp != wme->wp || data->refresh_active) + return; + data->refresh_active = 1; + window_copy_refresh_arm(wme); +} + +static void +window_copy_refresh_stop(struct window_mode_entry *wme) +{ + struct window_copy_mode_data *data = wme->data; + + data->refresh_active = 0; + evtimer_del(&data->refresh_timer); +} + +static enum window_copy_cmd_action +window_copy_cmd_refresh_on(struct window_copy_cmd_state *cs) +{ + window_copy_refresh_start(cs->wme); + return (WINDOW_COPY_CMD_NOTHING); +} + +static enum window_copy_cmd_action +window_copy_cmd_refresh_off(struct window_copy_cmd_state *cs) +{ + window_copy_refresh_stop(cs->wme); + return (WINDOW_COPY_CMD_NOTHING); +} + +static enum window_copy_cmd_action +window_copy_cmd_refresh_toggle(struct window_copy_cmd_state *cs) +{ + struct window_copy_mode_data *data = cs->wme->data; + + if (data->refresh_active) + window_copy_refresh_stop(cs->wme); + else + window_copy_refresh_start(cs->wme); + return (WINDOW_COPY_CMD_NOTHING); } static enum window_copy_cmd_action @@ -3280,11 +3503,23 @@ static const struct { .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, .f = window_copy_cmd_rectangle_toggle }, - { .command = "refresh-from-pane", + { .command = "refresh-on", .args = { "", 0, 0, NULL }, .flags = WINDOW_COPY_CMD_FLAG_READONLY, - .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, - .f = window_copy_cmd_refresh_from_pane + .clear = WINDOW_COPY_CMD_CLEAR_NEVER, + .f = window_copy_cmd_refresh_on + }, + { .command = "refresh-off", + .args = { "", 0, 0, NULL }, + .flags = WINDOW_COPY_CMD_FLAG_READONLY, + .clear = WINDOW_COPY_CMD_CLEAR_NEVER, + .f = window_copy_cmd_refresh_off + }, + { .command = "refresh-toggle", + .args = { "", 0, 0, NULL }, + .flags = WINDOW_COPY_CMD_FLAG_READONLY, + .clear = WINDOW_COPY_CMD_CLEAR_NEVER, + .f = window_copy_cmd_refresh_toggle }, { .command = "scroll-bottom", .args = { "", 0, 0, NULL }, diff --git a/window.c b/window.c index 9b2a8bd9..fa7bdd65 100644 --- a/window.c +++ b/window.c @@ -386,7 +386,7 @@ window_pane_destroy_ready(struct window_pane *wp) * If a command queue item is blocked on this pane, wait for the * child's exit status before destroying it. */ - if (wp->block_item != NULL && (~wp->flags & PANE_STATUSREADY)) + if (wp->wait_item != NULL && (~wp->flags & PANE_STATUSREADY)) return (0); return (1); } @@ -1123,15 +1123,15 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit) } void -window_pane_block_finish(struct window_pane *wp) +window_pane_wait_finish(struct window_pane *wp) { - struct cmdq_item *item = wp->block_item; + struct cmdq_item *item = wp->wait_item; struct client *c; int retval = 0; if (item == NULL) return; - wp->block_item = NULL; + wp->wait_item = NULL; if (wp->flags & PANE_STATUSREADY) { if (WIFEXITED(wp->status)) @@ -1152,7 +1152,7 @@ window_pane_destroy(struct window_pane *wp) struct window_pane_resize *r; struct window_pane_resize *r1; - window_pane_block_finish(wp); + window_pane_wait_finish(wp); window_pane_reset_mode_all(wp); free(wp->searchstr);