diff --git a/cmd-break-pane.c b/cmd-break-pane.c index 790b8df2..aad23334 100644 --- a/cmd-break-pane.c +++ b/cmd-break-pane.c @@ -96,6 +96,7 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item) } TAILQ_REMOVE(&w->panes, wp, entry); + TAILQ_REMOVE(&w->z_index, wp, zentry); server_client_remove_pane(wp); window_lost_pane(w, wp); layout_close_pane(wp); @@ -104,6 +105,7 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item) options_set_parent(wp->options, w->options); wp->flags |= (PANE_STYLECHANGED|PANE_THEMECHANGED); TAILQ_INSERT_HEAD(&w->panes, wp, entry); + TAILQ_INSERT_HEAD(&w->z_index, wp, zentry); w->active = wp; w->latest = tc; diff --git a/cmd-join-pane.c b/cmd-join-pane.c index 5b6abc51..4ab05e82 100644 --- a/cmd-join-pane.c +++ b/cmd-join-pane.c @@ -146,14 +146,18 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item) server_client_remove_pane(src_wp); window_lost_pane(src_w, src_wp); TAILQ_REMOVE(&src_w->panes, src_wp, entry); + TAILQ_REMOVE(&src_w->z_index, src_wp, zentry); src_wp->window = dst_w; options_set_parent(src_wp->options, dst_w->options); src_wp->flags |= (PANE_STYLECHANGED|PANE_THEMECHANGED); - if (flags & SPAWN_BEFORE) + if (flags & SPAWN_BEFORE) { TAILQ_INSERT_BEFORE(dst_wp, src_wp, entry); - else + TAILQ_INSERT_BEFORE(dst_wp, src_wp, zentry); + } else { TAILQ_INSERT_AFTER(&dst_w->panes, dst_wp, src_wp, entry); + TAILQ_INSERT_AFTER(&dst_w->z_index, dst_wp, src_wp, zentry); + } layout_assign_pane(lc, src_wp, 0); colour_palette_from_option(&src_wp->palette, src_wp->options); diff --git a/cmd-split-window.c b/cmd-split-window.c index 2a372ead..9585b888 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -38,11 +38,12 @@ const struct cmd_entry cmd_new_pane_entry = { .name = "new-pane", .alias = "newp", - .args = { "bc:de:fF:hIkl:m:p:PR:s:S:t:vZ", 0, -1, NULL }, + .args = { "bc:de:fF:hIkl:Lm:p:PR:s:S:t:vx:X:y:Y:Z", 0, -1, NULL }, .usage = "[-bdefhIklPvZ] [-c start-directory] [-e environment] " "[-F format] [-l size] [-m message] [-p percentage] " "[-s style] [-S active-border-style] " - "[-R inactive-border-style] " CMD_TARGET_PANE_USAGE " " + "[-R inactive-border-style] [-x width] [-y height] " + "[-X x-position] [-Y y-position] " CMD_TARGET_PANE_USAGE " " "[shell-command [argument ...]]", .target = { 't', CMD_FIND_PANE, 0 }, @@ -69,15 +70,48 @@ const struct cmd_entry cmd_split_window_entry = { }; static struct layout_cell * -cmd_split_window_get_tiled_layout_cell(struct cmdq_item *item, - struct args *args, struct window *w, struct window_pane *wp, int flags) +cmd_split_window_get_floating_cell(struct cmdq_item *item, struct args *args, + struct window *w, struct window_pane *wp) +{ + struct layout_cell *lc = NULL; + char *cause = NULL; + u_int x, y, sx, sy; + + if (window_pane_floating_geometry(w, wp, &x, &y, &sx, &sy, item, args, + &cause) != 0) { + cmdq_error(item, "invalid floating pane geometry %s", cause); + free(cause); + return (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; + + return (lc); +} + +static struct layout_cell * +cmd_split_window_get_tiled_cell(struct cmdq_item *item, struct args *args, + struct window *w, struct window_pane *wp, int flags) { enum layout_type type; struct layout_cell *lc = NULL; char *cause = NULL; int size; - if (window_pane_tile_geometry(w, wp, &size, &flags, &type, item, args, + if (wp->flags & PANE_FLOATING) { + cmdq_error(item, "can't split a floating pane"); + return (NULL); + } + + if (window_pane_tiled_geometry(w, wp, &size, &flags, &type, item, args, &cause) != 0) { cmdq_error(item, "invalid tiled geometry %s", cause); free(cause); @@ -88,6 +122,7 @@ cmd_split_window_get_tiled_layout_cell(struct cmdq_item *item, lc = layout_split_pane(wp, type, size, flags); if (lc == NULL) cmdq_error(item, "no space for new pane"); + return (lc); } @@ -105,15 +140,19 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) struct window_pane *wp = target->wp, *new_wp; struct layout_cell *lc = NULL; struct cmd_find_state fs; - int flags, input; + int input, is_floating, flags = 0; const char *template, *style; char *cause = NULL, *cp; struct args_value *av; u_int count = args_count(args); + if (cmd_get_entry(self) == &cmd_new_pane_entry) + is_floating = 0; /* !args_has(args, 'L'); */ + else + is_floating = 0; input = (args_has(args, 'I') && count == 0); - flags = 0; + flags = is_floating ? SPAWN_FLOATING : 0; if (args_has(args, 'b')) flags |= SPAWN_BEFORE; if (args_has(args, 'f')) @@ -121,7 +160,10 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) if (input || (count == 1 && *args_string(args, 0) == '\0')) flags |= SPAWN_EMPTY; - lc = cmd_split_window_get_tiled_layout_cell(item, args, w, wp, flags); + if (is_floating) + lc = cmd_split_window_get_floating_cell(item, args, w, wp); + else + lc = cmd_split_window_get_tiled_cell(item, args, w, wp, flags); if (lc == NULL) return (CMD_RETURN_ERROR); @@ -168,8 +210,8 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) } options_set_string(new_wp->options, "window-active-style", 0, "%s", style); - new_wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED - |PANE_THEMECHANGED); + new_wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED| + PANE_THEMECHANGED); } style = args_get(args, 'S'); if (style != NULL) { @@ -200,7 +242,8 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) switch (window_pane_start_input(new_wp, item, &cause)) { case -1: server_client_remove_pane(new_wp); - layout_close_pane(new_wp); + if (!is_floating) + layout_close_pane(new_wp); window_remove_pane(wp->window, new_wp); cmdq_error(item, "%s", cause); free(cause); @@ -215,8 +258,11 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) } if (!args_has(args, 'd')) cmd_find_from_winlink_pane(current, wl, new_wp, 0); - window_pop_zoom(wp->window); - server_redraw_window(wp->window); + + if (!is_floating) { + window_pop_zoom(wp->window); + server_redraw_window(wp->window); + } server_status_session(s); if (args_has(args, 'P')) { diff --git a/format.c b/format.c index 9735be0e..d6b02bf4 100644 --- a/format.c +++ b/format.c @@ -1184,9 +1184,9 @@ format_cb_pane_at_bottom(struct format_tree *ft) status = options_get_number(w->options, "pane-border-status"); if (status == PANE_STATUS_BOTTOM) - flag = (wp->yoff + wp->sy == w->sy - 1); + flag = (wp->yoff + (int)wp->sy == (int)w->sy - 1); else - flag = (wp->yoff + wp->sy == w->sy); + flag = (wp->yoff + (int)wp->sy == (int)w->sy); xasprintf(&value, "%d", flag); return (value); } @@ -2048,7 +2048,7 @@ static void * format_cb_pane_at_right(struct format_tree *ft) { if (ft->wp != NULL) { - if (ft->wp->xoff + ft->wp->sx == ft->wp->window->sx) + if (ft->wp->xoff + (int)ft->wp->sx == (int)ft->wp->window->sx) return (xstrdup("1")); return (xstrdup("0")); } @@ -2059,8 +2059,10 @@ format_cb_pane_at_right(struct format_tree *ft) static void * format_cb_pane_bottom(struct format_tree *ft) { - if (ft->wp != NULL) - return (format_printf("%d", ft->wp->yoff + ft->wp->sy - 1)); + struct window_pane *wp = ft->wp; + + if (wp != NULL) + return (format_printf("%d", wp->yoff + (int)wp->sy - 1)); return (NULL); } @@ -2342,8 +2344,10 @@ format_cb_pane_pb_state(struct format_tree *ft) static void * format_cb_pane_right(struct format_tree *ft) { - if (ft->wp != NULL) - return (format_printf("%d", ft->wp->xoff + ft->wp->sx - 1)); + struct window_pane *wp = ft->wp; + + if (wp != NULL) + return (format_printf("%d", wp->xoff + (int)wp->sx - 1)); return (NULL); } diff --git a/layout-custom.c b/layout-custom.c index aa36d2c4..054be77f 100644 --- a/layout-custom.c +++ b/layout-custom.c @@ -288,6 +288,15 @@ layout_parse(struct window *w, const char *layout, char **cause) if (floating_lc != NULL) layout_assign(&wp, floating_lc, PANE_FLOATING); + /* Fix pane Z indexes. */ + while (!TAILQ_EMPTY(&w->z_index)) { + wp = TAILQ_FIRST(&w->z_index); + TAILQ_REMOVE(&w->z_index, wp, zentry); + } + if (floating_lc != NULL) + 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); diff --git a/layout.c b/layout.c index 5afd697b..407c5a05 100644 --- a/layout.c +++ b/layout.c @@ -233,6 +233,30 @@ layout_make_node(struct layout_cell *lc, enum layout_type type) lc->wp = NULL; } +/* Fix Z indexes. */ +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) diff --git a/spawn.c b/spawn.c index 1c3c2a14..7219a67a 100644 --- a/spawn.c +++ b/spawn.c @@ -280,6 +280,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 @@ -372,7 +379,7 @@ spawn_pane(struct spawn_context *sc, char **cause) goto complete; } - /* Store current working directory and change to new one. */ + /* Store current working directory and change to new one. */ if (getcwd(path, sizeof path) != NULL) { if (chdir(new_wp->cwd) == 0) actual_cwd = new_wp->cwd; diff --git a/tmux.h b/tmux.h index 2c978046..a4e121af 100644 --- a/tmux.h +++ b/tmux.h @@ -1380,6 +1380,9 @@ struct window { u_int new_xpixel; u_int new_ypixel; + u_int last_new_pane_x; + u_int last_new_pane_y; + struct utf8_data *fill_character; int flags; #define WINDOW_BELL 0x1 @@ -3472,9 +3475,12 @@ enum client_theme window_pane_get_theme(struct window_pane *); void window_pane_send_theme_update(struct window_pane *); struct style_range *window_pane_border_status_get_range(struct window_pane *, u_int, u_int); -int window_pane_tile_geometry(struct window *, +int window_pane_tiled_geometry(struct window *, struct window_pane *, int *, int *, enum layout_type *, struct cmdq_item *, struct args *, char **); +int window_pane_floating_geometry(struct window *, + struct window_pane *, u_int *, u_int *, u_int *, u_int *, + struct cmdq_item *, struct args *, char **); /* layout.c */ u_int layout_count_cells(struct layout_cell *); @@ -3489,6 +3495,7 @@ struct layout_cell *layout_search_by_border(struct layout_cell *, u_int, u_int); void layout_set_size(struct layout_cell *, u_int, u_int, int, 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 *, diff --git a/window.c b/window.c index 9b631471..de6049ee 100644 --- a/window.c +++ b/window.c @@ -548,6 +548,7 @@ window_set_active_pane(struct window *w, struct window_pane *wp, int notify) } tty_update_window_offset(w); + server_redraw_window(w); if (notify) notify_window("window-pane-changed", w); @@ -594,7 +595,17 @@ window_redraw_active_switch(struct window *w, struct window_pane *wp) } if (wp == w->active) break; + + /* If the pane is floating, move to the front. */ + if (wp->flags & PANE_FLOATING) { + TAILQ_REMOVE(&w->z_index, wp, zentry); + TAILQ_INSERT_HEAD(&w->z_index, wp, zentry); + wp->flags |= PANE_REDRAW; + } + wp = w->active; + if (wp == NULL) + break; } } @@ -607,18 +618,32 @@ window_get_active_at(struct window *w, u_int x, u_int y) pane_status = options_get_number(w->options, "pane-border-status"); - TAILQ_FOREACH(wp, &w->panes, entry) { + TAILQ_FOREACH(wp, &w->z_index, zentry) { if (!window_pane_visible(wp)) continue; window_pane_full_size_offset(wp, &xoff, &yoff, &sx, &sy); - if ((int)x < xoff || x > xoff + sx) - continue; - if (pane_status == PANE_STATUS_TOP) { - if ((int)y <= yoff - 2 || y > yoff + sy - 1) + if (~wp->flags & PANE_FLOATING) { + /* Tiled - to and including bottom or right border. */ + if ((int)x < xoff || x > xoff + sx) continue; + if (pane_status == PANE_STATUS_TOP) { + if ((int)y < yoff - 1 || y > yoff + sy) + continue; + } else { + if ((int)y < yoff || y > yoff + sy) + continue; + } } else { - if ((int)y < yoff || y > yoff + sy) + /* Floating - include top or or left border. */ + if ((int)x < xoff - 1 || x > xoff + sx) continue; + if (pane_status == PANE_STATUS_TOP) { + if ((int)y <= yoff - 2 || y > yoff + sy - 1) + continue; + } else { + if ((int)y < yoff - 1 || y > yoff + sy) + continue; + } } return (wp); } @@ -674,7 +699,6 @@ window_zoom(struct window_pane *wp) if (w->flags & WINDOW_ZOOMED) return (-1); - if (window_count_panes(w, 1) == 1) return (-1); @@ -711,7 +735,7 @@ window_unzoom(struct window *w, int notify) TAILQ_FOREACH(wp, &w->panes, entry) { wp->layout_cell = wp->saved_layout_cell; wp->saved_layout_cell = NULL; - wp->flags ^= PANE_ZOOMED; + wp->flags &= ~PANE_ZOOMED; } layout_fix_panes(w, NULL); @@ -764,11 +788,17 @@ window_add_pane(struct window *w, struct window_pane *other, u_int hlimit, TAILQ_INSERT_BEFORE(other, wp, entry); } else { log_debug("%s: @%u after %%%u", __func__, w->id, wp->id); - if (flags & SPAWN_FULLSIZE) + if (flags & (SPAWN_FULLSIZE|SPAWN_FLOATING)) TAILQ_INSERT_TAIL(&w->panes, wp, entry); else TAILQ_INSERT_AFTER(&w->panes, other, wp, entry); } + if (~flags & SPAWN_FLOATING) + TAILQ_INSERT_TAIL(&w->z_index, wp, zentry); + else { + wp->flags |= PANE_FLOATING; + TAILQ_INSERT_HEAD(&w->z_index, wp, zentry); + } return (wp); } @@ -801,8 +831,8 @@ void window_remove_pane(struct window *w, struct window_pane *wp) { window_lost_pane(w, wp); - TAILQ_REMOVE(&w->panes, wp, entry); + TAILQ_REMOVE(&w->z_index, wp, zentry); window_pane_destroy(wp); } @@ -868,7 +898,7 @@ window_count_panes(struct window *w, int with_floating) u_int n = 0; TAILQ_FOREACH(wp, &w->panes, entry) { - if (with_floating || ~wp->flags & PANE_FLOATING) + if (with_floating || ~wp->flags & PANE_FLOATING) n++; } return (n); @@ -887,6 +917,7 @@ window_destroy_panes(struct window *w) while (!TAILQ_EMPTY(&w->panes)) { wp = TAILQ_FIRST(&w->panes); TAILQ_REMOVE(&w->panes, wp, entry); + TAILQ_REMOVE(&w->z_index, wp, zentry); window_pane_destroy(wp); } } @@ -924,7 +955,7 @@ window_pane_printable_flags(struct window_pane *wp) { struct window *w = wp->window; static char flags[32]; - u_int pos = 0; + int pos = 0; if (wp == w->active) flags[pos++] = '*'; @@ -1225,7 +1256,7 @@ window_pane_reset_mode_all(struct window_pane *wp) static void window_pane_copy_paste(struct window_pane *wp, char *buf, size_t len) { - struct window_pane *loop; + struct window_pane *loop; TAILQ_FOREACH(loop, &wp->window->panes, entry) { if (loop != wp && @@ -1243,7 +1274,7 @@ window_pane_copy_paste(struct window_pane *wp, char *buf, size_t len) static void window_pane_copy_key(struct window_pane *wp, key_code key) { - struct window_pane *loop; + struct window_pane *loop; TAILQ_FOREACH(loop, &wp->window->panes, entry) { if (loop != wp && @@ -2039,8 +2070,9 @@ window_pane_border_status_get_range(struct window_pane *wp, u_int x, u_int y) return (style_ranges_get_range(srs, x - wp->xoff - 2)); } +/* Work out geometry for tiled panes. */ int -window_pane_tile_geometry(struct window *w, struct window_pane *wp, +window_pane_tiled_geometry(struct window *w, struct window_pane *wp, int *out_size, int *out_flags, enum layout_type *out_type, struct cmdq_item *item, struct args *args, char **cause) { @@ -2088,3 +2120,62 @@ window_pane_tile_geometry(struct window *w, struct window_pane *wp, *out_type = type; return (0); } + +/* Work out geometry for floating panes. */ +int +window_pane_floating_geometry(struct window *w, __unused struct window_pane *wp, + u_int *out_x, u_int *out_y, u_int *out_sx, u_int *out_sy, + struct cmdq_item *item, struct args *args, char **cause) +{ + u_int x, y, sx = w->sx / 2, sy = w->sy / 2; + + if (args_has(args, 'x')) { + sx = args_percentage_and_expand(args, 'x', 0, USHRT_MAX, w->sx, + item, cause); + if (*cause != NULL) + return (-1); + } + if (args_has(args, 'y')) { + sy = args_percentage_and_expand(args, 'y', 0, USHRT_MAX, w->sy, + item, cause); + if (*cause != NULL) + return (-1); + } + + if (args_has(args, 'X')) { + x = args_percentage_and_expand(args, 'X', 0, USHRT_MAX, w->sx, + item, cause); + if (*cause != NULL) + return (-1); + } else { + if (w->last_new_pane_x == 0) + x = 4; + else { + x = w->last_new_pane_x + 4; + if (w->last_new_pane_x > w->sx) + x = 4; + } + w->last_new_pane_x = x; + } + if (args_has(args, 'Y')) { + y = args_percentage_and_expand(args, 'Y', 0, USHRT_MAX, w->sy, + item, cause); + if (*cause != NULL) + return (-1); + } else { + if (w->last_new_pane_y == 0) + y = 2; + else { + y = w->last_new_pane_y + 2; + if (w->last_new_pane_y > w->sy) + y = 2; + } + w->last_new_pane_y = y; + } + + *out_x = x; + *out_y = y; + *out_sx = sx; + *out_sy = sy; + return (0); +}