From eaa467618b42845ad4a3e0571e334977733ba340 Mon Sep 17 00:00:00 2001 From: Michael Grant Date: Mon, 8 Dec 2025 14:28:17 +0000 Subject: [PATCH] 1. Rework floating panes to have a stub layout_cell, 2. Add new <..> format to list-windows & select-layout for floating anes, 3. Fix zooming to work with floating panes, 4. Fix several display issues. --- cmd-new-pane.c | 22 ++++-- cmd-resize-pane.c | 77 ++++++++++--------- cmd-select-layout.c | 2 +- cmd-select-pane.c | 2 +- cmd-split-window.c | 2 +- format.c | 6 +- layout-custom.c | 180 +++++++++++++++++++++++++++++++++----------- layout.c | 96 ++++++++++++++++++----- screen-redraw.c | 38 +++++----- screen-write.c | 3 +- server-client.c | 17 +++-- spawn.c | 16 ++-- tmux.h | 9 +-- tty.c | 4 +- window.c | 43 ++++++++--- 15 files changed, 357 insertions(+), 160 deletions(-) diff --git a/cmd-new-pane.c b/cmd-new-pane.c index 77be1baa..a014aec6 100644 --- a/cmd-new-pane.c +++ b/cmd-new-pane.c @@ -59,6 +59,7 @@ cmd_new_pane_exec(struct cmd *self, struct cmdq_item *item) struct winlink *wl = target->wl; struct window *w = wl->window; struct window_pane *wp = target->wp, *new_wp; + struct layout_cell *lc; struct cmd_find_state fs; int flags, input; const char *template; @@ -147,13 +148,6 @@ cmd_new_pane_exec(struct cmd *self, struct cmdq_item *item) } } - sc.xoff = x; - sc.yoff = y; - last_x = x; - last_y = y; - sc.sx = sx; - sc.sy = sy; - input = (args_has(args, 'I') && count == 0); flags = SPAWN_FLOATING; @@ -169,7 +163,19 @@ cmd_new_pane_exec(struct cmd *self, struct cmdq_item *item) sc.wl = wl; sc.wp0 = wp; - sc.lc = NULL; + + /* Floating panes sit in layout cells which are not in the layout_root + * tree so we call it with parent == NULL. + */ + lc = layout_create_cell(NULL); + lc->xoff = x; + lc->yoff = y; + lc->sx = sx; + lc->sy = sy; + sc.lc = lc; + + last_x = x; /* Statically save last xoff & yoff so that new */ + last_y = y; /* floating panes offset so they don't overlap. */ args_to_vector(args, &sc.argc, &sc.argv); sc.environ = environ_create(); diff --git a/cmd-resize-pane.c b/cmd-resize-pane.c index 75ef9e0f..5abbaa3c 100644 --- a/cmd-resize-pane.c +++ b/cmd-resize-pane.c @@ -82,14 +82,14 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_NORMAL); if (c == NULL || c->session != s) return (CMD_RETURN_NORMAL); - if (c->tty.mouse_wp->layout_cell != NULL) { - c->tty.mouse_drag_update = cmd_resize_pane_mouse_update_tiled; - cmd_resize_pane_mouse_update_tiled(c, &event->m); - } else { + if (c->tty.mouse_wp->flags & PANE_FLOATING) { window_redraw_active_switch(w, c->tty.mouse_wp); window_set_active_pane(w, c->tty.mouse_wp, 1); c->tty.mouse_drag_update = cmd_resize_pane_mouse_update_floating; cmd_resize_pane_mouse_update_floating(c, &event->m); + } else { + c->tty.mouse_drag_update = cmd_resize_pane_mouse_update_tiled; + cmd_resize_pane_mouse_update_tiled(c, &event->m); } return (CMD_RETURN_NORMAL); } @@ -163,7 +163,9 @@ cmd_resize_pane_mouse_update_floating(struct client *c, struct mouse_event *m) struct winlink *wl; struct window *w; struct window_pane *wp; - u_int y, ly, x, lx, new_sx, new_sy, resizes = 0; + struct layout_cell *lc; + u_int y, ly, x, lx, new_sx, new_sy; + int new_xoff, new_yoff, resizes = 0; wl = cmd_mouse_window(m, NULL); if (wl == NULL) { @@ -184,89 +186,93 @@ cmd_resize_pane_mouse_update_floating(struct client *c, struct mouse_event *m) ly = m->statusat - 1; wp = c->tty.mouse_wp; + lc = wp->layout_cell; log_debug("%s: %%%u resize_pane xoff=%u sx=%u xy=%ux%u lxy=%ux%u", __func__, wp->id, wp->xoff, wp->sx, x, y, lx, ly); if ((((int)lx == wp->xoff - 1) || ((int)lx == wp->xoff)) && ((int)ly == wp->yoff - 1)) { - /* Top left border */ - new_sx = wp->sx + (lx - x); + /* Top left corner */ + new_sx = lc->sx + (lx - x); if (new_sx < PANE_MINIMUM) new_sx = PANE_MINIMUM; - new_sy = wp->sy + (ly - y); + new_sy = lc->sy + (ly - y); if (new_sy < PANE_MINIMUM) new_sy = PANE_MINIMUM; - window_pane_move(wp, x + 1, y + 1); - window_pane_resize(wp, new_sx, new_sy); + new_xoff = x + 1; /* Because mouse is on border at xoff - 1 */ + new_yoff = y + 1; + layout_set_size(lc, new_sx, new_sy, new_xoff, new_yoff); resizes++; } else if ((((int)lx == wp->xoff + (int)wp->sx + 1) || ((int)lx == wp->xoff + (int)wp->sx)) && ((int)ly == wp->yoff - 1)) { - /* Top right border */ - new_sx = x - wp->xoff - 1; + /* Top right corner */ + new_sx = x - lc->xoff; if (new_sx < PANE_MINIMUM) new_sx = PANE_MINIMUM; - new_sy = wp->sy + (ly - y); + new_sy = lc->sy + (ly - y); if (new_sy < PANE_MINIMUM) new_sy = PANE_MINIMUM; - window_pane_move(wp, wp->xoff, y + 1); - window_pane_resize(wp, new_sx, new_sy); + new_yoff = y + 1; + layout_set_size(lc, new_sx, new_sy, lc->xoff, new_yoff); resizes++; } else if ((((int)lx == wp->xoff - 1) || ((int)lx == wp->xoff)) && ((int)ly == wp->yoff + (int)wp->sy)) { - /* Bottom left border */ - new_sx = wp->sx + (lx - x); + /* Bottom left corner */ + new_sx = lc->sx + (lx - x); if (new_sx < PANE_MINIMUM) new_sx = PANE_MINIMUM; - new_sy = y - wp->yoff; + new_sy = y - lc->yoff; if (new_sy < PANE_MINIMUM) return; - window_pane_move(wp, x + 1, wp->yoff); - window_pane_resize(wp, new_sx, new_sy); + new_xoff = x + 1; + layout_set_size(lc, new_sx, new_sy, new_xoff, lc->yoff); resizes++; } else if ((((int)lx == wp->xoff + (int)wp->sx + 1) || ((int)lx == wp->xoff + (int)wp->sx)) && ((int)ly == wp->yoff + (int)wp->sy)) { /* Bottom right corner */ - new_sx = x - wp->xoff - 1; + new_sx = x - lc->xoff; if (new_sx < PANE_MINIMUM) new_sx = PANE_MINIMUM; - new_sy = y - wp->yoff; + new_sy = y - lc->yoff; if (new_sy < PANE_MINIMUM) new_sy = PANE_MINIMUM; - window_pane_resize(wp, new_sx, new_sy); + layout_set_size(lc, new_sx, new_sy, lc->xoff, lc->yoff); resizes++; } else if ((int)lx == wp->xoff + (int)wp->sx + 1) { /* Right border */ - new_sx = x - wp->xoff - 1; + new_sx = x - lc->xoff; if (new_sx < PANE_MINIMUM) return; - window_pane_resize(wp, new_sx, wp->sy); + layout_set_size(lc, new_sx, lc->sy, lc->xoff, lc->yoff); resizes++; } else if ((int)lx == wp->xoff - 1) { /* Left border */ - new_sx = wp->sx + (lx - x); + new_sx = lc->sx + (lx - x); if (new_sx < PANE_MINIMUM) return; - window_pane_move(wp, x + 1, wp->yoff); - window_pane_resize(wp, new_sx, wp->sy); + new_xoff = x + 1; + layout_set_size(lc, new_sx, lc->sy, new_xoff, lc->yoff); resizes++; } else if ((int)ly == wp->yoff + (int)wp->sy) { /* Bottom border */ - new_sy = y - wp->yoff; + new_sy = y - lc->yoff; if (new_sy < PANE_MINIMUM) return; - window_pane_resize(wp, wp->sx, new_sy); + layout_set_size(lc, lc->sx, new_sy, lc->xoff, lc->yoff); resizes++; } else if ((int)ly == wp->yoff - 1) { - /* Top border */ - window_pane_move(wp, wp->xoff + (x - lx), y + 1); - /* + /* Top border (move instead of resize) */ + new_xoff = lc->xoff + (x - lx); + new_yoff = y + 1; + layout_set_size(lc, lc->sx, lc->sy, new_xoff, new_yoff); + /* To resize instead of move: new_sy = wp->sy + (ly - y); if (new_sy < PANE_MINIMUM) return; - window_pane_move(wp, wp->xoff, y + 1); - window_pane_resize(wp, wp->sx, new_sy); + new_yoff = y + 1; + layout_set_size(lc, lc->sx, new_sy, lc->xoff, new_yoff); */ resizes++; } else { @@ -274,6 +280,7 @@ cmd_resize_pane_mouse_update_floating(struct client *c, struct mouse_event *m) __func__, wp->id, wp->xoff, wp->sx, x, y, lx, ly); } if (resizes != 0) { + layout_fix_panes(w, NULL); server_redraw_window(w); server_redraw_window_borders(w); } diff --git a/cmd-select-layout.c b/cmd-select-layout.c index 6dfe2b6a..41779747 100644 --- a/cmd-select-layout.c +++ b/cmd-select-layout.c @@ -90,7 +90,7 @@ cmd_select_layout_exec(struct cmd *self, struct cmdq_item *item) previous = 1; oldlayout = w->old_layout; - w->old_layout = layout_dump(w->layout_root); + w->old_layout = layout_dump(w, w->layout_root); if (next || previous) { if (next) diff --git a/cmd-select-pane.c b/cmd-select-pane.c index b0138893..99b0841c 100644 --- a/cmd-select-pane.c +++ b/cmd-select-pane.c @@ -160,7 +160,7 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) server_redraw_window_borders(markedwp->window); server_status_window(markedwp->window); } - if (wp->layout_cell == NULL) { + if (wp->flags & PANE_FLOATING) { window_redraw_active_switch(w, wp); window_set_active_pane(w, wp, 1); } diff --git a/cmd-split-window.c b/cmd-split-window.c index ca8354a7..bd2a4b48 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -71,7 +71,7 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) struct args_value *av; u_int count = args_count(args), curval = 0; - if (wp->layout_cell == NULL) { + if (wp->flags & PANE_FLOATING) { cmdq_error(item, "can't split a floating pane"); return (CMD_RETURN_ERROR); } diff --git a/format.c b/format.c index 17a9dd53..36799d27 100644 --- a/format.c +++ b/format.c @@ -821,8 +821,8 @@ format_cb_window_layout(struct format_tree *ft) return (NULL); if (w->saved_layout_root != NULL) - return (layout_dump(w->saved_layout_root)); - return (layout_dump(w->layout_root)); + return (layout_dump(w, w->saved_layout_root)); + return (layout_dump(w, w->layout_root)); } /* Callback for window_visible_layout. */ @@ -834,7 +834,7 @@ format_cb_window_visible_layout(struct format_tree *ft) if (w == NULL) return (NULL); - return (layout_dump(w->layout_root)); + return (layout_dump(w, w->layout_root)); } /* Callback for pane_start_command. */ diff --git a/layout-custom.c b/layout-custom.c index 2bfb6d89..30f8909b 100644 --- a/layout-custom.c +++ b/layout-custom.c @@ -27,10 +27,11 @@ static struct layout_cell *layout_find_bottomright(struct layout_cell *); static u_short layout_checksum(const char *); static int layout_append(struct layout_cell *, char *, size_t); -static struct layout_cell *layout_construct(struct layout_cell *, - const char **); +static int layout_construct(struct layout_cell *, + const char **, struct layout_cell **, + struct layout_cell **); static void layout_assign(struct window_pane **, - struct layout_cell *); + struct layout_cell *, int); /* Find the bottom-right cell. */ static struct layout_cell * @@ -58,14 +59,33 @@ layout_checksum(const char *layout) /* Dump layout as a string. */ char * -layout_dump(struct layout_cell *root) +layout_dump(struct window *w, struct layout_cell *root) { - char layout[8192], *out; + char layout[8192], *out; + int braket; + struct window_pane *wp; *layout = '\0'; if (layout_append(root, layout, sizeof layout) != 0) return (NULL); + braket = 0; + TAILQ_FOREACH(wp, &w->z_index, zentry) { + if (~wp->flags & PANE_FLOATING) + break; + if (!braket) { + strcat(layout, "<"); + braket = 1; + } + if (layout_append(wp->layout_cell, layout, sizeof layout) != 0) + return (NULL); + strcat(layout, ","); + } + if (braket) { + /* Overwrite the trailing ','. */ + layout[strlen(layout) - 1] = '>'; + } + xasprintf(&out, "%04hx,%s", layout_checksum(layout), layout); return (out); } @@ -81,7 +101,8 @@ layout_append(struct layout_cell *lc, char *buf, size_t len) if (len == 0) return (-1); - + if (lc == NULL) + return (0); if (lc->wp != NULL) { tmplen = xsnprintf(tmp, sizeof tmp, "%ux%u,%u,%u,%u", lc->sx, lc->sy, lc->xoff, lc->yoff, lc->wp->id); @@ -109,6 +130,7 @@ layout_append(struct layout_cell *lc, char *buf, size_t len) } buf[strlen(buf) - 1] = brackets[0]; break; + case LAYOUT_FLOATING: case LAYOUT_WINDOWPANE: break; } @@ -125,6 +147,7 @@ layout_check(struct layout_cell *lc) switch (lc->type) { case LAYOUT_WINDOWPANE: + case LAYOUT_FLOATING: break; case LAYOUT_LEFTRIGHT: TAILQ_FOREACH(lcchild, &lc->cells, entry) { @@ -156,7 +179,7 @@ layout_check(struct layout_cell *lc) int layout_parse(struct window *w, const char *layout, char **cause) { - struct layout_cell *lc, *lcchild; + struct layout_cell *lcchild, *tiled_lc = NULL, *floating_lc = NULL; struct window_pane *wp; u_int npanes, ncells, sx = 0, sy = 0; u_short csum; @@ -173,11 +196,16 @@ layout_parse(struct window *w, const char *layout, char **cause) } /* Build the layout. */ - lc = layout_construct(NULL, &layout); - if (lc == NULL) { + if (layout_construct(NULL, &layout, &tiled_lc, &floating_lc) != 0) { *cause = xstrdup("invalid layout"); return (-1); } + if (tiled_lc == NULL) { + /* A stub layout cell for an empty window. */ + tiled_lc = layout_create_cell(NULL); + tiled_lc->type = LAYOUT_LEFTRIGHT; + layout_set_size(tiled_lc, w->sx, w->sy, 0, 0); + } if (*layout != '\0') { *cause = xstrdup("invalid layout"); goto fail; @@ -186,8 +214,10 @@ layout_parse(struct window *w, const char *layout, char **cause) /* Check this window will fit into the layout. */ for (;;) { npanes = window_count_panes(w); - ncells = layout_count_cells(lc); + ncells = layout_count_cells(tiled_lc); + ncells += layout_count_cells(floating_lc); if (npanes > ncells) { + /* Modify this to open a new pane */ xasprintf(cause, "have %u panes but need %u", npanes, ncells); goto fail; @@ -195,9 +225,17 @@ layout_parse(struct window *w, const char *layout, char **cause) if (npanes == ncells) break; - /* Fewer panes than cells - close the bottom right. */ - lcchild = layout_find_bottomright(lc); - layout_destroy_cell(w, lcchild, &lc); + /* + * Fewer panes than cells - close floating panes first + * then close the bottom right until. + */ + if (floating_lc && ! TAILQ_EMPTY(&floating_lc->cells)) { + lcchild = TAILQ_FIRST(&floating_lc->cells); + layout_destroy_cell(w, lcchild, &floating_lc); + } else { + lcchild = layout_find_bottomright(tiled_lc); + layout_destroy_cell(w, lcchild, &tiled_lc); + } } /* @@ -205,85 +243,112 @@ layout_parse(struct window *w, const char *layout, char **cause) * an incorrect top cell size - if it is larger than the top child then * correct that (if this is still wrong the check code will catch it). */ - switch (lc->type) { + + switch (tiled_lc->type) { case LAYOUT_WINDOWPANE: break; case LAYOUT_LEFTRIGHT: - TAILQ_FOREACH(lcchild, &lc->cells, entry) { + TAILQ_FOREACH(lcchild, &tiled_lc->cells, entry) { sy = lcchild->sy + 1; sx += lcchild->sx + 1; } break; case LAYOUT_TOPBOTTOM: - TAILQ_FOREACH(lcchild, &lc->cells, entry) { + TAILQ_FOREACH(lcchild, &tiled_lc->cells, entry) { sx = lcchild->sx + 1; sy += lcchild->sy + 1; } break; + case LAYOUT_FLOATING: + *cause = xstrdup("invalid layout"); + goto fail; } - if (lc->type != LAYOUT_WINDOWPANE && (lc->sx != sx || lc->sy != sy)) { - log_debug("fix layout %u,%u to %u,%u", lc->sx, lc->sy, sx,sy); - layout_print_cell(lc, __func__, 0); - lc->sx = sx - 1; lc->sy = sy - 1; + if (tiled_lc->type != LAYOUT_WINDOWPANE && + (tiled_lc->sx != sx || tiled_lc->sy != sy)) { + log_debug("fix layout %u,%u to %u,%u", tiled_lc->sx, + tiled_lc->sy, sx,sy); + layout_print_cell(tiled_lc, __func__, 0); + tiled_lc->sx = sx - 1; tiled_lc->sy = sy - 1; } /* Check the new layout. */ - if (!layout_check(lc)) { + if (!layout_check(tiled_lc)) { *cause = xstrdup("size mismatch after applying layout"); goto fail; } - /* Resize to the layout size. */ - window_resize(w, lc->sx, lc->sy, -1, -1); + /* Resize window to the layout size. */ + if (sx != 0 && sy != 0) + window_resize(w, tiled_lc->sx, tiled_lc->sy, -1, -1); /* Destroy the old layout and swap to the new. */ layout_free_cell(w->layout_root); - w->layout_root = lc; + w->layout_root = tiled_lc; /* Assign the panes into the cells. */ wp = TAILQ_FIRST(&w->panes); - layout_assign(&wp, lc); + layout_assign(&wp, tiled_lc, 0); + layout_assign(&wp, floating_lc, 1); + + /* Fix z_indexes. */ + while (!TAILQ_EMPTY(&w->z_index)) { + wp = TAILQ_FIRST(&w->z_index); + TAILQ_REMOVE(&w->z_index, wp, zentry); + } + layout_fix_zindexes(w, floating_lc); + layout_fix_zindexes(w, tiled_lc); /* Update pane offsets and sizes. */ layout_fix_offsets(w); layout_fix_panes(w, NULL); recalculate_sizes(); - layout_print_cell(lc, __func__, 0); + layout_print_cell(tiled_lc, __func__, 0); + layout_print_cell(floating_lc, __func__, 0); + + /* Free the floating layout cell, no longer needed. */ + layout_free_cell(floating_lc); notify_window("window-layout-changed", w); return (0); fail: - layout_free_cell(lc); + layout_free_cell(tiled_lc); + layout_free_cell(floating_lc); return (-1); } /* Assign panes into cells. */ static void -layout_assign(struct window_pane **wp, struct layout_cell *lc) +layout_assign(struct window_pane **wp, struct layout_cell *lc, int floating) { struct layout_cell *lcchild; + if (lc == NULL) + return; + switch (lc->type) { case LAYOUT_WINDOWPANE: layout_make_leaf(lc, *wp); + if (floating) { + (*wp)->flags |= PANE_FLOATING; + } *wp = TAILQ_NEXT(*wp, entry); return; case LAYOUT_LEFTRIGHT: case LAYOUT_TOPBOTTOM: + case LAYOUT_FLOATING: TAILQ_FOREACH(lcchild, &lc->cells, entry) - layout_assign(wp, lcchild); + layout_assign(wp, lcchild, 1); return; } } -/* Construct a cell from all or part of a layout tree. */ static struct layout_cell * -layout_construct(struct layout_cell *lcparent, const char **layout) +layout_construct_cell(struct layout_cell *lcparent, const char **layout) { - struct layout_cell *lc, *lcchild; + struct layout_cell *lc; u_int sx, sy, xoff, yoff; const char *saved; @@ -324,17 +389,42 @@ layout_construct(struct layout_cell *lcparent, const char **layout) lc->xoff = xoff; lc->yoff = yoff; + return (lc); +} + + +/* + * Given a character string layout, recursively construct cells. + * Possible return values: + * lc LAYOUT_WINDOWPANE, no children + * lc LAYOUT_LEFTRIGHT or LAYOUT_TOPBOTTOM, with children + * floating_lc LAYOUT_FLOATING, with children + */ +static int +layout_construct(struct layout_cell *lcparent, const char **layout, + struct layout_cell **lc, struct layout_cell **floating_lc) +{ + struct layout_cell *lcchild, *saved_lc; + + *lc = layout_construct_cell(lcparent, layout); + switch (**layout) { case ',': case '}': case ']': + case '>': case '\0': - return (lc); + return (0); case '{': - lc->type = LAYOUT_LEFTRIGHT; + (*lc)->type = LAYOUT_LEFTRIGHT; break; case '[': - lc->type = LAYOUT_TOPBOTTOM; + (*lc)->type = LAYOUT_TOPBOTTOM; + break; + case '<': + saved_lc = *lc; + *lc = layout_create_cell(lcparent); + (*lc)->type = LAYOUT_FLOATING; break; default: goto fail; @@ -342,13 +432,12 @@ layout_construct(struct layout_cell *lcparent, const char **layout) do { (*layout)++; - lcchild = layout_construct(lc, layout); - if (lcchild == NULL) + if (layout_construct(*lc, layout, &lcchild, floating_lc) != 0) goto fail; - TAILQ_INSERT_TAIL(&lc->cells, lcchild, entry); + TAILQ_INSERT_TAIL(&(*lc)->cells, lcchild, entry); } while (**layout == ','); - switch (lc->type) { + switch ((*lc)->type) { case LAYOUT_LEFTRIGHT: if (**layout != '}') goto fail; @@ -357,14 +446,21 @@ layout_construct(struct layout_cell *lcparent, const char **layout) if (**layout != ']') goto fail; break; + case LAYOUT_FLOATING: + if (**layout != '>') + goto fail; + *floating_lc = *lc; + *lc = saved_lc; + break; default: goto fail; } (*layout)++; - return (lc); + return (0); fail: - layout_free_cell(lc); - return (NULL); + layout_free_cell(*lc); + layout_free_cell(*floating_lc); + return (-1); } diff --git a/layout.c b/layout.c index 8087a9e7..b3d83622 100644 --- a/layout.c +++ b/layout.c @@ -83,9 +83,24 @@ layout_free_cell(struct layout_cell *lc) layout_free_cell(lcchild); } break; + case LAYOUT_FLOATING: + /* A Floating layout cell is only used temporarily + * while select-layout constructs a layout. + * Cleave the children from the temp layout, then + * free temp floating layout cell. Each floating + * pane has stub layout. + */ + while (!TAILQ_EMPTY(&lc->cells)) { + lcchild = TAILQ_FIRST(&lc->cells); + TAILQ_REMOVE(&lc->cells, lcchild, entry); + lcchild->parent = NULL; + } + break; case LAYOUT_WINDOWPANE: - if (lc->wp != NULL) + if (lc->wp != NULL) { + lc->wp->layout_cell->parent = NULL; lc->wp->layout_cell = NULL; + } break; } @@ -98,6 +113,9 @@ layout_print_cell(struct layout_cell *lc, const char *hdr, u_int n) struct layout_cell *lcchild; const char *type; + if (lc == NULL) + return; + switch (lc->type) { case LAYOUT_LEFTRIGHT: type = "LEFTRIGHT"; @@ -105,6 +123,9 @@ layout_print_cell(struct layout_cell *lc, const char *hdr, u_int n) case LAYOUT_TOPBOTTOM: type = "TOPBOTTOM"; break; + case LAYOUT_FLOATING: + type = "FLOATING"; + break; case LAYOUT_WINDOWPANE: type = "WINDOWPANE"; break; @@ -118,6 +139,7 @@ layout_print_cell(struct layout_cell *lc, const char *hdr, u_int n) switch (lc->type) { case LAYOUT_LEFTRIGHT: case LAYOUT_TOPBOTTOM: + case LAYOUT_FLOATING: TAILQ_FOREACH(lcchild, &lc->cells, entry) layout_print_cell(lcchild, hdr, n + 1); break; @@ -153,6 +175,7 @@ layout_search_by_border(struct layout_cell *lc, u_int x, u_int y) return (last); break; case LAYOUT_WINDOWPANE: + case LAYOUT_FLOATING: break; } @@ -198,6 +221,29 @@ layout_make_node(struct layout_cell *lc, enum layout_type type) lc->wp = NULL; } +void +layout_fix_zindexes(struct window *w, struct layout_cell *lc) +{ + struct layout_cell *lcchild; + + if (lc == NULL) + return; + + switch (lc->type) { + case LAYOUT_WINDOWPANE: + TAILQ_INSERT_TAIL(&w->z_index, lc->wp, zentry); + break; + case LAYOUT_LEFTRIGHT: + case LAYOUT_TOPBOTTOM: + case LAYOUT_FLOATING: + TAILQ_FOREACH(lcchild, &lc->cells, entry) + layout_fix_zindexes(w, lcchild); + return; + default: + fatalx("bad layout type"); + } +} + /* Fix cell offsets for a child cell. */ static void layout_fix_offsets1(struct layout_cell *lc) @@ -307,7 +353,8 @@ layout_fix_panes(struct window *w, struct window_pane *skip) sx = lc->sx; sy = lc->sy; - if (layout_add_horizontal_border(w, lc, status)) { + if (~wp->flags & PANE_FLOATING && + layout_add_horizontal_border(w, lc, status)) { if (status == PANE_STATUS_TOP) wp->yoff++; sy--; @@ -353,6 +400,7 @@ layout_count_cells(struct layout_cell *lc) return (1); case LAYOUT_LEFTRIGHT: case LAYOUT_TOPBOTTOM: + case LAYOUT_FLOATING: count = 0; TAILQ_FOREACH(lcchild, &lc->cells, entry) count += layout_count_cells(lcchild); @@ -462,7 +510,7 @@ layout_resize_adjust(struct window *w, struct layout_cell *lc, } } -/* Destroy a cell and redistribute the space. */ +/* Destroy a cell and redistribute the space in tiled cells. */ void layout_destroy_cell(struct window *w, struct layout_cell *lc, struct layout_cell **lcroot) @@ -470,36 +518,44 @@ layout_destroy_cell(struct window *w, struct layout_cell *lc, struct layout_cell *lcother, *lcparent; /* - * If no parent, this is the last pane so window close is imminent and - * there is no need to resize anything. + * If no parent, this is either a floating pane or the last + * pane so window close is imminent and there is no need to + * resize anything. */ lcparent = lc->parent; if (lcparent == NULL) { - layout_free_cell(lc); - *lcroot = NULL; + if (lc->wp != NULL && ~lc->wp->flags & PANE_FLOATING) + *lcroot = NULL; + /* xxx if (lc->type == LAYOUT_WINDOWPANE) */ + layout_free_cell(lc); return; } - /* Merge the space into the previous or next cell. */ - if (lc == TAILQ_FIRST(&lcparent->cells)) - lcother = TAILQ_NEXT(lc, entry); - else - lcother = TAILQ_PREV(lc, layout_cells, entry); - if (lcother != NULL && lcparent->type == LAYOUT_LEFTRIGHT) - layout_resize_adjust(w, lcother, lcparent->type, lc->sx + 1); - else if (lcother != NULL) - layout_resize_adjust(w, lcother, lcparent->type, lc->sy + 1); + /* In tiled layouts, merge the space into the previous or next cell. */ + if (lcparent->type != LAYOUT_FLOATING) { + if (lc == TAILQ_FIRST(&lcparent->cells)) + lcother = TAILQ_NEXT(lc, entry); + else + lcother = TAILQ_PREV(lc, layout_cells, entry); + if (lcother != NULL && lcparent->type == LAYOUT_LEFTRIGHT) + layout_resize_adjust(w, lcother, lcparent->type, lc->sx + 1); + else if (lcother != NULL) + layout_resize_adjust(w, lcother, lcparent->type, lc->sy + 1); + } /* Remove this from the parent's list. */ TAILQ_REMOVE(&lcparent->cells, lc, entry); layout_free_cell(lc); + if (lcparent->type == LAYOUT_FLOATING) + return; + /* - * If the parent now has one cell, remove the parent from the tree and - * replace it by that cell. + * In tiled layouts, if the parent now has one cell, remove + * the parent from the tree and replace it by that cell. */ lc = TAILQ_FIRST(&lcparent->cells); - if (TAILQ_NEXT(lc, entry) == NULL) { + if (lc != NULL && TAILQ_NEXT(lc, entry) == NULL) { TAILQ_REMOVE(&lcparent->cells, lc, entry); lc->parent = lcparent->parent; @@ -741,7 +797,7 @@ layout_resize_pane_shrink(struct window *w, struct layout_cell *lc, return (size); } -/* Assign window pane to newly split cell. */ +/* Assign window pane to new cell. */ void layout_assign_pane(struct layout_cell *lc, struct window_pane *wp, int do_not_resize) diff --git a/screen-redraw.c b/screen-redraw.c index 71cdf542..9244348f 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -106,7 +106,7 @@ screen_redraw_two_panes(struct window *w, enum layout_type *type) u_int count = 0; TAILQ_FOREACH(wp, &w->panes, entry) { - if (wp->layout_cell == NULL) + if (wp->flags & PANE_FLOATING || wp->layout_cell == NULL) continue; count++; if (count > 2 || wp->layout_cell->parent == NULL) @@ -146,7 +146,7 @@ screen_redraw_pane_border(struct screen_redraw_ctx *ctx, struct window_pane *wp, sb_w = wp->scrollbar_style.width + wp->scrollbar_style.pad; /* Floating pane borders */ - if (wp->layout_cell == NULL) { + if (wp->flags & PANE_FLOATING) { if ((int)px == wp->xoff - 1 && (int)py >= wp->yoff - 1 && (int)py <= wp->yoff + (int)wp->sy) return (SCREEN_REDRAW_BORDER_LEFT); @@ -247,7 +247,7 @@ screen_redraw_cell_border(struct screen_redraw_ctx *ctx, struct window_pane *wp, u_int sy = w->sy; int sb_w, floating = 0; - floating = (wp->layout_cell == NULL); + floating = (wp->flags & PANE_FLOATING); sb_w = wp->scrollbar_style.width + wp->scrollbar_style.pad; @@ -268,7 +268,7 @@ screen_redraw_cell_border(struct screen_redraw_ctx *ctx, struct window_pane *wp, /* If checking a cell from a tiled pane, ignore floating panes * because 2 side-by-side or top-bottom panes share a border * which is used to do split colouring. Essentially treat all - * non-floating panes as being in a single z-index. + * tiled panes as being in a single z-index. * * If checking a cell from a floating pane, only check cells * from this floating pane, again, essentially only this z-index. @@ -277,8 +277,7 @@ screen_redraw_cell_border(struct screen_redraw_ctx *ctx, struct window_pane *wp, /* Check all the panes. */ TAILQ_FOREACH(wp2, &w->z_index, zentry) { if (!window_pane_visible(wp2) || - (wp->flags & PANE_MINIMISED) || - (!floating && wp2->layout_cell==NULL) || + (!floating && (wp2->flags & PANE_FLOATING)) || (floating && wp2 != wp)) continue; if (((int)px < wp2->xoff - 1 || @@ -317,7 +316,7 @@ screen_redraw_type_of_cell(struct screen_redraw_ctx *ctx, if (px > sx || py > sy) return (CELL_OUTSIDE); - floating = (wp->layout_cell == NULL); + floating = (wp->flags & PANE_FLOATING); /* * Construct a bitmask of whether the cells to the left (bit 8), right, @@ -433,7 +432,7 @@ screen_redraw_check_cell(struct screen_redraw_ctx *ctx, u_int px, u_int py, TAILQ_FOREACH(wp, &w->z_index, zentry) { sb_w = wp->scrollbar_style.width + wp->scrollbar_style.pad; - if (! (wp->flags & PANE_MINIMISED) && + if (~wp->flags & PANE_MINIMISED && ((int)px >= wp->xoff - 1 && (int)px <= wp->xoff + (int)wp->sx + sb_w) && ((int)py >= wp->yoff - 1 && @@ -453,14 +452,13 @@ screen_redraw_check_cell(struct screen_redraw_ctx *ctx, u_int px, u_int py, * top-bottom windows with a shared border and half the shared * border is the active border. */ - if (wp->layout_cell != NULL) + if (~wp->flags & PANE_FLOATING) tiled_only = 1; do { /* Loop until back to wp==start.*/ if (!window_pane_visible(wp) || - (wp->flags & PANE_MINIMISED) || - (tiled_only && wp->layout_cell==NULL)) + (tiled_only && (wp->flags & PANE_FLOATING))) goto next; *wpp = wp; @@ -1067,13 +1065,13 @@ screen_redraw_get_visible_ranges(struct window_pane *base_wp, u_int px, continue; } - tb = wp->yoff-1; - bb = wp->yoff + wp->sy; + tb = wp->yoff - 1; + bb = wp->yoff + wp->sy + 1; if (!found_self || - (wp->flags & PANE_MINIMISED) || - py < tb || - (wp->layout_cell == NULL && py > bb) || - (wp->layout_cell != NULL && py >= bb)) + !window_pane_visible(wp) || + py < tb || + ((wp->flags & PANE_FLOATING) && py > bb) || + (~(wp->flags & PANE_FLOATING) && py >= bb)) continue; /* Are scrollbars enabled? */ @@ -1283,6 +1281,9 @@ screen_redraw_draw_pane_scrollbar(struct screen_redraw_ctx *ctx, else sb_x = xoff + wp->sx - ox; + if (ctx->statustop) + sb_y += ctx->statuslines; + if (slider_h < 1) slider_h = 1; if (slider_y >= sb_h) @@ -1312,6 +1313,9 @@ screen_redraw_draw_scrollbar(struct screen_redraw_ctx *ctx, int yoff = wp->yoff; struct visible_ranges *vr; + if (ctx->statustop) + sy += ctx->statuslines; + /* Set up style for slider. */ gc = sb_style->gc; memcpy(&slgc, &gc, sizeof slgc); diff --git a/screen-write.c b/screen-write.c index 96cdeab4..48ee3d15 100644 --- a/screen-write.c +++ b/screen-write.c @@ -139,10 +139,9 @@ screen_write_set_client_cb(struct tty_ctx *ttyctx, struct client *c) if (c->session->curw->window != wp->window) return (0); - /* if (wp->layout_cell == NULL) return (0); - */ + if (wp->flags & (PANE_REDRAW|PANE_DROP)) return (-1); if (c->flags & CLIENT_REDRAWPANES) { diff --git a/server-client.c b/server-client.c index 1e79a49c..6c6e1930 100644 --- a/server-client.c +++ b/server-client.c @@ -643,9 +643,9 @@ server_client_check_mouse_in_pane(struct window_pane *wp, u_int px, u_int py, return (SCROLLBAR_SLIDER); } else /* py > sl_bottom */ return (SCROLLBAR_DOWN); - } else if (wp->layout_cell == NULL && + } else if (wp->flags & PANE_FLOATING && ((int)px == wp->xoff - 1 || - (int)py == wp->yoff -1 || + (int)py == wp->yoff - 1 || (int)py == wp->yoff + (int)wp->sy)) { /* Floating pane left, bottom or top border. */ return (BORDER); @@ -664,7 +664,7 @@ server_client_check_mouse_in_pane(struct window_pane *wp, u_int px, u_int py, if ((int)py >= fwp->yoff - 1 && py <= fwp->yoff + fwp->sy) { if (px == bdr_right) break; - if (wp->layout_cell == NULL) { + if (wp->flags & PANE_FLOATING) { /* Floating pane, check if left border. */ bdr_left = fwp->xoff - 1; if (px == bdr_left) @@ -675,7 +675,7 @@ server_client_check_mouse_in_pane(struct window_pane *wp, u_int px, u_int py, bdr_bottom = fwp->yoff + fwp->sy; if (py == bdr_bottom) break; - if (wp->layout_cell == NULL) { + if (wp->flags & PANE_FLOATING) { /* Floating pane, check if top border. */ bdr_top = fwp->yoff - 1; if (py == bdr_top) @@ -885,8 +885,13 @@ have_event: px = px + m->ox; py = py + m->oy; - /* Try inside the pane. */ - wp = window_get_active_at(w, px, py); + if (type == DRAG && + c->tty.mouse_wp != NULL) + /* Use pane from last mouse event. */ + wp = c->tty.mouse_wp; + else + /* Try inside the pane. */ + wp = window_get_active_at(w, px, py); if (wp == NULL) return (KEYC_UNKNOWN); where = server_client_check_mouse_in_pane(wp, px, py, diff --git a/spawn.c b/spawn.c index 69dbb278..93ffc4b1 100644 --- a/spawn.c +++ b/spawn.c @@ -265,14 +265,7 @@ spawn_pane(struct spawn_context *sc, char **cause) new_wp->flags &= ~(PANE_STATUSREADY|PANE_STATUSDRAWN); } else if (sc->lc == NULL) { new_wp = window_add_pane(w, NULL, hlimit, sc->flags); - if (sc->flags & SPAWN_FLOATING) { - new_wp->flags |= PANE_FLOATING; - window_pane_resize(new_wp, sc->sx, sc->sy); - new_wp->xoff = sc->xoff; - new_wp->yoff = sc->yoff; - } else { - layout_init(w, new_wp); - } + layout_init(w, new_wp); } else { new_wp = window_add_pane(w, sc->wp0, hlimit, sc->flags); if (sc->flags & SPAWN_ZOOM) @@ -281,6 +274,13 @@ spawn_pane(struct spawn_context *sc, char **cause) layout_assign_pane(sc->lc, new_wp, 0); } + /* + * If window currently zoomed, window_set_active_pane calls + * window_unzoom which it copies back the saved_layout_cell. + */ + if (w->flags & WINDOW_ZOOMED) + new_wp->saved_layout_cell = new_wp->layout_cell; + /* * Now we have a pane with nothing running in it ready for the new * process. Work out the command and arguments and store the working diff --git a/tmux.h b/tmux.h index ff04ba95..eaeb6eeb 100644 --- a/tmux.h +++ b/tmux.h @@ -1176,6 +1176,7 @@ struct window_pane { int yoff; int flags; + int saved_flags; #define PANE_REDRAW 0x1 #define PANE_DROP 0x2 #define PANE_FOCUSED 0x4 @@ -1365,6 +1366,7 @@ TAILQ_HEAD(winlink_stack, winlink); enum layout_type { LAYOUT_LEFTRIGHT, LAYOUT_TOPBOTTOM, + LAYOUT_FLOATING, LAYOUT_WINDOWPANE }; @@ -2226,10 +2228,6 @@ struct spawn_context { struct window_pane *wp0; struct layout_cell *lc; - u_int xoff; - u_int yoff; - u_int sx; - u_int sy; const char *name; char **argv; @@ -3350,6 +3348,7 @@ void layout_set_size(struct layout_cell *, u_int, u_int, u_int, u_int); void layout_make_leaf(struct layout_cell *, struct window_pane *); void layout_make_node(struct layout_cell *, enum layout_type); +void layout_fix_zindexes(struct window *, struct layout_cell *); void layout_fix_offsets(struct window *); void layout_fix_panes(struct window *, struct window_pane *); void layout_resize_adjust(struct window *, struct layout_cell *, @@ -3370,7 +3369,7 @@ int layout_spread_cell(struct window *, struct layout_cell *); void layout_spread_out(struct window_pane *); /* layout-custom.c */ -char *layout_dump(struct layout_cell *); +char *layout_dump(struct window *, struct layout_cell *); int layout_parse(struct window *, const char *, char **); /* layout-set.c */ diff --git a/tty.c b/tty.c index c484cbdd..26752a9a 100644 --- a/tty.c +++ b/tty.c @@ -1382,7 +1382,7 @@ tty_draw_pane(struct tty *tty, const struct tty_ctx *ctx, u_int py) if (!ctx->bigger) { if (wp) { - vr = screen_redraw_get_visible_ranges(wp, 0, py, nx); + vr = screen_redraw_get_visible_ranges(wp, 0, ctx->yoff + py, nx); for (r=0; r < vr->used; r++) { if (vr->nx[r] == 0) continue; @@ -2048,7 +2048,7 @@ tty_is_obscured(const struct tty_ctx *ctx) found_self = 1; continue; } - if (found_self && wp->layout_cell == NULL && + if (found_self && wp->flags & PANE_FLOATING && ! (wp->flags & PANE_MINIMISED) && ((wp->yoff >= base_wp->yoff && wp->yoff <= base_wp->yoff + (int)base_wp->sy) || diff --git a/window.c b/window.c index 4660cd18..aaa22ce1 100644 --- a/window.c +++ b/window.c @@ -525,6 +525,8 @@ window_set_active_pane(struct window *w, struct window_pane *wp, int notify) if (wp == w->active) return (0); + if (w->flags & WINDOW_ZOOMED) + window_unzoom(w, 1); lastwp = w->active; window_pane_stack_remove(&w->last_panes, wp); @@ -589,9 +591,9 @@ window_redraw_active_switch(struct window *w, struct window_pane *wp) /* If you want tiled planes to be able to bury * floating planes then do this regardless of - * wp->layout_cell==NULL or not. A new option? + * wp->flags & PANE_FLOATING or not. A new option? */ - if (wp->layout_cell == NULL) { + if (wp->flags & PANE_FLOATING) { TAILQ_REMOVE(&w->z_index, wp, zentry); TAILQ_INSERT_HEAD(&w->z_index, wp, zentry); wp->flags |= PANE_REDRAW; @@ -614,7 +616,7 @@ window_get_active_at(struct window *w, u_int x, u_int y) if (!window_pane_visible(wp)) continue; window_pane_full_size_offset(wp, &xoff, &yoff, &sx, &sy); - if (wp->layout_cell != NULL) { + if (~wp->flags & PANE_FLOATING) { /* Tiled, select up to including bottom or right border. */ if ((int)x < xoff || x > xoff + sx) @@ -688,12 +690,24 @@ window_zoom(struct window_pane *wp) if (w->flags & WINDOW_ZOOMED) return (-1); - if (window_count_panes(w) == 1) - return (-1); - if (w->active != wp) window_set_active_pane(w, wp, 1); + /* Bring pane above other tiled panes and minimise floating panes. */ + TAILQ_FOREACH(wp1, &w->z_index, zentry) { + if (wp1 == wp) { + wp1->saved_flags |= (wp1->flags & PANE_MINIMISED); + wp1->flags &= ~PANE_MINIMISED; + continue; + } + if (wp1->flags & PANE_FLOATING) { + wp1->saved_flags |= (wp1->flags & PANE_MINIMISED); + wp1->flags |= PANE_MINIMISED; + continue; + } + break; + } + TAILQ_FOREACH(wp1, &w->panes, entry) { wp1->saved_layout_cell = wp1->layout_cell; wp1->layout_cell = NULL; @@ -720,6 +734,14 @@ window_unzoom(struct window *w, int notify) w->layout_root = w->saved_layout_root; w->saved_layout_root = NULL; + TAILQ_FOREACH(wp, &w->z_index, zentry) { + if (wp->flags & PANE_FLOATING) { + wp->flags &= ~PANE_MINIMISED | (wp->saved_flags & PANE_MINIMISED); + continue; + } + break; + } + TAILQ_FOREACH(wp, &w->panes, entry) { wp->layout_cell = wp->saved_layout_cell; wp->saved_layout_cell = NULL; @@ -781,9 +803,10 @@ window_add_pane(struct window *w, struct window_pane *other, u_int hlimit, TAILQ_INSERT_AFTER(&w->panes, other, wp, entry); } /* Floating panes are created above tiled planes. */ - if (flags & (SPAWN_FLOATING)) + if (flags & SPAWN_FLOATING) { + wp->flags |= PANE_FLOATING; TAILQ_INSERT_HEAD(&w->z_index, wp, zentry); - else + } else TAILQ_INSERT_TAIL(&w->z_index, wp, zentry); return (wp); } @@ -1311,8 +1334,10 @@ window_pane_key(struct window_pane *wp, struct client *c, struct session *s, int window_pane_visible(struct window_pane *wp) { - if (~wp->window->flags & WINDOW_ZOOMED) + if (~wp->window->flags & WINDOW_ZOOMED && + ~wp->flags & PANE_MINIMISED) return (1); + return (wp == wp->window->active); }