Add support in screen-write.c to properly display cmd output when there are floating panes on the screen.

This commit is contained in:
Michael Grant
2025-10-10 23:59:47 +01:00
parent 7634daa834
commit 5faf41b695
4 changed files with 173 additions and 62 deletions

View File

@@ -262,8 +262,8 @@ cmd_new_floating_window_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }
} }
sc.xoff = 0; sc.xoff = 10;
sc.yoff = 0; sc.yoff = 10;
sc.sx = sx; sc.sx = sx;
sc.sy = sy; sc.sy = sy;

View File

@@ -885,34 +885,41 @@ screen_redraw_draw_status(struct screen_redraw_ctx *ctx)
} }
} }
struct visual_range { /* Construct ranges of line at px,py of width cells of base_wp that are
u_int px; unobsructed. */
u_int nx; void
}; screen_redraw_get_visible_ranges(u_int px, u_int py, u_int width,
struct window_pane *base_wp, struct visible_range **vr_p,
int *vr_count_p) {
struct window_pane *wp;
struct window *w;
struct visible_range *vr;
int found_self, r, s;
int vr_len, vr_count;
static void /* Caller must call free(vr). */
screen_redraw_get_visual_ranges(u_int px, u_int py, u_int width, vr = xcalloc(4, sizeof(struct visible_range *));
struct window_pane *base_wp, struct window *w, vr_len = 4;
struct visual_range **vr_p, int *vr_count_p) { vr[0].px = px;
struct visual_range *vr;
int found_self, r, s;
int vr_len, vr_count;
struct window_pane *wp;
vr = xcalloc(4, sizeof(struct visual_range *));
vr_len = 4; // 4 initial slots
vr[0].px = px; // initialise first slot
vr[0].nx = width; vr[0].nx = width;
vr_count = 1; vr_count = 1;
if (base_wp == NULL) {
*vr_p = vr;
*vr_count_p = vr_count;
return;
}
w = base_wp->window;
TAILQ_FOREACH(wp, &w->panes, entry) { TAILQ_FOREACH(wp, &w->panes, entry) {
if (wp == base_wp) { // wp == current pane if (wp == base_wp) {
found_self = 1; found_self = 1;
continue; continue;
} }
if (found_self && wp->layout_cell == NULL && if (found_self && wp->layout_cell == NULL &&
!(wp->flags & PANE_MINIMISED) && !(wp->flags & PANE_MINIMISED) &&
(wp->yoff >= py && py <= wp->yoff + wp->sy)) { (py >= wp->yoff && py <= wp->yoff + wp->sy)) {
for (r=0; r<vr_count; r++) { for (r=0; r<vr_count; r++) {
/* if the left edge of wp /* if the left edge of wp
falls inside this range and right falls inside this range and right
@@ -920,19 +927,20 @@ screen_redraw_get_visual_ranges(u_int px, u_int py, u_int width,
then shrink nx */ then shrink nx */
if (wp->xoff > vr[r].px && if (wp->xoff > vr[r].px &&
wp->xoff < vr[r].px + vr[r].nx && wp->xoff < vr[r].px + vr[r].nx &&
wp->xoff + wp->sx > vr[r].px + vr[r].nx) wp->xoff + wp->sx > vr[r].px + vr[r].nx) {
vr[r].nx = wp->xoff; vr[r].nx = wp->xoff;
/* else if the right edge of wp /* else if the right edge of wp
falls inside of this range and left falls inside of this range and left
edge is less than the start of the range, edge is less than the start of the range,
then move px forward */ then move px forward */
else if (wp->xoff + wp->sx > vr[r].px && } else if (wp->xoff + wp->sx > vr[r].px &&
wp->xoff + wp->sx < vr[r].px + vr[r].nx && wp->xoff + wp->sx < vr[r].px + vr[r].nx &&
wp->xoff < vr[r].px + vr[r].nx) wp->xoff < vr[r].px) {
vr[r].px = wp->xoff + wp->sx; vr[r].px = vr[r].px + (wp->xoff + wp->sx);
vr[r].nx = vr[r].nx - (wp->xoff + wp->sx);
/* else if wp fully inside range /* else if wp fully inside range
then split range into 2 ranges */ then split range into 2 ranges */
else if (wp->xoff > vr[r].px && } else if (wp->xoff > vr[r].px &&
wp->xoff + wp->sx < vr[r].px + vr[r].nx) { wp->xoff + wp->sx < vr[r].px + vr[r].nx) {
/* make space */ /* make space */
if (vr_len == vr_count) { if (vr_len == vr_count) {
@@ -946,7 +954,8 @@ screen_redraw_get_visual_ranges(u_int px, u_int py, u_int width,
} }
vr[r].nx = wp->xoff; vr[r].nx = wp->xoff;
vr[r+1].px = wp->xoff + wp->sx; vr[r+1].px = wp->xoff + wp->sx;
} vr_count++;
} /* XXX what if it's completely obscured? */
} }
} }
} }
@@ -965,7 +974,7 @@ screen_redraw_draw_pane(struct screen_redraw_ctx *ctx, struct window_pane *wp)
struct screen *s = wp->screen; struct screen *s = wp->screen;
struct colour_palette *palette = &wp->palette; struct colour_palette *palette = &wp->palette;
struct grid_cell defaults; struct grid_cell defaults;
struct visual_range *vr; struct visible_range *vr;
int vr_count, r; int vr_count, r;
u_int i, j, top, x, y, width; u_int i, j, top, x, y, width;
@@ -1009,12 +1018,15 @@ screen_redraw_draw_pane(struct screen_redraw_ctx *ctx, struct window_pane *wp)
log_debug("%s: %s %%%u line %u,%u at %u,%u, width %u", log_debug("%s: %s %%%u line %u,%u at %u,%u, width %u",
__func__, c->name, wp->id, i, j, x, y, width); __func__, c->name, wp->id, i, j, x, y, width);
screen_redraw_get_visual_ranges(x, y, width, wp, w, &vr, &vr_count); /* Get visible ranges of line before we draw it. */
screen_redraw_get_visible_ranges(x, y, width, wp,
&vr, &vr_count);
tty_default_colours(&defaults, wp); tty_default_colours(&defaults, wp);
for (r=0; r<vr_count; r++) for (r=0; r<vr_count; r++)
tty_draw_line(tty, s, i, j, vr[r].nx, vr[r].px, y, &defaults, palette); tty_draw_line(tty, s, i + vr[r].px, j,
vr[r].nx, vr[r].px, y, &defaults, palette);
free(vr); free(vr);
} }

