diff --git a/input.c b/input.c index 57eb967bb..54826d06c 100644 --- a/input.c +++ b/input.c @@ -95,14 +95,6 @@ struct input_param { }; }; -/* Saved OSC 133 output marker. */ -struct input_osc_133_ctx { - int running; - u_int start; - u_int col; - u_int collected; -}; - /* Input parser context. */ struct input_ctx { struct window_pane *wp; @@ -181,9 +173,6 @@ static void input_osc_110(struct input_ctx *, const char *); static void input_osc_111(struct input_ctx *, const char *); static void input_osc_112(struct input_ctx *, const char *); static void input_osc_133(struct input_ctx *, const char *); -static struct input_osc_133_ctx input_osc_133_save_marker(struct input_ctx *); -static void input_osc_133_restore_marker(struct input_ctx *, - const struct input_osc_133_ctx *); /* Transition entry/exit handlers. */ static void input_clear(struct input_ctx *); @@ -947,6 +936,7 @@ input_reset(struct input_ctx *ictx, int clear) screen_write_start_pane(sctx, wp, &wp->base); else screen_write_start(sctx, &wp->base); + sctx->owner = wp; screen_write_reset(sctx); screen_write_stop(sctx); } @@ -1075,6 +1065,7 @@ input_parse_buffer(struct window_pane *wp, const u_char *buf, size_t len) screen_write_start_pane(sctx, wp, &wp->base); else screen_write_start(sctx, &wp->base); + sctx->owner = wp; log_debug("%s: %%%u %s, %zu bytes: %.*s", __func__, wp->id, ictx->state->name, len, (int)len, buf); @@ -1094,6 +1085,7 @@ input_parse_screen(struct input_ctx *ictx, struct screen *s, return; screen_write_start_callback(sctx, s, cb, arg); + sctx->owner = ictx->wp; input_parse(ictx, buf, len); screen_write_stop(sctx); } @@ -1477,7 +1469,6 @@ input_csi_dispatch(struct input_ctx *ictx) struct screen *s = sctx->s; const struct input_table_entry *entry; struct options *oo; - struct input_osc_133_ctx osc133 = {0}; int i, n, m, ek, set, p; u_int cx, bg = ictx->cell.cell.bg; @@ -1751,19 +1742,13 @@ input_csi_dispatch(struct input_ctx *ictx) case -1: break; case 0: - /* Erasing to the end clears the whole screen at 0,0. */ - if (s->cx == 0 && s->cy == 0) - osc133 = input_osc_133_save_marker(ictx); screen_write_clearendofscreen(sctx, bg); - input_osc_133_restore_marker(ictx, &osc133); break; case 1: screen_write_clearstartofscreen(sctx, bg); break; case 2: - osc133 = input_osc_133_save_marker(ictx); screen_write_clearscreen(sctx, bg); - input_osc_133_restore_marker(ictx, &osc133); break; case 3: if (input_get(ictx, 1, 0, 0) == 0) { @@ -3310,63 +3295,6 @@ input_osc_133_mark_prompt(struct grid_line *gl, u_int col, const char *p) gl->flags |= GRID_LINE_START_PROMPT; } -/* Save the running command's output start before clearing the screen. */ -static struct input_osc_133_ctx -input_osc_133_save_marker(struct input_ctx *ictx) -{ - struct window_pane *wp = ictx->wp; - struct screen *s = ictx->ctx.s; - struct grid *gd = s->grid; - struct grid_line *gl; - struct input_osc_133_ctx ctx = {0}; - u_int y; - - if (wp == NULL || s != &wp->base || SCREEN_IS_ALTERNATE(s) || - (~wp->flags & PANE_CMDRUNNING)) - return (ctx); - ctx.running = 1; - ctx.start = UINT_MAX; - ctx.collected = gd->scroll_collected; - for (y = gd->hsize + gd->sy; y > 0; y--) { - gl = grid_get_line(gd, y - 1); - if ((gl->flags & GRID_LINE_START_OUTPUT) && - (~gl->flags & GRID_LINE_END_OUTPUT || - gl->osc133_data.out_end_col < gl->osc133_data.out_start_col) && - (~gl->flags & GRID_LINE_START_PROMPT || - gl->osc133_data.prompt_col <= gl->osc133_data.out_start_col)) { - ctx.start = y - 1; - ctx.col = gl->osc133_data.out_start_col; - break; - } - if (gl->flags & (GRID_LINE_START_PROMPT|GRID_LINE_END_OUTPUT)) - break; - } - return (ctx); -} - -/* Restore the running command's output start if a screen clear removed it. */ -static void -input_osc_133_restore_marker(struct input_ctx *ictx, - const struct input_osc_133_ctx *ctx) -{ - struct grid *gd = ictx->ctx.s->grid; - struct grid_line *gl; - u_int collected; - - if (!ctx->running) - return; - collected = gd->scroll_collected - ctx->collected; - if (ctx->start != UINT_MAX && ctx->start >= collected) { - gl = grid_get_line(gd, ctx->start - collected); - if ((gl->flags & GRID_LINE_START_OUTPUT) && - gl->osc133_data.out_start_col == ctx->col) - return; - } - gl = grid_get_line(gd, gd->hsize); - gl->flags |= GRID_LINE_START_OUTPUT; - gl->osc133_data.out_start_col = 0; -} - /* Handle the OSC 133 sequence. */ static void input_osc_133(struct input_ctx *ictx, const char *p) diff --git a/regress/output-commands.sh b/regress/output-commands.sh index ab305eed2..4676e8ea3 100755 --- a/regress/output-commands.sh +++ b/regress/output-commands.sh @@ -202,6 +202,25 @@ for integration in plain marked; do done done +# Preserve C when the clear happens while copy mode suppresses tty updates. +$TMUX set-option -g scroll-on-clear off || exit 1 +$TMUX new-window -d -n modeclear "seq 1 40; printf '\\033]133;A\\007p\\$ \\033]133;B\\007clear; echo hello; echo world\\n\\033]133;C\\007before\\n'; $TMUX wait-for -S modeclear-ready; $TMUX wait-for modeclear-go; printf '\\033[H\\033[2Jhello\\nworld\\n\\033]133;D;0\\007\\033]133;A\\007p\\$ \\033]133;B\\007'; $TMUX wait-for -S modeclear-done; exec sleep 100" || exit 1 +$TMUX wait-for modeclear-ready || exit 1 +$TMUX copy-mode -t :modeclear || exit 1 +$TMUX wait-for -S modeclear-go || exit 1 +$TMUX wait-for modeclear-done || exit 1 +sleep 1 +[ "$($TMUX display-message -p -t :modeclear.0 '#{pane_in_mode}')" = 1 ] || exit 1 +$TMUX send-keys -t :modeclear.0 -X cancel || exit 1 +$TMUX copy-mode -t :modeclear || exit 1 +$TMUX send-keys -t :modeclear.0 -X cursor-up || exit 1 +$TMUX send-keys -t :modeclear.0 C-o || exit 1 +[ "$($TMUX display-message -p -t :modeclear.0 '#{selection_present}')" = 1 ] || exit 1 +$TMUX set-buffer sentinel || exit 1 +$TMUX send-keys -t :modeclear.0 -X copy-selection || exit 1 +[ "$($TMUX show-buffer)" = "$(printf 'hello\nworld')" ] || exit 1 +$TMUX send-keys -t :modeclear.0 -X cancel || exit 1 + # Preserve C when scroll-on-clear keeps the command's earlier output. $TMUX set-option -g scroll-on-clear on || exit 1 $TMUX new-window -d -n preserve "printf '\\033]133;A\\007p\\$ \\033]133;B\\007echo before; clear; echo hello; echo world\\n\\033]133;C\\007before\\n\\033[H\\033[2Jhello\\nworld\\n\\033]133;D;0\\007\\033]133;A\\007p\\$ \\033]133;B\\007'; exec sleep 100" || exit 1 diff --git a/screen-write.c b/screen-write.c index 4eb2427f7..3511ccd2e 100644 --- a/screen-write.c +++ b/screen-write.c @@ -401,6 +401,7 @@ screen_write_start_pane(struct screen_write_ctx *ctx, struct window_pane *wp, s = wp->screen; screen_write_init(ctx, s); ctx->wp = wp; + ctx->owner = wp; if (log_get_level() != 0) { log_debug("%s: size %ux%u, pane %%%u (at %u,%u)", @@ -2061,6 +2062,71 @@ screen_write_carriagereturn(struct screen_write_ctx *ctx) screen_write_set_cursor(ctx, 0, -1); } +/* Saved OSC 133 output marker. */ +struct screen_write_osc_133_ctx { + int running; + u_int start; + u_int col; + u_int collected; +}; + +/* Save the running command's output start before clearing the screen. */ +static struct screen_write_osc_133_ctx +screen_write_osc_133_save_marker(struct screen_write_ctx *sctx) +{ + struct window_pane *wp = sctx->owner; + struct screen *s = sctx->s; + struct grid *gd = s->grid; + struct grid_line *gl; + struct screen_write_osc_133_ctx ctx = {0}; + u_int y; + + if (wp == NULL || s != &wp->base || SCREEN_IS_ALTERNATE(s) || + (~wp->flags & PANE_CMDRUNNING)) + return (ctx); + ctx.running = 1; + ctx.start = UINT_MAX; + ctx.collected = gd->scroll_collected; + for (y = gd->hsize + gd->sy; y > 0; y--) { + gl = grid_get_line(gd, y - 1); + if ((gl->flags & GRID_LINE_START_OUTPUT) && + (~gl->flags & GRID_LINE_END_OUTPUT || + gl->osc133_data.out_end_col < gl->osc133_data.out_start_col) && + (~gl->flags & GRID_LINE_START_PROMPT || + gl->osc133_data.prompt_col <= gl->osc133_data.out_start_col)) { + ctx.start = y - 1; + ctx.col = gl->osc133_data.out_start_col; + break; + } + if (gl->flags & (GRID_LINE_START_PROMPT|GRID_LINE_END_OUTPUT)) + break; + } + return (ctx); +} + +/* Restore the running command's output start if a screen clear removed it. */ +static void +screen_write_osc_133_restore_marker(struct screen_write_ctx *sctx, + const struct screen_write_osc_133_ctx *ctx) +{ + struct grid *gd = sctx->s->grid; + struct grid_line *gl; + u_int collected; + + if (!ctx->running) + return; + collected = gd->scroll_collected - ctx->collected; + if (ctx->start != UINT_MAX && ctx->start >= collected) { + gl = grid_get_line(gd, ctx->start - collected); + if ((gl->flags & GRID_LINE_START_OUTPUT) && + gl->osc133_data.out_start_col == ctx->col) + return; + } + gl = grid_get_line(gd, gd->hsize); + gl->flags |= GRID_LINE_START_OUTPUT; + gl->osc133_data.out_start_col = 0; +} + /* Clear to end of screen from cursor. */ void screen_write_clearendofscreen(struct screen_write_ctx *ctx, u_int bg) @@ -2072,6 +2138,7 @@ screen_write_clearendofscreen(struct screen_write_ctx *ctx, u_int bg) u_int y, i, xoff, yoff, ocx, ocy; struct visible_ranges *r; struct visible_range *ri; + struct screen_write_osc_133_ctx osc133 = {0}; #ifdef ENABLE_SIXEL if (image_check_line(s, s->cy, sy - s->cy) && ctx->wp != NULL) @@ -2081,6 +2148,10 @@ screen_write_clearendofscreen(struct screen_write_ctx *ctx, u_int bg) screen_write_initctx(ctx, &ttyctx, 1, 1); ttyctx.bg = bg; + /* Erasing to the end clears the whole screen at 0,0. */ + if (s->cx == 0 && s->cy == 0) + osc133 = screen_write_osc_133_save_marker(ctx); + /* Scroll into history if it is enabled and clearing entire screen. */ if (s->cx == 0 && s->cy == 0 && @@ -2093,6 +2164,7 @@ screen_write_clearendofscreen(struct screen_write_ctx *ctx, u_int bg) grid_view_clear(gd, s->cx, s->cy, sx - s->cx, 1, bg); grid_view_clear(gd, 0, s->cy + 1, sx, sy - (s->cy + 1), bg); } + screen_write_osc_133_restore_marker(ctx, &osc133); screen_write_collect_clear(ctx, s->cy + 1, sy - (s->cy + 1)); screen_write_collect_flush(ctx, 0, __func__); @@ -2226,6 +2298,7 @@ screen_write_clearscreen(struct screen_write_ctx *ctx, u_int bg) u_int y, i, xoff, yoff, ocx, ocy; struct visible_ranges *r; struct visible_range *ri; + struct screen_write_osc_133_ctx osc133; #ifdef ENABLE_SIXEL if (image_free_all(s) && ctx->wp != NULL) @@ -2235,6 +2308,8 @@ screen_write_clearscreen(struct screen_write_ctx *ctx, u_int bg) screen_write_initctx(ctx, &ttyctx, 1, 1); ttyctx.bg = bg; + osc133 = screen_write_osc_133_save_marker(ctx); + /* Scroll into history if it is enabled. */ if ((s->grid->flags & GRID_HISTORY) && ctx->wp != NULL && @@ -2242,6 +2317,7 @@ screen_write_clearscreen(struct screen_write_ctx *ctx, u_int bg) grid_view_clear_history(s->grid, bg); else grid_view_clear(s->grid, 0, 0, sx, sy, bg); + screen_write_osc_133_restore_marker(ctx, &osc133); screen_write_collect_clear(ctx, 0, sy); diff --git a/tmux.h b/tmux.h index fe9997eed..607fcfa55 100644 --- a/tmux.h +++ b/tmux.h @@ -1120,6 +1120,7 @@ typedef void (*screen_write_init_ctx_cb)(struct screen_write_ctx *, struct tty_ctx *); struct screen_write_ctx { struct window_pane *wp; + struct window_pane *owner; /* pane for screen metadata */ struct screen *s; int flags;