From c103138040c9d3ce5da059572cf8f446f145c07a Mon Sep 17 00:00:00 2001 From: Michael Grant Date: Tue, 6 Oct 2026 12:28:42 +0100 Subject: [PATCH] Fix OSC 133 output ranges after resize and cursor movement. --- grid.c | 82 +++++++++++++++++++++++++++++++++++++++++++++++---- tmux.1 | 3 +- window-copy.c | 9 ++++-- 3 files changed, 85 insertions(+), 9 deletions(-) diff --git a/grid.c b/grid.c index 424f5bea3..be47aef15 100644 --- a/grid.c +++ b/grid.c @@ -1347,6 +1347,53 @@ grid_reflow_move(struct grid *gd, struct grid_line *from) return (to); } +/* A portion of a line being moved during reflow. */ +struct grid_reflow_range { + u_int offset; + u_int count; + int last; +}; + +/* Move OSC 133 markers and adjust those left on the source line. */ +static void +grid_reflow_move_osc133(struct grid_line *to, struct grid_line *from, + const struct grid_reflow_range *range) +{ + struct osc133_data *src = &from->osc133_data; + struct osc133_data *dst = &to->osc133_data; + u_short *src_col[] = { &src->prompt_col, &src->cmd_col, + &src->out_start_col, &src->out_end_col }; + u_short *dst_col[] = { &dst->prompt_col, &dst->cmd_col, + &dst->out_start_col, &dst->out_end_col }; + u_int flags[] = { + GRID_LINE_START_PROMPT|GRID_LINE_SECOND_PROMPT, + GRID_LINE_START_COMMAND, + GRID_LINE_START_OUTPUT, + GRID_LINE_END_OUTPUT + }; + u_int i, flag; + + if ((from->flags & GRID_LINE_OSC133_FLAGS) == 0) + return; + for (i = 0; i < nitems(flags); i++) { + flag = from->flags & flags[i]; + if (flag == 0) + continue; + if (!range->last && *src_col[i] >= range->count) { + *src_col[i] -= range->count; + continue; + } + *dst_col[i] = range->offset + *src_col[i]; + to->flags |= flag; + from->flags &= ~flag; + *src_col[i] = 0; + if (flag == GRID_LINE_END_OUTPUT) { + dst->exit_status = src->exit_status; + src->exit_status = 0; + } + } +} + /* Join line below onto this one. */ static void grid_reflow_join(struct grid *target, struct grid *gd, u_int sx, u_int yy, @@ -1354,6 +1401,7 @@ grid_reflow_join(struct grid *target, struct grid *gd, u_int sx, u_int yy, { struct grid_line *gl, *from = NULL; struct grid_cell gc; + struct grid_reflow_range range; u_int lines, left, i, to, line, want = 0; u_int at; int wrapped = 1; @@ -1383,13 +1431,19 @@ grid_reflow_join(struct grid *target, struct grid *gd, u_int sx, u_int yy, break; line = yy + 1 + lines; - /* If the next line is empty, skip it. */ + /* Consume empty lines, including any markers. */ if (~gd->linedata[line].flags & GRID_LINE_WRAPPED) wrapped = 0; if (gd->linedata[line].cellused == 0) { + from = &gd->linedata[line]; + want = 0; + range.offset = at; + range.count = 0; + range.last = 1; + grid_reflow_move_osc133(gl, from, &range); + lines++; if (!wrapped) break; - lines++; continue; } @@ -1416,6 +1470,10 @@ grid_reflow_join(struct grid *target, struct grid *gd, u_int sx, u_int yy, grid_set_cell(target, at, to, &gc); at++; } + range.offset = at - want; + range.count = want; + range.last = (want == from->cellused); + grid_reflow_move_osc133(gl, from, &range); lines++; /* @@ -1462,6 +1520,7 @@ grid_reflow_split(struct grid *target, struct grid *gd, u_int sx, u_int yy, { struct grid_line *gl = &gd->linedata[yy], *first; struct grid_cell gc; + struct grid_reflow_range range; u_int line, lines, width, i, xx; u_int used = gl->cellused; int flags = gl->flags; @@ -1485,6 +1544,15 @@ grid_reflow_split(struct grid *target, struct grid *gd, u_int sx, u_int yy, /* Insert new lines. */ line = target->sy + 1; first = grid_reflow_add(target, lines); + memcpy(first, gl, sizeof *first); + first->cellsize = first->cellused = at; + first->flags &= ~GRID_LINE_OSC133_FLAGS; + first->flags |= GRID_LINE_WRAPPED; + memset(&first->osc133_data, 0, sizeof first->osc133_data); + range.offset = 0; + range.count = at; + range.last = 0; + grid_reflow_move_osc133(first, gl, &range); /* Copy sections from the original line. */ width = 0; @@ -1493,6 +1561,9 @@ grid_reflow_split(struct grid *target, struct grid *gd, u_int sx, u_int yy, grid_get_cell1(gl, i, &gc); if (width + gc.data.width > sx) { target->linedata[line].flags |= GRID_LINE_WRAPPED; + range.count = xx; + grid_reflow_move_osc133(&target->linedata[line], gl, + &range); line++; width = 0; @@ -1504,11 +1575,10 @@ grid_reflow_split(struct grid *target, struct grid *gd, u_int sx, u_int yy, } if (flags & GRID_LINE_WRAPPED) target->linedata[line].flags |= GRID_LINE_WRAPPED; + range.count = xx; + range.last = 1; + grid_reflow_move_osc133(&target->linedata[line], gl, &range); - /* Move the remainder of the original line. */ - gl->cellsize = gl->cellused = at; - gl->flags |= GRID_LINE_WRAPPED; - memcpy(first, gl, sizeof *first); grid_reflow_dead(gl); /* Adjust the scroll position. */ diff --git a/tmux.1 b/tmux.1 index f4c19226e..cc671e473 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2576,7 +2576,8 @@ marker up to its matching marker. If there is no .Ql D -marker, the output ends at the end of the copy-mode buffer. +marker, the output ends at the last used cell in the copy-mode buffer, +omitting unused blank rows at the bottom. At the following prompt or below it, they use the most recent nonempty output. With .Fl a , diff --git a/window-copy.c b/window-copy.c index f874152ae..19dd0e61f 100644 --- a/window-copy.c +++ b/window-copy.c @@ -6387,8 +6387,13 @@ window_copy_find_previous_output_range(struct window_mode_entry *wme, static void window_copy_output_end(struct screen *s, u_int *x, u_int *y) { - *x = s->cx; - *y = screen_hsize(s) + s->cy; + struct grid *gd = s->grid; + u_int last = gd->hsize + gd->sy - 1; + + while (last > 0 && grid_get_line(gd, last)->cellused == 0) + last--; + *x = grid_get_line(gd, last)->cellused; + *y = last; } static void *