View File

@@ -1745,30 +1745,72 @@ screen_write_collect_clear(struct screen_write_ctx *ctx, u_int y, u_int n)
static void static void
screen_write_collect_scroll(struct screen_write_ctx *ctx, u_int bg) screen_write_collect_scroll(struct screen_write_ctx *ctx, u_int bg)
{ {
struct screen *s = ctx->s; struct screen *s = ctx->s;
struct screen_write_cline *cl; struct screen_write_cline *cl_src, *cl_dst;
u_int y; u_int y, r_start, r_end;
char *saved; u_int ci_start, ci_end, new_end;
struct screen_write_citem *ci; char *saved;
struct screen_write_citem *ci, *new_ci;
struct window_pane *wp = ctx->wp;
struct visible_range *vr;
int vr_count, r;
log_debug("%s: at %u,%u (region %u-%u)", __func__, s->cx, s->cy, log_debug("%s: at %u,%u (region %u-%u)", __func__, s->cx, s->cy,
s->rupper, s->rlower); s->rupper, s->rlower);
screen_write_collect_clear(ctx, s->rupper, 1); screen_write_collect_clear(ctx, s->rupper, 1);
saved = ctx->s->write_list[s->rupper].data; saved = s->write_list[s->rupper].data;
for (y = s->rupper; y < s->rlower; y++) {
cl = &ctx->s->write_list[y + 1];
TAILQ_CONCAT(&ctx->s->write_list[y].items, &cl->items, entry);
ctx->s->write_list[y].data = cl->data;
}
ctx->s->write_list[s->rlower].data = saved;
ci = screen_write_get_citem(); /* For each line being scrolled, copy visible ranges from the line
ci->x = 0; below to the line above. */
ci->used = screen_size_x(s); for (y = s->rupper; y < s->rlower; y++) {
ci->type = CLEAR; cl_src = &s->write_list[y + 1];
ci->bg = bg; cl_dst = &s->write_list[y];
TAILQ_INSERT_TAIL(&ctx->s->write_list[s->rlower].items, ci, entry); vr = NULL;
vr_count = 0;
screen_redraw_get_visible_ranges(0, y, screen_size_x(s), wp,
&vr, &vr_count);
TAILQ_INIT(&cl_dst->items);
if (cl_dst->data == NULL)
cl_dst->data = xmalloc(screen_size_x(s));
/* For each visible range, copy corresponding items from cl_src
to cl_dst. */
for (r = 0; r < vr_count; r++) {
r_start = vr[r].px;
r_end = vr[r].px + vr[r].nx;
TAILQ_FOREACH(ci, &cl_src->items, entry) {
ci_start = ci->x;
ci_end = ci->x + ci->used;
if (ci_end <= r_start || ci_start >= r_end)
continue;
new_ci = screen_write_get_citem();
new_ci->x = (ci_start < r_start) ? r_start :
ci_start;
new_end = (ci_end > r_end) ? r_end : ci_end;
new_ci->used = new_end - new_ci->x;
new_ci->type = ci->type;
new_ci->wrapped = ci->wrapped;
new_ci->bg = bg;
memcpy(&new_ci->gc, &ci->gc, sizeof(ci->gc));
if (ci->type == TEXT)
memcpy(cl_dst->data, cl_src->data,
screen_size_x(s));
TAILQ_INSERT_TAIL(&cl_dst->items, new_ci,
entry);
}
}
free(vr);
}
s->write_list[s->rlower].data = saved;
screen_write_collect_clear(ctx, s->rlower, 1);
} }
/* Flush collected lines. */ /* Flush collected lines. */
@@ -1780,7 +1822,12 @@ screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only,
struct screen_write_citem *ci, *tmp; struct screen_write_citem *ci, *tmp;
struct screen_write_cline *cl; struct screen_write_cline *cl;
u_int y, cx, cy, last, items = 0; u_int y, cx, cy, last, items = 0;
u_int r_start, r_end, ci_start, ci_end;
u_int wr_start, wr_end, wr_length;
struct tty_ctx ttyctx; struct tty_ctx ttyctx;
struct visible_range *vr = NULL;
int vr_count = 0;
struct window_pane *wp = ctx->wp;
if (ctx->scrolled != 0) { if (ctx->scrolled != 0) {
log_debug("%s: scrolled %u (region %u-%u)", __func__, log_debug("%s: scrolled %u (region %u-%u)", __func__,
@@ -1803,34 +1850,57 @@ screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only,
return; return;
cx = s->cx; cy = s->cy; cx = s->cx; cy = s->cy;
for (y = 0; y < screen_size_y(s); y++) { for (y = 0; y < screen_size_y(s); y++) {
cl = &ctx->s->write_list[y]; cl = &ctx->s->write_list[y];
screen_redraw_get_visible_ranges(0, y, screen_size_x(s), wp,
&vr, &vr_count);
last = UINT_MAX; last = UINT_MAX;
TAILQ_FOREACH_SAFE(ci, &cl->items, entry, tmp) { TAILQ_FOREACH_SAFE(ci, &cl->items, entry, tmp) {
if (last != UINT_MAX && ci->x <= last) { if (last != UINT_MAX && ci->x <= last) {
fatalx("collect list not in order: %u <= %u", fatalx("collect list not in order: %u <= %u",
ci->x, last); ci->x, last);
} }
screen_write_set_cursor(ctx, ci->x, y); for (int r = 0; r < vr_count; r++) {
if (ci->type == CLEAR) { r_start = vr[r].px;
screen_write_initctx(ctx, &ttyctx, 1); r_end = vr[r].px + vr[r].nx;
ttyctx.bg = ci->bg; ci_start = ci->x;
ttyctx.num = ci->used; ci_end = ci->x + ci->used;
tty_write(tty_cmd_clearcharacter, &ttyctx);
} else {
screen_write_initctx(ctx, &ttyctx, 0);
ttyctx.cell = &ci->gc;
ttyctx.wrapped = ci->wrapped;
ttyctx.ptr = cl->data + ci->x;
ttyctx.num = ci->used;
tty_write(tty_cmd_cells, &ttyctx);
}
items++;
if (ci_end <= r_start || ci_start >= r_end)
continue;
wr_start = (ci_start < r_start) ? r_start :
ci_start;
wr_end = (ci_end > r_end) ? r_end : ci_end;
wr_length = wr_end - wr_start;
if (wr_length == 0)
continue;
screen_write_set_cursor(ctx, wr_start, y);
if (ci->type == CLEAR) {
screen_write_initctx(ctx, &ttyctx, 1);
ttyctx.bg = ci->bg;
ttyctx.num = wr_length;
tty_write(tty_cmd_clearcharacter,
&ttyctx);
} else {
screen_write_initctx(ctx, &ttyctx, 0);
ttyctx.cell = &ci->gc;
ttyctx.wrapped = ci->wrapped;
ttyctx.ptr = cl->data + wr_start;
ttyctx.num = wr_length;
tty_write(tty_cmd_cells, &ttyctx);
}
items++;
}
TAILQ_REMOVE(&cl->items, ci, entry); TAILQ_REMOVE(&cl->items, ci, entry);
screen_write_free_citem(ci); screen_write_free_citem(ci);
last = ci->x; last = ci->x;
} }
free(vr);
} }
s->cx = cx; s->cy = cy; s->cx = cx; s->cy = cy;
@@ -1967,6 +2037,27 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc)
u_int sx = screen_size_x(s), sy = screen_size_y(s); u_int sx = screen_size_x(s), sy = screen_size_y(s);
u_int width = ud->width, xx, not_wrap; u_int width = ud->width, xx, not_wrap;
int selected, skip = 1; int selected, skip = 1;
struct window_pane *base_wp = ctx->wp, *wp;
struct window *w;
u_int found_self, px, py;
if (base_wp != NULL) {
w = base_wp->window;
px = ctx->s->cx;
py = ctx->s->cy;
TAILQ_FOREACH(wp, &w->panes, entry) {
if (wp == base_wp) {
found_self = 1;
continue;
}
if (found_self && wp->layout_cell == NULL &&
!(wp->flags & PANE_MINIMISED) &&
(py >= wp->yoff && py <= wp->yoff + wp->sy) &&
(px >= wp->xoff && px <= wp->xoff + wp->sx))
return;
}
}
/* Ignore padding cells. */ /* Ignore padding cells. */
if (gc->flags & GRID_FLAG_PADDING) if (gc->flags & GRID_FLAG_PADDING)

8
tmux.h
View File

@@ -2234,6 +2234,11 @@ struct mode_tree_sort_criteria {
int reversed; int reversed;
}; };
struct visible_range {
u_int px;
u_int nx;
};
/* tmux.c */ /* tmux.c */
extern struct options *global_options; extern struct options *global_options;
extern struct options *global_s_options; extern struct options *global_s_options;
@@ -3164,6 +3169,9 @@ void screen_write_alternateoff(struct screen_write_ctx *,
/* screen-redraw.c */ /* screen-redraw.c */
void screen_redraw_screen(struct client *); void screen_redraw_screen(struct client *);
void screen_redraw_pane(struct client *, struct window_pane *, int); void screen_redraw_pane(struct client *, struct window_pane *, int);
void screen_redraw_get_visible_ranges(u_int, u_int, u_int,
struct window_pane *, struct visible_range **, int *);
/* screen.c */ /* screen.c */
void screen_init(struct screen *, u_int, u_int, u_int); void screen_init(struct screen *, u_int, u_int, u_int);