From 78287e27c87ccdda1f8580382b336a0d02c26a03 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 17 Apr 2019 14:37:48 +0000 Subject: [PATCH 1/9] Break new window and pane creation common code from various commands and window.c into a separate file spawn.c. --- Makefile | 1 + cmd-attach-session.c | 2 +- cmd-join-pane.c | 10 +- cmd-kill-session.c | 4 +- cmd-new-session.c | 107 ++++------- cmd-new-window.c | 103 +++------- cmd-queue.c | 2 +- cmd-respawn-pane.c | 60 +++--- cmd-respawn-window.c | 64 ++----- cmd-rotate-window.c | 4 +- cmd-select-pane.c | 4 +- cmd-split-window.c | 151 ++++++--------- cmd-swap-pane.c | 10 +- cmd-switch-client.c | 2 +- layout.c | 15 +- server-fn.c | 4 +- server.c | 2 +- session.c | 68 +------ spawn.c | 434 +++++++++++++++++++++++++++++++++++++++++++ tmux.h | 58 ++++-- window-tree.c | 2 +- window.c | 215 +++------------------ 22 files changed, 709 insertions(+), 613 deletions(-) create mode 100644 spawn.c diff --git a/Makefile b/Makefile index 99417ba2..aedabadf 100644 --- a/Makefile +++ b/Makefile @@ -104,6 +104,7 @@ SRCS= alerts.c \ server-fn.c \ server.c \ session.c \ + spawn.c \ status.c \ style.c \ tmux.c \ diff --git a/cmd-attach-session.c b/cmd-attach-session.c index 73ff530d..bcd6d895 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -87,7 +87,7 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag, if (wl != NULL) { if (wp != NULL) - window_set_active_pane(wp->window, wp); + window_set_active_pane(wp->window, wp, 1); session_set_current(s, wl); if (wp != NULL) cmd_find_from_winlink_pane(current, wl, wp, 0); diff --git a/cmd-join-pane.c b/cmd-join-pane.c index 79bc1c9d..ab245302 100644 --- a/cmd-join-pane.c +++ b/cmd-join-pane.c @@ -72,7 +72,7 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item) int size, percentage, dst_idx; enum layout_type type; struct layout_cell *lc; - int not_same_window; + int not_same_window, flags; if (self->entry == &cmd_join_pane_entry) not_same_window = 1; @@ -124,7 +124,11 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item) else size = (dst_wp->sx * percentage) / 100; } - lc = layout_split_pane(dst_wp, type, size, args_has(args, 'b'), 0); + if (args_has(args, 'b')) + flags = SPAWN_BEFORE; + else + flags = 0; + lc = layout_split_pane(dst_wp, type, size, flags); if (lc == NULL) { cmdq_error(item, "create pane failed: pane too small"); return (CMD_RETURN_ERROR); @@ -145,7 +149,7 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item) server_redraw_window(dst_w); if (!args_has(args, 'd')) { - window_set_active_pane(dst_w, src_wp); + window_set_active_pane(dst_w, src_wp, 1); session_select(dst_s, dst_idx); cmd_find_from_session(current, dst_s, 0); server_redraw_session(dst_s); diff --git a/cmd-kill-session.c b/cmd-kill-session.c index 00ea7789..dcef8097 100644 --- a/cmd-kill-session.c +++ b/cmd-kill-session.c @@ -61,12 +61,12 @@ cmd_kill_session_exec(struct cmd *self, struct cmdq_item *item) RB_FOREACH_SAFE(sloop, sessions, &sessions, stmp) { if (sloop != s) { server_destroy_session(sloop); - session_destroy(sloop, __func__); + session_destroy(sloop, 1, __func__); } } } else { server_destroy_session(s); - session_destroy(s, __func__); + session_destroy(s, 1, __func__); } return (CMD_RETURN_NORMAL); } diff --git a/cmd-new-session.c b/cmd-new-session.c index e1be350b..f0a353d8 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -69,20 +69,17 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) struct args *args = self->args; struct client *c = item->client; struct session *s, *as, *groupwith; - struct window *w; struct environ *env; struct options *oo; struct termios tio, *tiop; struct session_group *sg; - const char *errstr, *template, *group, *prefix; - const char *path, *cmd, *tmp, *value; - char **argv, *cause, *cp, *newname, *cwd = NULL; - int detached, already_attached, idx, argc; - int is_control = 0; - u_int sx, sy, dsx = 80, dsy = 24; - struct environ_entry *envent; - struct cmd_find_state fs; + const char *errstr, *template, *group, *prefix, *tmp; + char *cause, *cwd = NULL, *cp, *newname = NULL; + int detached, already_attached, is_control = 0; + u_int sx, sy, dsx, dsy; + struct spawn_context sc; enum cmd_retval retval; + struct cmd_find_state fs; if (self->entry == &cmd_has_session_entry) { /* @@ -97,13 +94,12 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_ERROR); } - newname = NULL; if (args_has(args, 's')) { newname = format_single(item, args_get(args, 's'), c, NULL, NULL, NULL); if (!session_check_name(newname)) { cmdq_error(item, "bad session name: %s", newname); - goto error; + goto fail; } if ((as = session_find(newname)) != NULL) { if (args_has(args, 'A')) { @@ -114,7 +110,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) return (retval); } cmdq_error(item, "duplicate session: %s", newname); - goto error; + goto fail; } } @@ -125,7 +121,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) if (groupwith == NULL) { if (!session_check_name(group)) { cmdq_error(item, "bad group name: %s", group); - goto error; + goto fail; } sg = session_group_find(group); } else @@ -173,7 +169,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) if (server_client_check_nested(item->client)) { cmdq_error(item, "sessions should be nested with care, " "unset $TMUX to force"); - return (CMD_RETURN_ERROR); + goto fail; } if (tcgetattr(c->tty.fd, &tio) != 0) fatal("tcgetattr failed"); @@ -186,7 +182,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) if (server_client_open(c, &cause) != 0) { cmdq_error(item, "open terminal failed: %s", cause); free(cause); - goto error; + goto fail; } } @@ -200,7 +196,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) dsx = strtonum(tmp, 1, USHRT_MAX, &errstr); if (errstr != NULL) { cmdq_error(item, "width %s", errstr); - goto error; + goto fail; } } } @@ -213,7 +209,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) dsy = strtonum(tmp, 1, USHRT_MAX, &errstr); if (errstr != NULL) { cmdq_error(item, "height %s", errstr); - goto error; + goto fail; } } } @@ -225,8 +221,8 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) if (sy > 0 && options_get_number(global_s_options, "status")) sy--; } else { - value = options_get_string(global_s_options, "default-size"); - if (sscanf(value, "%ux%u", &sx, &sy) != 2) { + tmp = options_get_string(global_s_options, "default-size"); + if (sscanf(tmp, "%ux%u", &sx, &sy) != 2) { sx = 80; sy = 24; } @@ -240,59 +236,34 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) if (sy == 0) sy = 1; - /* Figure out the command for the new window. */ - argc = -1; - argv = NULL; - if (!args_has(args, 't') && args->argc != 0) { - argc = args->argc; - argv = args->argv; - } else if (sg == NULL && groupwith == NULL) { - cmd = options_get_string(global_s_options, "default-command"); - if (cmd != NULL && *cmd != '\0') { - argc = 1; - argv = (char **)&cmd; - } else { - argc = 0; - argv = NULL; - } - } - - path = NULL; - if (c != NULL && c->session == NULL) - envent = environ_find(c->environ, "PATH"); - else - envent = environ_find(global_environ, "PATH"); - if (envent != NULL) - path = envent->value; - - /* Construct the environment. */ - env = environ_create(); - if (c != NULL && !args_has(args, 'E')) - environ_update(global_s_options, c->environ, env); - - /* Set up the options. */ + /* Create the new session. */ oo = options_create(global_s_options); if (args_has(args, 'x') || args_has(args, 'y')) options_set_string(oo, "default-size", 0, "%ux%u", dsx, dsy); + env = environ_create(); + if (c != NULL && !args_has(args, 'E')) + environ_update(global_s_options, c->environ, env); + s = session_create(prefix, newname, cwd, env, oo, tiop); - /* Create the new session. */ - idx = -1 - options_get_number(global_s_options, "base-index"); - s = session_create(prefix, newname, argc, argv, path, cwd, env, oo, - tiop, idx, &cause); - environ_free(env); - if (s == NULL) { - cmdq_error(item, "create session failed: %s", cause); + /* Spawn the initial window. */ + memset(&sc, 0, sizeof sc); + sc.item = item; + sc.s = s; + + sc.name = args_get(args, 'n'); + sc.argc = args->argc; + sc.argv = args->argv; + + sc.idx = -1; + sc.cwd = args_get(args, 'c'); + + sc.flags = 0; + + if (spawn_window(&sc, &cause) == NULL) { + session_destroy(s, 0, __func__); + cmdq_error(item, "create window failed: %s", cause); free(cause); - goto error; - } - - /* Set the initial window name if one given. */ - if (argc >= 0 && (tmp = args_get(args, 'n')) != NULL) { - cp = format_single(item, tmp, c, s, NULL, NULL); - w = s->curw->window; - window_set_name(w, cp); - options_set_number(w->options, "automatic-rename", 0); - free(cp); + goto fail; } /* @@ -364,7 +335,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) free(newname); return (CMD_RETURN_NORMAL); -error: +fail: free(cwd); free(newname); return (CMD_RETURN_ERROR); diff --git a/cmd-new-window.c b/cmd-new-window.c index 5baeff65..e3bacff8 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -53,87 +53,45 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = self->args; struct cmd_find_state *current = &item->shared->current; + struct spawn_context sc; + struct client *c = cmd_find_client(item, NULL, 1); struct session *s = item->target.s; struct winlink *wl = item->target.wl; - struct client *c = cmd_find_client(item, NULL, 1); int idx = item->target.idx; - const char *cmd, *path, *template, *tmp; - char **argv, *cause, *cp, *cwd, *name; - int argc, detached; - struct environ_entry *envent; + struct winlink *new_wl; + char *cause = NULL, *cp; + const char *template; struct cmd_find_state fs; - if (args_has(args, 'a') && wl != NULL) { - if ((idx = winlink_shuffle_up(s, wl)) == -1) { - cmdq_error(item, "no free window indexes"); - return (CMD_RETURN_ERROR); - } - } - detached = args_has(args, 'd'); - - if (args->argc == 0) { - cmd = options_get_string(s->options, "default-command"); - if (cmd != NULL && *cmd != '\0') { - argc = 1; - argv = (char **)&cmd; - } else { - argc = 0; - argv = NULL; - } - } else { - argc = args->argc; - argv = args->argv; + if (args_has(args, 'a') && (idx = winlink_shuffle_up(s, wl)) == -1) { + cmdq_error(item, "couldn't get a window index"); + return (CMD_RETURN_ERROR); } - path = NULL; - if (item->client != NULL && item->client->session == NULL) - envent = environ_find(item->client->environ, "PATH"); - else - envent = environ_find(s->environ, "PATH"); - if (envent != NULL) - path = envent->value; + memset(&sc, 0, sizeof sc); + sc.item = item; + sc.s = s; - if ((tmp = args_get(args, 'c')) != NULL) - cwd = format_single(item, tmp, c, s, NULL, NULL); - else - cwd = xstrdup(server_client_get_cwd(item->client, s)); + sc.name = args_get(args, 'n'); + sc.argc = args->argc; + sc.argv = args->argv; - if ((tmp = args_get(args, 'n')) != NULL) - name = format_single(item, tmp, c, s, NULL, NULL); - else - name = NULL; + sc.idx = idx; + sc.cwd = args_get(args, 'c'); - if (idx != -1) - wl = winlink_find_by_index(&s->windows, idx); - if (wl != NULL && args_has(args, 'k')) { - /* - * Can't use session_detach as it will destroy session if this - * makes it empty. - */ - notify_session_window("window-unlinked", s, wl->window); - wl->flags &= ~WINLINK_ALERTFLAGS; - winlink_stack_remove(&s->lastw, wl); - winlink_remove(&s->windows, wl); + sc.flags = 0; + if (args_has(args, 'd')) + sc.flags |= SPAWN_DETACHED; + if (args_has(args, 'k')) + sc.flags |= SPAWN_KILL; - /* Force select/redraw if current. */ - if (wl == s->curw) { - detached = 0; - s->curw = NULL; - } - } - - if (idx == -1) - idx = -1 - options_get_number(s->options, "base-index"); - wl = session_new(s, name, argc, argv, path, cwd, idx, - &cause); - if (wl == NULL) { + if ((new_wl = spawn_window(&sc, &cause)) == NULL) { cmdq_error(item, "create window failed: %s", cause); free(cause); - goto error; + return (CMD_RETURN_ERROR); } - if (!detached) { - session_select(s, wl->idx); - cmd_find_from_winlink(current, wl, 0); + if (!args_has(args, 'd') || new_wl == s->curw) { + cmd_find_from_winlink(current, new_wl, 0); server_redraw_session_group(s); } else server_status_session_group(s); @@ -141,20 +99,13 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item) if (args_has(args, 'P')) { if ((template = args_get(args, 'F')) == NULL) template = NEW_WINDOW_TEMPLATE; - cp = format_single(item, template, c, s, wl, NULL); + cp = format_single(item, template, c, s, new_wl, NULL); cmdq_print(item, "%s", cp); free(cp); } - cmd_find_from_winlink(&fs, wl, 0); + cmd_find_from_winlink(&fs, new_wl, 0); hooks_insert(s->hooks, item, &fs, "after-new-window"); - free(name); - free(cwd); return (CMD_RETURN_NORMAL); - -error: - free(name); - free(cwd); - return (CMD_RETURN_ERROR); } diff --git a/cmd-queue.c b/cmd-queue.c index 97b3c1c9..03fd5f10 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -111,7 +111,7 @@ cmdq_remove(struct cmdq_item *item) if (item->client != NULL) server_client_unref(item->client); - if (item->type == CMDQ_COMMAND) + if (item->cmdlist != NULL) cmd_list_free(item->cmdlist); TAILQ_REMOVE(item->queue, item, entry); diff --git a/cmd-respawn-pane.c b/cmd-respawn-pane.c index eb4a7e09..44234315 100644 --- a/cmd-respawn-pane.c +++ b/cmd-respawn-pane.c @@ -20,7 +20,7 @@ #include #include -#include +#include #include "tmux.h" @@ -36,7 +36,7 @@ const struct cmd_entry cmd_respawn_pane_entry = { .args = { "c:kt:", 0, -1 }, .usage = "[-c start-directory] [-k] " CMD_TARGET_PANE_USAGE - " [command]", + " [command]", .target = { 't', CMD_FIND_PANE, 0 }, @@ -48,53 +48,39 @@ static enum cmd_retval cmd_respawn_pane_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = self->args; - struct winlink *wl = item->target.wl; - struct window *w = wl->window; - struct window_pane *wp = item->target.wp; - struct client *c = cmd_find_client(item, NULL, 1); + struct spawn_context sc; struct session *s = item->target.s; - struct environ *env; - const char *path = NULL, *cp; - char *cause, *cwd = NULL; - u_int idx; - struct environ_entry *envent; + struct winlink *wl = item->target.wl; + struct window_pane *wp = item->target.wp; + char *cause = NULL; - if (!args_has(self->args, 'k') && wp->fd != -1) { - if (window_pane_index(wp, &idx) != 0) - fatalx("index not found"); - cmdq_error(item, "pane still active: %s:%d.%u", - s->name, wl->idx, idx); - return (CMD_RETURN_ERROR); - } + memset(&sc, 0, sizeof sc); + sc.item = item; + sc.s = s; + sc.wl = wl; - window_pane_reset_mode_all(wp); - screen_reinit(&wp->base); - input_init(wp); + sc.wp0 = wp; + sc.lc = NULL; - if (item->client != NULL && item->client->session == NULL) - envent = environ_find(item->client->environ, "PATH"); - else - envent = environ_find(s->environ, "PATH"); - if (envent != NULL) - path = envent->value; + sc.name = NULL; + sc.argc = args->argc; + sc.argv = args->argv; - if ((cp = args_get(args, 'c')) != NULL) - cwd = format_single(item, cp, c, s, NULL, NULL); + sc.idx = -1; + sc.cwd = args_get(args, 'c'); - env = environ_for_session(s, 0); - if (window_pane_spawn(wp, args->argc, args->argv, path, NULL, cwd, env, - s->tio, &cause) != 0) { + sc.flags = SPAWN_RESPAWN; + if (args_has(args, 'k')) + sc.flags |= SPAWN_KILL; + + if (spawn_pane(&sc, &cause) == NULL) { cmdq_error(item, "respawn pane failed: %s", cause); free(cause); - environ_free(env); - free(cwd); return (CMD_RETURN_ERROR); } - environ_free(env); - free(cwd); wp->flags |= PANE_REDRAW; - server_status_window(w); + server_status_window(wp->window); return (CMD_RETURN_NORMAL); } diff --git a/cmd-respawn-window.c b/cmd-respawn-window.c index 68791990..2ccb2fde 100644 --- a/cmd-respawn-window.c +++ b/cmd-respawn-window.c @@ -19,7 +19,7 @@ #include #include -#include +#include #include "tmux.h" @@ -48,64 +48,34 @@ static enum cmd_retval cmd_respawn_window_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = self->args; + struct spawn_context sc; struct session *s = item->target.s; struct winlink *wl = item->target.wl; - struct window *w = wl->window; - struct window_pane *wp; - struct client *c = cmd_find_client(item, NULL, 1); - struct environ *env; - const char *path = NULL, *cp; - char *cause, *cwd = NULL; - struct environ_entry *envent; + char *cause = NULL; - if (!args_has(self->args, 'k')) { - TAILQ_FOREACH(wp, &w->panes, entry) { - if (wp->fd == -1) - continue; - cmdq_error(item, "window still active: %s:%d", s->name, - wl->idx); - return (CMD_RETURN_ERROR); - } - } + memset(&sc, 0, sizeof sc); + sc.item = item; + sc.s = s; + sc.wl = wl; - wp = TAILQ_FIRST(&w->panes); - TAILQ_REMOVE(&w->panes, wp, entry); - layout_free(w); - window_destroy_panes(w); - TAILQ_INSERT_HEAD(&w->panes, wp, entry); - window_pane_resize(wp, w->sx, w->sy); + sc.name = NULL; + sc.argc = args->argc; + sc.argv = args->argv; - if (item->client != NULL && item->client->session == NULL) - envent = environ_find(item->client->environ, "PATH"); - else - envent = environ_find(s->environ, "PATH"); - if (envent != NULL) - path = envent->value; + sc.idx = -1; + sc.cwd = args_get(args, 'c'); - if ((cp = args_get(args, 'c')) != NULL) - cwd = format_single(item, cp, c, s, NULL, NULL); + sc.flags = SPAWN_RESPAWN; + if (args_has(args, 'k')) + sc.flags |= SPAWN_KILL; - env = environ_for_session(s, 0); - if (window_pane_spawn(wp, args->argc, args->argv, path, NULL, cwd, env, - s->tio, &cause) != 0) { + if (spawn_window(&sc, &cause) == NULL) { cmdq_error(item, "respawn window failed: %s", cause); free(cause); - environ_free(env); - free(cwd); - server_destroy_pane(wp, 0); return (CMD_RETURN_ERROR); } - environ_free(env); - free(cwd); - layout_init(w, wp); - window_pane_reset_mode_all(wp); - screen_reinit(&wp->base); - input_init(wp); - window_set_active_pane(w, wp); - - recalculate_sizes(); - server_redraw_window(w); + server_redraw_window(wl->window); return (CMD_RETURN_NORMAL); } diff --git a/cmd-rotate-window.c b/cmd-rotate-window.c index 5a900a13..6dc0f2a8 100644 --- a/cmd-rotate-window.c +++ b/cmd-rotate-window.c @@ -77,7 +77,7 @@ cmd_rotate_window_exec(struct cmd *self, struct cmdq_item *item) if ((wp = TAILQ_PREV(w->active, window_panes, entry)) == NULL) wp = TAILQ_LAST(&w->panes, window_panes); - window_set_active_pane(w, wp); + window_set_active_pane(w, wp, 1); cmd_find_from_winlink_pane(current, wl, wp, 0); server_redraw_window(w); } else { @@ -105,7 +105,7 @@ cmd_rotate_window_exec(struct cmd *self, struct cmdq_item *item) if ((wp = TAILQ_NEXT(w->active, entry)) == NULL) wp = TAILQ_FIRST(&w->panes); - window_set_active_pane(w, wp); + window_set_active_pane(w, wp, 1); cmd_find_from_winlink_pane(current, wl, wp, 0); server_redraw_window(w); } diff --git a/cmd-select-pane.c b/cmd-select-pane.c index 2873737f..af0f033b 100644 --- a/cmd-select-pane.c +++ b/cmd-select-pane.c @@ -112,7 +112,7 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) else { server_unzoom_window(w); window_redraw_active_switch(w, lastwp); - if (window_set_active_pane(w, lastwp)) { + if (window_set_active_pane(w, lastwp, 1)) { cmd_find_from_winlink(current, wl, 0); cmd_select_pane_redraw(w); } @@ -194,7 +194,7 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_NORMAL); server_unzoom_window(wp->window); window_redraw_active_switch(w, wp); - if (window_set_active_pane(w, wp)) { + if (window_set_active_pane(w, wp, 1)) { cmd_find_from_winlink_pane(current, wl, wp, 0); hooks_insert(s->hooks, item, current, "after-select-pane"); cmd_select_pane_redraw(w); diff --git a/cmd-split-window.c b/cmd-split-window.c index 28821413..f8cab9f9 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -53,111 +53,87 @@ const struct cmd_entry cmd_split_window_entry = { static enum cmd_retval cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) { - struct cmd_find_state *current = &item->shared->current; struct args *args = self->args; + struct cmd_find_state *current = &item->shared->current; + struct spawn_context sc; struct client *c = cmd_find_client(item, NULL, 1); struct session *s = item->target.s; struct winlink *wl = item->target.wl; - struct window *w = wl->window; - struct window_pane *wp = item->target.wp, *new_wp = NULL; - struct environ *env; - const char *cmd, *path, *shell, *template, *tmp; - char **argv, *cause, *new_cause, *cp, *cwd; - u_int hlimit; - int argc, size, percentage, before; + struct window_pane *wp = item->target.wp, *new_wp; enum layout_type type; struct layout_cell *lc; - struct environ_entry *envent; - struct cmd_find_state fs; + struct cmd_find_state fs; + int size, percentage, flags; + const char *template; + char *cause, *cp; - server_unzoom_window(w); - - if (args->argc == 0) { - cmd = options_get_string(s->options, "default-command"); - if (cmd != NULL && *cmd != '\0') { - argc = 1; - argv = (char **)&cmd; - } else { - argc = 0; - argv = NULL; - } - } else { - argc = args->argc; - argv = args->argv; - } - - if ((tmp = args_get(args, 'c')) != NULL) - cwd = format_single(item, tmp, c, s, NULL, NULL); - else - cwd = xstrdup(server_client_get_cwd(item->client, s)); - - type = LAYOUT_TOPBOTTOM; if (args_has(args, 'h')) type = LAYOUT_LEFTRIGHT; - before = args_has(args, 'b'); - - size = -1; + else + type = LAYOUT_TOPBOTTOM; if (args_has(args, 'l')) { size = args_strtonum(args, 'l', 0, INT_MAX, &cause); if (cause != NULL) { - xasprintf(&new_cause, "size %s", cause); + cmdq_error(item, "create pane failed: -l %s", cause); free(cause); - cause = new_cause; - goto error; + return (CMD_RETURN_ERROR); } } else if (args_has(args, 'p')) { percentage = args_strtonum(args, 'p', 0, INT_MAX, &cause); if (cause != NULL) { - xasprintf(&new_cause, "percentage %s", cause); + cmdq_error(item, "create pane failed: -p %s", cause); free(cause); - cause = new_cause; - goto error; + return (CMD_RETURN_ERROR); } if (type == LAYOUT_TOPBOTTOM) size = (wp->sy * percentage) / 100; else size = (wp->sx * percentage) / 100; - } - hlimit = options_get_number(s->options, "history-limit"); - - shell = options_get_string(s->options, "default-shell"); - if (*shell == '\0' || areshell(shell)) - shell = _PATH_BSHELL; - - lc = layout_split_pane(wp, type, size, before, args_has(args, 'f')); - if (lc == NULL) { - cause = xstrdup("pane too small"); - goto error; - } - new_wp = window_add_pane(w, wp, before, args_has(args, 'f'), hlimit); - layout_make_leaf(lc, new_wp); - - path = NULL; - if (item->client != NULL && item->client->session == NULL) - envent = environ_find(item->client->environ, "PATH"); - else - envent = environ_find(s->environ, "PATH"); - if (envent != NULL) - path = envent->value; - - env = environ_for_session(s, 0); - if (window_pane_spawn(new_wp, argc, argv, path, shell, cwd, env, - s->tio, &cause) != 0) { - environ_free(env); - goto error; - } - environ_free(env); - - layout_fix_panes(w); - server_redraw_window(w); - - if (!args_has(args, 'd')) { - window_set_active_pane(w, new_wp); - session_select(s, wl->idx); - cmd_find_from_session(current, s, 0); - server_redraw_session(s); } else - server_status_session(s); + size = -1; + + server_unzoom_window(wp->window); + + flags = 0; + if (args_has(args, 'b')) + flags |= SPAWN_BEFORE; + if (args_has(args, 'f')) + flags |= SPAWN_FULLSIZE; + + lc = layout_split_pane(wp, type, size, flags); + if (lc == NULL) { + cmdq_error(item, "no space for new pane"); + return (CMD_RETURN_ERROR); + } + + memset(&sc, 0, sizeof sc); + sc.item = item; + sc.s = s; + sc.wl = wl; + + sc.wp0 = wp; + sc.lc = lc; + + sc.name = NULL; + sc.argc = args->argc; + sc.argv = args->argv; + + sc.idx = -1; + sc.cwd = args_get(args, 'c'); + + sc.flags = flags; + if (args_has(args, 'd')) + sc.flags |= SPAWN_DETACHED; + + if ((new_wp = spawn_pane(&sc, &cause)) == NULL) { + cmdq_error(item, "create pane failed: %s", cause); + free(cause); + return (CMD_RETURN_ERROR); + } + if (!args_has(args, 'd')) + cmd_find_from_winlink_pane(current, wl, new_wp, 0); + server_redraw_window(wp->window); + server_status_session(s); if (args_has(args, 'P')) { if ((template = args_get(args, 'F')) == NULL) @@ -166,22 +142,9 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) cmdq_print(item, "%s", cp); free(cp); } - notify_window("window-layout-changed", w); cmd_find_from_winlink_pane(&fs, wl, new_wp, 0); hooks_insert(s->hooks, item, &fs, "after-split-window"); - free(cwd); return (CMD_RETURN_NORMAL); - -error: - if (new_wp != NULL) { - layout_close_pane(new_wp); - window_remove_pane(w, new_wp); - } - cmdq_error(item, "create pane failed: %s", cause); - free(cause); - - free(cwd); - return (CMD_RETURN_ERROR); } diff --git a/cmd-swap-pane.c b/cmd-swap-pane.c index 1de272c4..2ad05561 100644 --- a/cmd-swap-pane.c +++ b/cmd-swap-pane.c @@ -101,17 +101,17 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item) if (!args_has(self->args, 'd')) { if (src_w != dst_w) { - window_set_active_pane(src_w, dst_wp); - window_set_active_pane(dst_w, src_wp); + window_set_active_pane(src_w, dst_wp, 1); + window_set_active_pane(dst_w, src_wp, 1); } else { tmp_wp = dst_wp; - window_set_active_pane(src_w, tmp_wp); + window_set_active_pane(src_w, tmp_wp, 1); } } else { if (src_w->active == src_wp) - window_set_active_pane(src_w, dst_wp); + window_set_active_pane(src_w, dst_wp, 1); if (dst_w->active == dst_wp) - window_set_active_pane(dst_w, src_wp); + window_set_active_pane(dst_w, src_wp, 1); } if (src_w != dst_w) { if (src_w->last == src_wp) diff --git a/cmd-switch-client.c b/cmd-switch-client.c index 3e19346e..a4f55102 100644 --- a/cmd-switch-client.c +++ b/cmd-switch-client.c @@ -116,7 +116,7 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item) server_unzoom_window(wl->window); if (wp != NULL) { window_redraw_active_switch(wp->window, wp); - window_set_active_pane(wp->window, wp); + window_set_active_pane(wp->window, wp, 1); } session_set_current(s, wl); cmd_find_from_session(&item->shared->current, s, 0); diff --git a/layout.c b/layout.c index 7093c607..1a43014d 100644 --- a/layout.c +++ b/layout.c @@ -831,11 +831,12 @@ layout_resize_child_cells(struct window *w, struct layout_cell *lc) */ struct layout_cell * layout_split_pane(struct window_pane *wp, enum layout_type type, int size, - int insert_before, int full_size) + int flags) { struct layout_cell *lc, *lcparent, *lcnew, *lc1, *lc2; u_int sx, sy, xoff, yoff, size1, size2; u_int new_size, saved_size, resize_first = 0; + int full_size = (flags & SPAWN_FULLSIZE); /* * If full_size is specified, add a new cell at the top of the window @@ -876,7 +877,7 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size, saved_size = sy; if (size < 0) size2 = ((saved_size + 1) / 2) - 1; - else if (insert_before) + else if (flags & SPAWN_BEFORE) size2 = saved_size - size - 1; else size2 = size; @@ -887,7 +888,7 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size, size1 = saved_size - 1 - size2; /* Which size are we using? */ - if (insert_before) + if (flags & SPAWN_BEFORE) new_size = size2; else new_size = size1; @@ -903,7 +904,7 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size, */ lcparent = lc->parent; lcnew = layout_create_cell(lcparent); - if (insert_before) + if (flags & SPAWN_BEFORE) TAILQ_INSERT_BEFORE(lc, lcnew, entry); else TAILQ_INSERT_AFTER(&lcparent->cells, lc, lcnew, entry); @@ -932,7 +933,7 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size, layout_set_size(lcnew, size, sy, 0, 0); else if (lc->type == LAYOUT_TOPBOTTOM) layout_set_size(lcnew, sx, size, 0, 0); - if (insert_before) + if (flags & SPAWN_BEFORE) TAILQ_INSERT_HEAD(&lc->cells, lcnew, entry); else TAILQ_INSERT_TAIL(&lc->cells, lcnew, entry); @@ -956,12 +957,12 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size, /* Create the new child cell. */ lcnew = layout_create_cell(lcparent); - if (insert_before) + if (flags & SPAWN_BEFORE) TAILQ_INSERT_HEAD(&lcparent->cells, lcnew, entry); else TAILQ_INSERT_TAIL(&lcparent->cells, lcnew, entry); } - if (insert_before) { + if (flags & SPAWN_BEFORE) { lc1 = lcnew; lc2 = lc; } else { diff --git a/server-fn.c b/server-fn.c index f947d8ff..c4ff4aa9 100644 --- a/server-fn.c +++ b/server-fn.c @@ -368,7 +368,7 @@ server_destroy_session_group(struct session *s) else { TAILQ_FOREACH_SAFE(s, &sg->sessions, gentry, s1) { server_destroy_session(s); - session_destroy(s, __func__); + session_destroy(s, 1, __func__); } } } @@ -435,7 +435,7 @@ server_check_unattached(void) if (s->attached != 0) continue; if (options_get_number (s->options, "destroy-unattached")) - session_destroy(s, __func__); + session_destroy(s, 1, __func__); } } diff --git a/server.c b/server.c index dd09c3c4..3c54f453 100644 --- a/server.c +++ b/server.c @@ -297,7 +297,7 @@ server_send_exit(void) } RB_FOREACH_SAFE(s, sessions, &sessions, s1) - session_destroy(s, __func__); + session_destroy(s, 1, __func__); } /* Update socket execute permissions based on whether sessions are attached. */ diff --git a/session.c b/session.c index 43ca321a..dddf18b0 100644 --- a/session.c +++ b/session.c @@ -112,12 +112,10 @@ session_find_by_id(u_int id) /* Create a new session. */ struct session * -session_create(const char *prefix, const char *name, int argc, char **argv, - const char *path, const char *cwd, struct environ *env, struct options *oo, - struct termios *tio, int idx, char **cause) +session_create(const char *prefix, const char *name, const char *cwd, + struct environ *env, struct options *oo, struct termios *tio) { struct session *s; - struct winlink *wl; s = xcalloc(1, sizeof *s); s->references = 1; @@ -129,10 +127,7 @@ session_create(const char *prefix, const char *name, int argc, char **argv, TAILQ_INIT(&s->lastw); RB_INIT(&s->windows); - s->environ = environ_create(); - if (env != NULL) - environ_copy(env, s->environ); - + s->environ = env; s->options = oo; s->hooks = hooks_create(global_hooks); @@ -166,17 +161,6 @@ session_create(const char *prefix, const char *name, int argc, char **argv, fatal("gettimeofday failed"); session_update_activity(s, &s->creation_time); - if (argc >= 0) { - wl = session_new(s, NULL, argc, argv, path, cwd, idx, cause); - if (wl == NULL) { - session_destroy(s, __func__); - return (NULL); - } - session_select(s, RB_ROOT(&s->windows)->idx); - } - - log_debug("session %s created", s->name); - return (s); } @@ -220,7 +204,7 @@ session_free(__unused int fd, __unused short events, void *arg) /* Destroy a session. */ void -session_destroy(struct session *s, const char *from) +session_destroy(struct session *s, int notify, const char *from) { struct winlink *wl; @@ -228,7 +212,8 @@ session_destroy(struct session *s, const char *from) s->curw = NULL; RB_REMOVE(sessions, &sessions, s); - notify_session("session-closed", s); + if (notify) + notify_session("session-closed", s); free(s->tio); @@ -338,45 +323,6 @@ session_previous_session(struct session *s) return (s2); } -/* Create a new window on a session. */ -struct winlink * -session_new(struct session *s, const char *name, int argc, char **argv, - const char *path, const char *cwd, int idx, char **cause) -{ - struct window *w; - struct winlink *wl; - struct environ *env; - const char *shell; - u_int hlimit, sx, sy; - - if ((wl = winlink_add(&s->windows, idx)) == NULL) { - xasprintf(cause, "index in use: %d", idx); - return (NULL); - } - wl->session = s; - - shell = options_get_string(s->options, "default-shell"); - if (*shell == '\0' || areshell(shell)) - shell = _PATH_BSHELL; - - default_window_size(s, NULL, &sx, &sy, -1); - hlimit = options_get_number(s->options, "history-limit"); - env = environ_for_session(s, 0); - w = window_create_spawn(name, argc, argv, path, shell, cwd, env, s->tio, - sx, sy, hlimit, cause); - if (w == NULL) { - winlink_remove(&s->windows, wl); - environ_free(env); - return (NULL); - } - winlink_set_window(wl, w); - environ_free(env); - notify_session_window("window-linked", s, w); - - session_group_synchronize_from(s); - return (wl); -} - /* Attach a window to a session. */ struct winlink * session_attach(struct session *s, struct window *w, int idx, char **cause) @@ -412,7 +358,7 @@ session_detach(struct session *s, struct winlink *wl) session_group_synchronize_from(s); if (RB_EMPTY(&s->windows)) { - session_destroy(s, __func__); + session_destroy(s, 1, __func__); return (1); } return (0); diff --git a/spawn.c b/spawn.c new file mode 100644 index 00000000..0565fee3 --- /dev/null +++ b/spawn.c @@ -0,0 +1,434 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2019 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "tmux.h" + +/* + * Set up the environment and create a new window and pane or a new pane. + * + * We need to set up the following items: + * + * - history limit, comes from the session; + * + * - base index, comes from the session; + * + * - current working directory, may be specified - if it isn't it comes from + * either the client or the session; + * + * - PATH variable, comes from the client if any, otherwise from the session + * environment; + * + * - shell, comes from default-shell; + * + * - termios, comes from the session; + * + * - remaining environment, comes from the session. + */ + +static void +spawn_log(const char *from, struct spawn_context *sc) +{ + struct session *s = sc->s; + struct winlink *wl = sc->wl; + struct window_pane *wp0 = sc->wp0; + char tmp[128]; + const char *name; + + log_debug("%s: %s, flags=%#x", from, sc->item->name, sc->flags); + + if (wl != NULL && wp0 != NULL) + xsnprintf(tmp, sizeof tmp, "wl=%d wp0=%%%u", wl->idx, wp0->id); + else if (wl != NULL) + xsnprintf(tmp, sizeof tmp, "wl=%d wp0=none", wl->idx); + else if (wp0 != NULL) + xsnprintf(tmp, sizeof tmp, "wl=none wp0=%%%u", wp0->id); + else + xsnprintf(tmp, sizeof tmp, "wl=none wp0=none"); + log_debug("%s: s=$%u %s idx=%d", from, s->id, tmp, sc->idx); + + name = sc->name; + if (name == NULL) + name = "none"; + log_debug("%s: name=%s", from, name); +} + +struct winlink * +spawn_window(struct spawn_context *sc, char **cause) +{ + struct session *s = sc->s; + struct window *w; + struct window_pane *wp; + struct winlink *wl; + int idx = sc->idx; + u_int sx, sy; + + spawn_log(__func__, sc); + + /* + * If the window already exists, we are respawning, so destroy all the + * panes except one. + */ + if (sc->flags & SPAWN_RESPAWN) { + w = sc->wl->window; + if (~sc->flags & SPAWN_KILL) { + TAILQ_FOREACH(wp, &w->panes, entry) { + if (wp->fd != -1) + break; + } + if (wp != NULL) { + xasprintf(cause, "window %s:%d still active", + s->name, sc->wl->idx); + return (NULL); + } + } + + sc->wp0 = TAILQ_FIRST(&w->panes); + TAILQ_REMOVE(&w->panes, sc->wp0, entry); + + layout_free(w); + window_destroy_panes(w); + + TAILQ_INSERT_HEAD(&w->panes, sc->wp0, entry); + window_pane_resize(sc->wp0, w->sx, w->sy); + + layout_init(w, sc->wp0); + window_set_active_pane(w, sc->wp0, 0); + } + + /* + * Otherwise we have no window so we will need to create one. First + * check if the given index already exists and destroy it if so. + */ + if ((~sc->flags & SPAWN_RESPAWN) && idx != -1) { + wl = winlink_find_by_index(&s->windows, idx); + if (wl != NULL && (~sc->flags & SPAWN_KILL)) { + xasprintf(cause, "index %d in use", idx); + return (NULL); + } + if (wl != NULL) { + /* + * Can't use session_detach as it will destroy session + * if this makes it empty. + */ + wl->flags &= ~WINLINK_ALERTFLAGS; + notify_session_window("window-unlinked", s, wl->window); + winlink_stack_remove(&s->lastw, wl); + winlink_remove(&s->windows, wl); + + if (s->curw == wl) { + s->curw = NULL; + sc->flags &= ~SPAWN_DETACHED; + } + } + } + + /* Then create a window if needed. */ + if (~sc->flags & SPAWN_RESPAWN) { + if (idx == -1) + idx = -1 - options_get_number(s->options, "base-index"); + if ((sc->wl = winlink_add(&s->windows, idx)) == NULL) { + xasprintf(cause, "couldn't add window %d", idx); + return (NULL); + } + default_window_size(s, NULL, &sx, &sy, -1); + if ((w = window_create(sx, sy)) == NULL) { + winlink_remove(&s->windows, sc->wl); + xasprintf(cause, "couldn't create window %d", idx); + return (NULL); + } + sc->wl->session = s; + winlink_set_window(sc->wl, w); + } else + w = NULL; + sc->flags |= SPAWN_NONOTIFY; + + /* Spawn the pane. */ + wp = spawn_pane(sc, cause); + if (wp == NULL) { + if (~sc->flags & SPAWN_RESPAWN) { + window_destroy(w); + winlink_remove(&s->windows, sc->wl); + } + return (NULL); + } + + /* Set the name of the new window. */ + if (~sc->flags & SPAWN_RESPAWN) { + if (sc->name != NULL) { + w->name = xstrdup(sc->name); + options_set_number(w->options, "automatic-rename", 0); + } else + w->name = xstrdup(default_window_name(w)); + } + + /* Switch to the new window if required. */ + if (~sc->flags & SPAWN_DETACHED) + session_select(s, sc->wl->idx); + + /* Fire notification if new window. */ + if (~sc->flags & SPAWN_RESPAWN) + notify_session_window("window-linked", s, w); + + session_group_synchronize_from(s); + return (sc->wl); +} + +struct window_pane * +spawn_pane(struct spawn_context *sc, char **cause) +{ + struct cmdq_item *item = sc->item; + struct client *c = item->client; + struct session *s = sc->s; + struct window *w = sc->wl->window; + struct window_pane *new_wp; + struct environ *child; + struct environ_entry *ee; + char **argv, *cp, **argvp, *argv0, *cwd; + const char *cmd, *tmp; + int argc; + u_int idx; + struct termios now; + u_int hlimit; + struct winsize ws; + sigset_t set, oldset; + + spawn_log(__func__, sc); + + /* + * If we are respawning then get rid of the old process. Otherwise + * either create a new cell or assign to the one we are given. + */ + hlimit = options_get_number(s->options, "history-limit"); + if (sc->flags & SPAWN_RESPAWN) { + if (sc->wp0->fd != -1 && (~sc->flags & SPAWN_KILL)) { + window_pane_index(sc->wp0, &idx); + xasprintf(cause, "pane %s:%d.%u still active", + s->name, sc->wl->idx, idx); + return (NULL); + } + if (sc->wp0->fd != -1) { + bufferevent_free(sc->wp0->event); + close(sc->wp0->fd); + } + window_pane_reset_mode_all(sc->wp0); + screen_reinit(&sc->wp0->base); + input_init(sc->wp0); + new_wp = sc->wp0; + new_wp->flags &= ~(PANE_STATUSREADY|PANE_STATUSDRAWN); + } else if (sc->lc == NULL) { + new_wp = window_add_pane(w, NULL, hlimit, sc->flags); + layout_init(w, new_wp); + } else { + new_wp = window_add_pane(w, sc->wp0, hlimit, sc->flags); + layout_assign_pane(sc->lc, new_wp); + } + + /* + * Now we have a pane with nothing running in it ready for the new + * process. Work out the command and arguments. + */ + if (sc->argc == 0) { + cmd = options_get_string(s->options, "default-command"); + if (cmd != NULL && *cmd != '\0') { + argc = 1; + argv = (char **)&cmd; + } else { + argc = 0; + argv = NULL; + } + } else { + argc = sc->argc; + argv = sc->argv; + } + + /* + * Replace the stored arguments if there are new ones. If not, the + * existing ones will be used (they will only exist for respawn). + */ + if (argc > 0) { + cmd_free_argv(new_wp->argc, new_wp->argv); + new_wp->argc = argc; + new_wp->argv = cmd_copy_argv(argc, argv); + } + + /* + * Work out the current working directory. If respawning, use + * the pane's stored one unless specified. + */ + if (sc->cwd != NULL) + cwd = format_single(item, sc->cwd, c, s, NULL, NULL); + else if (~sc->flags & SPAWN_RESPAWN) + cwd = xstrdup(server_client_get_cwd(c, s)); + else + cwd = NULL; + if (cwd != NULL) { + free(new_wp->cwd); + new_wp->cwd = cwd; + } + + /* Create an environment for this pane. */ + child = environ_for_session(s, 0); + environ_set(child, "TMUX_PANE", "%%%u", new_wp->id); + + /* + * Then the PATH environment variable. The session one is replaced from + * the client if there is one because otherwise running "tmux new + * myprogram" wouldn't work if myprogram isn't in the session's path. + */ + if (c != NULL && c->session == NULL) { /* only unattached clients */ + ee = environ_find(c->environ, "PATH"); + if (ee != NULL) + environ_set(child, "PATH", "%s", ee->value); + } + if (environ_find(child, "PATH") == NULL) + environ_set(child, "%s", _PATH_DEFPATH); + + /* Then the shell. If respawning, use the old one. */ + if (~sc->flags & SPAWN_RESPAWN) { + tmp = options_get_string(s->options, "default-shell"); + if (*tmp == '\0' || areshell(tmp)) + tmp = _PATH_BSHELL; + free(new_wp->shell); + new_wp->shell = xstrdup(tmp); + } + environ_set(child, "SHELL", "%s", new_wp->shell); + + /* Log the arguments we are going to use. */ + log_debug("%s: shell=%s", __func__, new_wp->shell); + if (new_wp->argc != 0) { + cp = cmd_stringify_argv(new_wp->argc, new_wp->argv); + log_debug("%s: cmd=%s", __func__, cp); + free(cp); + } + if (cwd != NULL) + log_debug("%s: cwd=%s", __func__, cwd); + cmd_log_argv(new_wp->argc, new_wp->argv, __func__); + environ_log(child, "%s: environment ", __func__); + + /* Initialize the window size. */ + memset(&ws, 0, sizeof ws); + ws.ws_col = screen_size_x(&new_wp->base); + ws.ws_row = screen_size_y(&new_wp->base); + + /* Block signals until fork has completed. */ + sigfillset(&set); + sigprocmask(SIG_BLOCK, &set, &oldset); + + /* Fork the new process. */ + new_wp->pid = fdforkpty(ptm_fd, &new_wp->fd, new_wp->tty, NULL, &ws); + if (new_wp->pid == -1) { + xasprintf(cause, "fork failed: %s", strerror(errno)); + new_wp->fd = -1; + if (~sc->flags & SPAWN_RESPAWN) { + layout_close_pane(new_wp); + window_remove_pane(w, new_wp); + } + sigprocmask(SIG_SETMASK, &oldset, NULL); + return (NULL); + } + + /* In the parent process, everything is done now. */ + if (new_wp->pid != 0) { + new_wp->pipe_off = 0; + new_wp->flags &= ~PANE_EXITED; + + sigprocmask(SIG_SETMASK, &oldset, NULL); + window_pane_set_event(new_wp); + + if (sc->flags & SPAWN_RESPAWN) + return (new_wp); + if ((~sc->flags & SPAWN_DETACHED) || w->active == NULL) { + if (sc->flags & SPAWN_NONOTIFY) + window_set_active_pane(w, new_wp, 0); + else + window_set_active_pane(w, new_wp, 1); + } + if (~sc->flags & SPAWN_NONOTIFY) + notify_window("window-layout-changed", w); + return (new_wp); + } + + /* + * Child process. Change to the working directory or home if that + * fails. + */ + if (chdir(new_wp->cwd) != 0) { + if ((tmp = find_home()) == NULL || chdir(tmp) != 0) + chdir("/"); + } + + /* + * Update terminal escape characters from the session if available and + * force VERASE to tmux's \177. + */ + if (tcgetattr(STDIN_FILENO, &now) != 0) + _exit(1); + if (s->tio != NULL) + memcpy(now.c_cc, s->tio->c_cc, sizeof now.c_cc); + now.c_cc[VERASE] = '\177'; + if (tcsetattr(STDIN_FILENO, TCSANOW, &now) != 0) + _exit(1); + + /* Clean up file descriptors and signals and update the environment. */ + closefrom(STDERR_FILENO + 1); + proc_clear_signals(server_proc, 1); + sigprocmask(SIG_SETMASK, &oldset, NULL); + log_close(); + environ_push(child); + + /* + * If given multiple arguments, use execvp(). Copy the arguments to + * ensure they end in a NULL. + */ + if (new_wp->argc != 0 && new_wp->argc != 1) { + argvp = cmd_copy_argv(new_wp->argc, new_wp->argv); + execvp(argvp[0], argvp); + _exit(1); + } + + /* + * If one argument, pass it to $SHELL -c. Otherwise create a login + * shell. + */ + cp = strrchr(new_wp->shell, '/'); + if (new_wp->argc == 1) { + tmp = new_wp->argv[0]; + if (cp != NULL && cp[1] != '\0') + xasprintf(&argv0, "%s", cp + 1); + else + xasprintf(&argv0, "%s", new_wp->shell); + execl(new_wp->shell, argv0, "-c", tmp, (char *)NULL); + _exit(1); + } + if (cp != NULL && cp[1] != '\0') + xasprintf(&argv0, "-%s", cp + 1); + else + xasprintf(&argv0, "-%s", new_wp->shell); + execl(new_wp->shell, argv0, (char *)NULL); + _exit(1); +} diff --git a/tmux.h b/tmux.h index 31fbae32..fc85344d 100644 --- a/tmux.h +++ b/tmux.h @@ -233,8 +233,8 @@ enum { /* Termcap codes. */ enum tty_code_code { - TTYC_AX = 0, TTYC_ACSC, + TTYC_AX, TTYC_BCE, TTYC_BEL, TTYC_BLINK, @@ -814,7 +814,7 @@ struct window_pane { int argc; char **argv; char *shell; - const char *cwd; + char *cwd; pid_t pid; char tty[TTY_NAME_MAX]; @@ -1561,6 +1561,32 @@ struct options_table_entry { #define CMD_SRCDST_CLIENT_USAGE "[-s src-client] [-t dst-client]" #define CMD_BUFFER_USAGE "[-b buffer-name]" +/* Spawn common context. */ +struct spawn_context { + struct cmdq_item *item; + + struct session *s; + struct winlink *wl; + + struct window_pane *wp0; + struct layout_cell *lc; + + const char *name; + char **argv; + int argc; + + int idx; + const char *cwd; + + int flags; +#define SPAWN_KILL 0x1 +#define SPAWN_DETACHED 0x2 +#define SPAWN_RESPAWN 0x4 +#define SPAWN_BEFORE 0x8 +#define SPAWN_NONOTIFY 0x10 +#define SPAWN_FULLSIZE 0x20 +}; + /* tmux.c */ extern struct hooks *global_hooks; extern struct options *global_options; @@ -2232,17 +2258,17 @@ struct window *window_find_by_id_str(const char *); struct window *window_find_by_id(u_int); void window_update_activity(struct window *); struct window *window_create(u_int, u_int); -struct window *window_create_spawn(const char *, int, char **, const char *, - const char *, const char *, struct environ *, - struct termios *, u_int, u_int, u_int, char **); +void window_destroy(struct window *); +void window_pane_set_event(struct window_pane *); struct window_pane *window_get_active_at(struct window *, u_int, u_int); struct window_pane *window_find_string(struct window *, const char *); int window_has_pane(struct window *, struct window_pane *); -int window_set_active_pane(struct window *, struct window_pane *); +int window_set_active_pane(struct window *, struct window_pane *, + int); void window_redraw_active_switch(struct window *, struct window_pane *); -struct window_pane *window_add_pane(struct window *, struct window_pane *, int, - int, u_int); +struct window_pane *window_add_pane(struct window *, struct window_pane *, + u_int, int); void window_resize(struct window *, u_int, u_int); int window_zoom(struct window_pane *); int window_unzoom(struct window *); @@ -2259,9 +2285,6 @@ void window_destroy_panes(struct window *); struct window_pane *window_pane_find_by_id_str(const char *); struct window_pane *window_pane_find_by_id(u_int); int window_pane_destroy_ready(struct window_pane *); -int window_pane_spawn(struct window_pane *, int, char **, - const char *, const char *, const char *, struct environ *, - struct termios *, char **); void window_pane_resize(struct window_pane *, u_int, u_int); void window_pane_alternate_on(struct window_pane *, struct grid_cell *, int); @@ -2319,7 +2342,7 @@ void layout_resize_pane_to(struct window_pane *, enum layout_type, u_int); void layout_assign_pane(struct layout_cell *, struct window_pane *); struct layout_cell *layout_split_pane(struct window_pane *, enum layout_type, - int, int, int); + int, int); void layout_close_pane(struct window_pane *); int layout_spread_cell(struct window *, struct layout_cell *); void layout_spread_out(struct window_pane *); @@ -2418,10 +2441,9 @@ int session_alive(struct session *); struct session *session_find(const char *); struct session *session_find_by_id_str(const char *); struct session *session_find_by_id(u_int); -struct session *session_create(const char *, const char *, int, char **, - const char *, const char *, struct environ *, - struct options *, struct termios *, int, char **); -void session_destroy(struct session *, const char *); +struct session *session_create(const char *, const char *, const char *, + struct environ *, struct options *, struct termios *); +void session_destroy(struct session *, int, const char *); void session_add_ref(struct session *, const char *); void session_remove_ref(struct session *, const char *); int session_check_name(const char *); @@ -2493,4 +2515,8 @@ void style_set(struct style *, const struct grid_cell *); void style_copy(struct style *, struct style *); int style_is_default(struct style *); +/* spawn.c */ +struct winlink *spawn_window(struct spawn_context *, char **); +struct window_pane *spawn_pane(struct spawn_context *, char **); + #endif /* TMUX_H */ diff --git a/window-tree.c b/window-tree.c index 120d22c8..fe0e5c14 100644 --- a/window-tree.c +++ b/window-tree.c @@ -1006,7 +1006,7 @@ window_tree_kill_each(__unused void* modedata, void* itemdata, case WINDOW_TREE_SESSION: if (s != NULL) { server_destroy_session(s); - session_destroy(s, __func__); + session_destroy(s, 1, __func__); } break; case WINDOW_TREE_WINDOW: diff --git a/window.c b/window.c index 3ec5d504..10571f99 100644 --- a/window.c +++ b/window.c @@ -26,7 +26,6 @@ #include #include #include -#include #include #include #include @@ -73,20 +72,10 @@ const struct window_mode *all_window_modes[] = { NULL }; -static void window_destroy(struct window *); - static struct window_pane *window_pane_create(struct window *, u_int, u_int, u_int); static void window_pane_destroy(struct window_pane *); -static void window_pane_read_callback(struct bufferevent *, void *); -static void window_pane_error_callback(struct bufferevent *, short, void *); - -static int winlink_next_index(struct winlinks *, int); - -static struct window_pane *window_pane_choose_best(struct window_pane **, - u_int); - RB_GENERATE(windows, window, entry, window_cmp); RB_GENERATE(winlinks, winlink, entry, winlink_cmp); RB_GENERATE(window_pane_tree, window_pane, tree_entry, window_pane_cmp); @@ -342,37 +331,7 @@ window_create(u_int sx, u_int sy) return (w); } -struct window * -window_create_spawn(const char *name, int argc, char **argv, const char *path, - const char *shell, const char *cwd, struct environ *env, - struct termios *tio, u_int sx, u_int sy, u_int hlimit, char **cause) -{ - struct window *w; - struct window_pane *wp; - - w = window_create(sx, sy); - wp = window_add_pane(w, NULL, 0, 0, hlimit); - layout_init(w, wp); - - if (window_pane_spawn(wp, argc, argv, path, shell, cwd, - env, tio, cause) != 0) { - window_destroy(w); - return (NULL); - } - - w->active = TAILQ_FIRST(&w->panes); - if (name != NULL) { - w->name = xstrdup(name); - options_set_number(w->options, "automatic-rename", 0); - } else - w->name = default_window_name(w); - - notify_window("window-pane-changed", w); - - return (w); -} - -static void +void window_destroy(struct window *w) { log_debug("window @%u destroyed (%d references)", w->id, w->references); @@ -463,17 +422,22 @@ window_has_pane(struct window *w, struct window_pane *wp) } int -window_set_active_pane(struct window *w, struct window_pane *wp) +window_set_active_pane(struct window *w, struct window_pane *wp, int notify) { - log_debug("%s: pane %%%u (was %%%u)", __func__, wp->id, w->active->id); + log_debug("%s: pane %%%u", __func__, wp->id); + if (wp == w->active) return (0); w->last = w->active; + w->active = wp; w->active->active_point = next_active_point++; w->active->flags |= PANE_CHANGED; + tty_update_window_offset(w); - notify_window("window-pane-changed", w); + + if (notify) + notify_window("window-pane-changed", w); return (1); } @@ -571,7 +535,7 @@ window_zoom(struct window_pane *wp) return (-1); if (w->active != wp) - window_set_active_pane(w, wp); + window_set_active_pane(w, wp, 1); TAILQ_FOREACH(wp1, &w->panes, entry) { wp1->saved_layout_cell = wp1->layout_cell; @@ -610,8 +574,8 @@ window_unzoom(struct window *w) } struct window_pane * -window_add_pane(struct window *w, struct window_pane *other, int before, - int full_size, u_int hlimit) +window_add_pane(struct window *w, struct window_pane *other, u_int hlimit, + int flags) { struct window_pane *wp; @@ -622,15 +586,15 @@ window_add_pane(struct window *w, struct window_pane *other, int before, if (TAILQ_EMPTY(&w->panes)) { log_debug("%s: @%u at start", __func__, w->id); TAILQ_INSERT_HEAD(&w->panes, wp, entry); - } else if (before) { + } else if (flags & SPAWN_BEFORE) { log_debug("%s: @%u before %%%u", __func__, w->id, wp->id); - if (full_size) + if (flags & SPAWN_FULLSIZE) TAILQ_INSERT_HEAD(&w->panes, wp, entry); else TAILQ_INSERT_BEFORE(other, wp, entry); } else { log_debug("%s: @%u after %%%u", __func__, w->id, wp->id); - if (full_size) + if (flags & SPAWN_FULLSIZE) TAILQ_INSERT_TAIL(&w->panes, wp, entry); else TAILQ_INSERT_AFTER(&w->panes, other, wp, entry); @@ -887,141 +851,6 @@ window_pane_destroy(struct window_pane *wp) free(wp); } -int -window_pane_spawn(struct window_pane *wp, int argc, char **argv, - const char *path, const char *shell, const char *cwd, struct environ *env, - struct termios *tio, char **cause) -{ - struct winsize ws; - char *argv0, *cmd, **argvp; - const char *ptr, *first, *home; - struct termios tio2; - sigset_t set, oldset; - - if (wp->fd != -1) { - bufferevent_free(wp->event); - close(wp->fd); - } - if (argc > 0) { - cmd_free_argv(wp->argc, wp->argv); - wp->argc = argc; - wp->argv = cmd_copy_argv(argc, argv); - } - if (shell != NULL) { - free(wp->shell); - wp->shell = xstrdup(shell); - } - if (cwd != NULL) { - free((void *)wp->cwd); - wp->cwd = xstrdup(cwd); - } - wp->flags &= ~(PANE_STATUSREADY|PANE_STATUSDRAWN); - - cmd = cmd_stringify_argv(wp->argc, wp->argv); - log_debug("%s: shell=%s", __func__, wp->shell); - log_debug("%s: cmd=%s", __func__, cmd); - log_debug("%s: cwd=%s", __func__, cwd); - cmd_log_argv(wp->argc, wp->argv, __func__); - environ_log(env, "%s: environment ", __func__); - - memset(&ws, 0, sizeof ws); - ws.ws_col = screen_size_x(&wp->base); - ws.ws_row = screen_size_y(&wp->base); - - sigfillset(&set); - sigprocmask(SIG_BLOCK, &set, &oldset); - switch (wp->pid = fdforkpty(ptm_fd, &wp->fd, wp->tty, NULL, &ws)) { - case -1: - wp->event = NULL; - wp->fd = -1; - - xasprintf(cause, "%s: %s", cmd, strerror(errno)); - free(cmd); - - sigprocmask(SIG_SETMASK, &oldset, NULL); - return (-1); - case 0: - proc_clear_signals(server_proc, 1); - sigprocmask(SIG_SETMASK, &oldset, NULL); - - cwd = NULL; - if (chdir(wp->cwd) == 0) - cwd = wp->cwd; - else if ((home = find_home()) != NULL && chdir(home) == 0) - cwd = home; - else - chdir("/"); - - if (tcgetattr(STDIN_FILENO, &tio2) != 0) - fatal("tcgetattr failed"); - if (tio != NULL) - memcpy(tio2.c_cc, tio->c_cc, sizeof tio2.c_cc); - tio2.c_cc[VERASE] = '\177'; - if (tcsetattr(STDIN_FILENO, TCSANOW, &tio2) != 0) - fatal("tcgetattr failed"); - - log_close(); - closefrom(STDERR_FILENO + 1); - - if (path != NULL) - environ_set(env, "PATH", "%s", path); - if (cwd != NULL) - environ_set(env, "PWD", "%s", cwd); - environ_set(env, "TMUX_PANE", "%%%u", wp->id); - environ_push(env); - - setenv("SHELL", wp->shell, 1); - ptr = strrchr(wp->shell, '/'); - - /* - * If given one argument, assume it should be passed to sh -c; - * with more than one argument, use execvp(). If there is no - * arguments, create a login shell. - */ - if (wp->argc > 0) { - if (wp->argc != 1) { - /* Copy to ensure argv ends in NULL. */ - argvp = cmd_copy_argv(wp->argc, wp->argv); - execvp(argvp[0], argvp); - fatal("execvp failed"); - } - first = wp->argv[0]; - - if (ptr != NULL && *(ptr + 1) != '\0') - xasprintf(&argv0, "%s", ptr + 1); - else - xasprintf(&argv0, "%s", wp->shell); - execl(wp->shell, argv0, "-c", first, (char *)NULL); - fatal("execl failed"); - } - if (ptr != NULL && *(ptr + 1) != '\0') - xasprintf(&argv0, "-%s", ptr + 1); - else - xasprintf(&argv0, "-%s", wp->shell); - execl(wp->shell, argv0, (char *)NULL); - fatal("execl failed"); - } - log_debug("%s: master=%s", __func__, ttyname(wp->fd)); - log_debug("%s: slave=%s", __func__, wp->tty); - - sigprocmask(SIG_SETMASK, &oldset, NULL); - setblocking(wp->fd, 0); - - wp->event = bufferevent_new(wp->fd, window_pane_read_callback, NULL, - window_pane_error_callback, wp); - if (wp->event == NULL) - fatalx("out of memory"); - - wp->pipe_off = 0; - wp->flags &= ~PANE_EXITED; - - bufferevent_setwatermark(wp->event, EV_READ, 0, READ_SIZE); - bufferevent_enable(wp->event, EV_READ|EV_WRITE); - - free(cmd); - return (0); -} - static void window_pane_read_callback(__unused struct bufferevent *bufev, void *data) { @@ -1056,6 +885,18 @@ window_pane_error_callback(__unused struct bufferevent *bufev, server_destroy_pane(wp, 1); } +void +window_pane_set_event(struct window_pane *wp) +{ + setblocking(wp->fd, 0); + + wp->event = bufferevent_new(wp->fd, window_pane_read_callback, + NULL, window_pane_error_callback, wp); + + bufferevent_setwatermark(wp->event, EV_READ, 0, READ_SIZE); + bufferevent_enable(wp->event, EV_READ|EV_WRITE); +} + void window_pane_resize(struct window_pane *wp, u_int sx, u_int sy) { @@ -1604,6 +1445,8 @@ winlink_shuffle_up(struct session *s, struct winlink *wl) { int idx, last; + if (wl == NULL) + return (-1); idx = wl->idx + 1; /* Find the next free index. */ From 5943cd1907d20381c527d4b3b338df090cbbb1de Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 17 Apr 2019 14:39:37 +0000 Subject: [PATCH 2/9] Document that switch-client can change all of session,window,pane and check for % in the target as well as ":.". --- cmd-switch-client.c | 2 +- tmux.1 | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/cmd-switch-client.c b/cmd-switch-client.c index a4f55102..8f51d0fe 100644 --- a/cmd-switch-client.c +++ b/cmd-switch-client.c @@ -61,7 +61,7 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item) if ((c = cmd_find_client(item, args_get(args, 'c'), 0)) == NULL) return (CMD_RETURN_ERROR); - if (tflag != NULL && tflag[strcspn(tflag, ":.")] != '\0') { + if (tflag != NULL && tflag[strcspn(tflag, ":.%")] != '\0') { type = CMD_FIND_PANE; flags = 0; } else { diff --git a/tmux.1 b/tmux.1 index 54404b80..0d4a17eb 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1053,6 +1053,14 @@ Switch the current session for client .Ar target-client to .Ar target-session . +As a special case, +.Fl t +may refer to a pane (a target that contains +.Ql : , +.Ql . +or +.Ql % ) +in which case the session, window and pane are all changed. If .Fl l , .Fl n From e3b1358bbc616c1ba95414d6102c23bfcaa37687 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 17 Apr 2019 14:41:08 +0000 Subject: [PATCH 3/9] Do not let the size of the pane status screen go negative. --- screen-redraw.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/screen-redraw.c b/screen-redraw.c index 691b2194..0ce3374d 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -290,7 +290,10 @@ screen_redraw_make_pane_status(struct client *c, struct window *w, format_defaults(ft, c, NULL, NULL, wp); expanded = format_expand_time(ft, fmt); - wp->status_size = width = wp->sx - 4; + if (wp->sx < 4) + wp->status_size = width = 0; + else + wp->status_size = width = wp->sx - 4; memcpy(&old, &wp->status_screen, sizeof old); screen_init(&wp->status_screen, width, 1, 0); From c660e46149d606bd5e52941081cbe155e493768b Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 17 Apr 2019 14:43:49 +0000 Subject: [PATCH 4/9] Set the window size as well as the layout size when using the preset layouts. --- cmd-select-layout.c | 1 + layout-set.c | 29 +++++++++++++++++++++++++---- layout.c | 4 ++-- resize.c | 2 ++ tty.c | 3 +-- 5 files changed, 31 insertions(+), 8 deletions(-) diff --git a/cmd-select-layout.c b/cmd-select-layout.c index d8fc1aeb..775d32c5 100644 --- a/cmd-select-layout.c +++ b/cmd-select-layout.c @@ -135,6 +135,7 @@ cmd_select_layout_exec(struct cmd *self, struct cmdq_item *item) changed: free(oldlayout); + recalculate_sizes(); server_redraw_window(w); notify_window("window-layout-changed", w); return (CMD_RETURN_NORMAL); diff --git a/layout-set.c b/layout-set.c index d99453c2..97315964 100644 --- a/layout-set.c +++ b/layout-set.c @@ -119,7 +119,7 @@ layout_set_even(struct window *w, enum layout_type type) { struct window_pane *wp; struct layout_cell *lc, *lcnew; - u_int n; + u_int n, sx, sy; layout_print_cell(w->layout_root, __func__, 1); @@ -131,7 +131,18 @@ layout_set_even(struct window *w, enum layout_type type) /* Free the old root and construct a new. */ layout_free(w); lc = w->layout_root = layout_create_cell(NULL); - layout_set_size(lc, w->sx, w->sy, 0, 0); + if (type == LAYOUT_LEFTRIGHT) { + sx = (n * (PANE_MINIMUM + 1)) - 1; + if (sx < w->sx) + sx = w->sx; + sy = w->sy; + } else { + sy = (n * (PANE_MINIMUM + 1)) - 1; + if (sy < w->sy) + sy = w->sy; + sx = w->sx; + } + layout_set_size(lc, sx, sy, 0, 0); layout_make_node(lc, type); /* Build new leaf cells. */ @@ -152,6 +163,7 @@ layout_set_even(struct window *w, enum layout_type type) layout_print_cell(w->layout_root, __func__, 1); + window_resize(w, lc->sx, lc->sy); notify_window("window-layout-changed", w); server_redraw_window(w); } @@ -288,6 +300,7 @@ layout_set_main_h(struct window *w) layout_print_cell(w->layout_root, __func__, 1); + window_resize(w, lc->sx, lc->sy); notify_window("window-layout-changed", w); server_redraw_window(w); } @@ -412,6 +425,7 @@ layout_set_main_v(struct window *w) layout_print_cell(w->layout_root, __func__, 1); + window_resize(w, lc->sx, lc->sy); notify_window("window-layout-changed", w); server_redraw_window(w); } @@ -421,7 +435,7 @@ layout_set_tiled(struct window *w) { struct window_pane *wp; struct layout_cell *lc, *lcrow, *lcchild; - u_int n, width, height, used; + u_int n, width, height, used, sx, sy; u_int i, j, columns, rows; layout_print_cell(w->layout_root, __func__, 1); @@ -450,7 +464,13 @@ layout_set_tiled(struct window *w) /* Free old tree and create a new root. */ layout_free(w); lc = w->layout_root = layout_create_cell(NULL); - layout_set_size(lc, w->sx, w->sy, 0, 0); + sx = ((width + 1) * columns) - 1; + if (sx < w->sx) + sx = w->sx; + sy = ((height + 1) * rows) - 1; + if (sy < w->sy) + sy = w->sy; + layout_set_size(lc, sx, sy, 0, 0); layout_make_node(lc, LAYOUT_TOPBOTTOM); /* Create a grid of the cells. */ @@ -514,6 +534,7 @@ layout_set_tiled(struct window *w) layout_print_cell(w->layout_root, __func__, 1); + window_resize(w, lc->sx, lc->sy); notify_window("window-layout-changed", w); server_redraw_window(w); } diff --git a/layout.c b/layout.c index 1a43014d..b825ee52 100644 --- a/layout.c +++ b/layout.c @@ -479,7 +479,7 @@ layout_resize(struct window *w, u_int sx, u_int sy) * out proportionately - this should leave the layout fitting the new * window size. */ - xchange = sx - w->sx; + xchange = sx - lc->sx; xlimit = layout_resize_check(w, lc, LAYOUT_LEFTRIGHT); if (xchange < 0 && xchange < -xlimit) xchange = -xlimit; @@ -493,7 +493,7 @@ layout_resize(struct window *w, u_int sx, u_int sy) layout_resize_adjust(w, lc, LAYOUT_LEFTRIGHT, xchange); /* Adjust vertically in a similar fashion. */ - ychange = sy - w->sy; + ychange = sy - lc->sy; ylimit = layout_resize_check(w, lc, LAYOUT_TOPBOTTOM); if (ychange < 0 && ychange < -ylimit) ychange = -ylimit; diff --git a/resize.c b/resize.c index 868ddac8..caac74ba 100644 --- a/resize.c +++ b/resize.c @@ -51,6 +51,8 @@ resize_window(struct window *w, u_int sx, u_int sy) if (sy < w->layout_root->sy) sy = w->layout_root->sy; window_resize(w, sx, sy); + log_debug("%s: @%u resized to %u,%u; layout %u,%u", __func__, w->id, + sx, sy, w->layout_root->sx, w->layout_root->sy); /* Restore the window zoom state. */ if (zoomed) diff --git a/tty.c b/tty.c index 855f8e6a..9ed2b3a9 100644 --- a/tty.c +++ b/tty.c @@ -907,9 +907,8 @@ tty_is_visible(struct tty *tty, const struct tty_ctx *ctx, u_int px, u_int py, lines = 0; if (xoff + nx <= ctx->ox || xoff >= ctx->ox + ctx->sx || - yoff + ny <= ctx->oy || yoff >= lines + ctx->oy + ctx->sy) { + yoff + ny <= ctx->oy || yoff >= lines + ctx->oy + ctx->sy) return (0); - } return (1); } From da31eddadc0c70875cd261d8925e4346c3e26590 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 17 Apr 2019 14:44:14 +0000 Subject: [PATCH 5/9] Rewrite main-vertical and horizontal to use the common spread out code and to handle the case where the panes won't fit into the existing window size. --- layout-set.c | 246 ++++++++++++++++----------------------------------- 1 file changed, 76 insertions(+), 170 deletions(-) diff --git a/layout-set.c b/layout-set.c index 97315964..504d9630 100644 --- a/layout-set.c +++ b/layout-set.c @@ -184,9 +184,8 @@ static void layout_set_main_h(struct window *w) { struct window_pane *wp; - struct layout_cell *lc, *lcmain, *lcrow, *lcchild; - u_int n, mainheight, otherheight, width, height; - u_int used, i, j, columns, rows, totalrows; + struct layout_cell *lc, *lcmain, *lcother, *lcchild; + u_int n, mainh, otherh, sx; layout_print_cell(w->layout_root, __func__, 1); @@ -196,103 +195,57 @@ layout_set_main_h(struct window *w) return; n--; /* take off main pane */ - /* How many rows and columns will be needed, not counting main? */ - columns = (w->sx + 1) / (PANE_MINIMUM + 1); /* maximum columns */ - if (columns == 0) - columns = 1; - rows = 1 + (n - 1) / columns; - columns = 1 + (n - 1) / rows; - width = (w->sx - (n - 1)) / columns; - - /* Get the main pane height and add one for separator line. */ - mainheight = options_get_number(w->options, "main-pane-height") + 1; - - /* Get the optional other pane height and add one for separator line. */ - otherheight = options_get_number(w->options, "other-pane-height") + 1; - - /* - * If an other pane height was specified, honour it so long as it - * doesn't shrink the main height to less than the main-pane-height - */ - if (otherheight > 1 && w->sy - otherheight > mainheight) - mainheight = w->sy - otherheight; - if (mainheight < PANE_MINIMUM + 1) - mainheight = PANE_MINIMUM + 1; - - /* Try and make everything fit. */ - totalrows = rows * (PANE_MINIMUM + 1) - 1; - if (mainheight + totalrows > w->sy) { - if (totalrows + PANE_MINIMUM + 1 > w->sy) - mainheight = PANE_MINIMUM + 2; + /* Get the main pane height and work out the other pane height. */ + mainh = options_get_number(w->options, "main-pane-height"); + if (mainh + PANE_MINIMUM + 1 >= w->sy) { + if (w->sy <= PANE_MINIMUM + 1 + PANE_MINIMUM) + mainh = PANE_MINIMUM; else - mainheight = w->sy - totalrows; - height = PANE_MINIMUM; - } else - height = (w->sy - mainheight - (rows - 1)) / rows; + mainh = w->sy - (PANE_MINIMUM + 1); + otherh = PANE_MINIMUM; + } else { + otherh = options_get_number(w->options, "other-pane-height"); + if (otherh == 0) + otherh = w->sy - mainh; + else if (otherh > w->sy || w->sy - otherh < mainh) + otherh = w->sy - mainh; + else + mainh = w->sy - otherh; + } + + /* Work out what height is needed. */ + sx = (n * (PANE_MINIMUM + 1)) - 1; + if (sx < w->sx) + sx = w->sx; /* Free old tree and create a new root. */ layout_free(w); lc = w->layout_root = layout_create_cell(NULL); - layout_set_size(lc, w->sx, mainheight + rows * (height + 1) - 1, 0, 0); + layout_set_size(lc, sx, mainh + otherh + 1, 0, 0); layout_make_node(lc, LAYOUT_TOPBOTTOM); /* Create the main pane. */ lcmain = layout_create_cell(lc); - layout_set_size(lcmain, w->sx, mainheight - 1, 0, 0); + layout_set_size(lcmain, sx, mainh, 0, 0); layout_make_leaf(lcmain, TAILQ_FIRST(&w->panes)); TAILQ_INSERT_TAIL(&lc->cells, lcmain, entry); - /* Create a grid of the remaining cells. */ - wp = TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry); - for (j = 0; j < rows; j++) { - /* If this is the last cell, all done. */ - if (wp == NULL) - break; + /* Create the other pane. */ + lcother = layout_create_cell(lc); + layout_set_size(lcother, sx, otherh, 0, 0); + layout_make_node(lcother, LAYOUT_LEFTRIGHT); + TAILQ_INSERT_TAIL(&lc->cells, lcother, entry); - /* Create the new row. */ - lcrow = layout_create_cell(lc); - layout_set_size(lcrow, w->sx, height, 0, 0); - TAILQ_INSERT_TAIL(&lc->cells, lcrow, entry); - - /* If only one column, just use the row directly. */ - if (columns == 1) { - layout_make_leaf(lcrow, wp); - wp = TAILQ_NEXT(wp, entry); + /* Add the remaining panes as children. */ + TAILQ_FOREACH(wp, &w->panes, entry) { + if (wp == TAILQ_FIRST(&w->panes)) continue; - } - - /* Add in the columns. */ - layout_make_node(lcrow, LAYOUT_LEFTRIGHT); - for (i = 0; i < columns; i++) { - /* Create and add a pane cell. */ - lcchild = layout_create_cell(lcrow); - layout_set_size(lcchild, width, height, 0, 0); - layout_make_leaf(lcchild, wp); - TAILQ_INSERT_TAIL(&lcrow->cells, lcchild, entry); - - /* Move to the next cell. */ - if ((wp = TAILQ_NEXT(wp, entry)) == NULL) - break; - } - - /* Adjust the row to fit the full width if necessary. */ - if (i == columns) - i--; - used = ((i + 1) * (width + 1)) - 1; - if (w->sx <= used) - continue; - lcchild = TAILQ_LAST(&lcrow->cells, layout_cells); - layout_resize_adjust(w, lcchild, LAYOUT_LEFTRIGHT, - w->sx - used); - } - - /* Adjust the last row height to fit if necessary. */ - used = mainheight + (rows * height) + rows - 1; - if (w->sy > used) { - lcrow = TAILQ_LAST(&lc->cells, layout_cells); - layout_resize_adjust(w, lcrow, LAYOUT_TOPBOTTOM, - w->sy - used); + lcchild = layout_create_cell(lc); + layout_set_size(lcchild, PANE_MINIMUM, otherh, 0, 0); + layout_make_leaf(lcchild, wp); + TAILQ_INSERT_TAIL(&lcother->cells, lcchild, entry); } + layout_spread_cell(w, lcother); /* Fix cell offsets. */ layout_fix_offsets(lc); @@ -309,9 +262,8 @@ static void layout_set_main_v(struct window *w) { struct window_pane *wp; - struct layout_cell *lc, *lcmain, *lccolumn, *lcchild; - u_int n, mainwidth, otherwidth, width, height; - u_int used, i, j, columns, rows, totalcolumns; + struct layout_cell *lc, *lcmain, *lcother, *lcchild; + u_int n, mainw, otherw, sy; layout_print_cell(w->layout_root, __func__, 1); @@ -321,103 +273,57 @@ layout_set_main_v(struct window *w) return; n--; /* take off main pane */ - /* How many rows and columns will be needed, not counting main? */ - rows = (w->sy + 1) / (PANE_MINIMUM + 1); /* maximum rows */ - if (rows == 0) - rows = 1; - columns = 1 + (n - 1) / rows; - rows = 1 + (n - 1) / columns; - height = (w->sy - (n - 1)) / rows; - - /* Get the main pane width and add one for separator line. */ - mainwidth = options_get_number(w->options, "main-pane-width") + 1; - - /* Get the optional other pane width and add one for separator line. */ - otherwidth = options_get_number(w->options, "other-pane-width") + 1; - - /* - * If an other pane width was specified, honour it so long as it - * doesn't shrink the main width to less than the main-pane-width - */ - if (otherwidth > 1 && w->sx - otherwidth > mainwidth) - mainwidth = w->sx - otherwidth; - if (mainwidth < PANE_MINIMUM + 1) - mainwidth = PANE_MINIMUM + 1; - - /* Try and make everything fit. */ - totalcolumns = columns * (PANE_MINIMUM + 1) - 1; - if (mainwidth + totalcolumns > w->sx) { - if (totalcolumns + PANE_MINIMUM + 1 > w->sx) - mainwidth = PANE_MINIMUM + 2; + /* Get the main pane width and work out the other pane width. */ + mainw = options_get_number(w->options, "main-pane-width"); + if (mainw + PANE_MINIMUM + 1 >= w->sx) { + if (w->sx <= PANE_MINIMUM + 1 + PANE_MINIMUM) + mainw = PANE_MINIMUM; else - mainwidth = w->sx - totalcolumns; - width = PANE_MINIMUM; - } else - width = (w->sx - mainwidth - (columns - 1)) / columns; + mainw = w->sx - (PANE_MINIMUM + 1); + otherw = PANE_MINIMUM; + } else { + otherw = options_get_number(w->options, "other-pane-width"); + if (otherw == 0) + otherw = w->sx - mainw; + else if (otherw > w->sx || w->sx - otherw < mainw) + otherw = w->sx - mainw; + else + mainw = w->sx - otherw; + } + + /* Work out what height is needed. */ + sy = (n * (PANE_MINIMUM + 1)) - 1; + if (sy < w->sy) + sy = w->sy; /* Free old tree and create a new root. */ layout_free(w); lc = w->layout_root = layout_create_cell(NULL); - layout_set_size(lc, mainwidth + columns * (width + 1) - 1, w->sy, 0, 0); + layout_set_size(lc, mainw + otherw + 1, sy, 0, 0); layout_make_node(lc, LAYOUT_LEFTRIGHT); /* Create the main pane. */ lcmain = layout_create_cell(lc); - layout_set_size(lcmain, mainwidth - 1, w->sy, 0, 0); + layout_set_size(lcmain, mainw, sy, 0, 0); layout_make_leaf(lcmain, TAILQ_FIRST(&w->panes)); TAILQ_INSERT_TAIL(&lc->cells, lcmain, entry); - /* Create a grid of the remaining cells. */ - wp = TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry); - for (j = 0; j < columns; j++) { - /* If this is the last cell, all done. */ - if (wp == NULL) - break; + /* Create the other pane. */ + lcother = layout_create_cell(lc); + layout_set_size(lcother, otherw, sy, 0, 0); + layout_make_node(lcother, LAYOUT_TOPBOTTOM); + TAILQ_INSERT_TAIL(&lc->cells, lcother, entry); - /* Create the new column. */ - lccolumn = layout_create_cell(lc); - layout_set_size(lccolumn, width, w->sy, 0, 0); - TAILQ_INSERT_TAIL(&lc->cells, lccolumn, entry); - - /* If only one row, just use the row directly. */ - if (rows == 1) { - layout_make_leaf(lccolumn, wp); - wp = TAILQ_NEXT(wp, entry); + /* Add the remaining panes as children. */ + TAILQ_FOREACH(wp, &w->panes, entry) { + if (wp == TAILQ_FIRST(&w->panes)) continue; - } - - /* Add in the rows. */ - layout_make_node(lccolumn, LAYOUT_TOPBOTTOM); - for (i = 0; i < rows; i++) { - /* Create and add a pane cell. */ - lcchild = layout_create_cell(lccolumn); - layout_set_size(lcchild, width, height, 0, 0); - layout_make_leaf(lcchild, wp); - TAILQ_INSERT_TAIL(&lccolumn->cells, lcchild, entry); - - /* Move to the next cell. */ - if ((wp = TAILQ_NEXT(wp, entry)) == NULL) - break; - } - - /* Adjust the column to fit the full height if necessary. */ - if (i == rows) - i--; - used = ((i + 1) * (height + 1)) - 1; - if (w->sy <= used) - continue; - lcchild = TAILQ_LAST(&lccolumn->cells, layout_cells); - layout_resize_adjust(w, lcchild, LAYOUT_TOPBOTTOM, - w->sy - used); - } - - /* Adjust the last column width to fit if necessary. */ - used = mainwidth + (columns * width) + columns - 1; - if (w->sx > used) { - lccolumn = TAILQ_LAST(&lc->cells, layout_cells); - layout_resize_adjust(w, lccolumn, LAYOUT_LEFTRIGHT, - w->sx - used); + lcchild = layout_create_cell(lc); + layout_set_size(lcchild, otherw, PANE_MINIMUM, 0, 0); + layout_make_leaf(lcchild, wp); + TAILQ_INSERT_TAIL(&lcother->cells, lcchild, entry); } + layout_spread_cell(w, lcother); /* Fix cell offsets. */ layout_fix_offsets(lc); From 1fd3b9ec1c61c96c6cd86f81a50470f4428eeafe Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 17 Apr 2019 14:44:33 +0000 Subject: [PATCH 6/9] Fix minimum size check on split and size of first cell on spread out with a pane status line. --- layout.c | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/layout.c b/layout.c index b825ee52..f974e10d 100644 --- a/layout.c +++ b/layout.c @@ -834,9 +834,9 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size, int flags) { struct layout_cell *lc, *lcparent, *lcnew, *lc1, *lc2; - u_int sx, sy, xoff, yoff, size1, size2; + u_int sx, sy, xoff, yoff, size1, size2, minimum; u_int new_size, saved_size, resize_first = 0; - int full_size = (flags & SPAWN_FULLSIZE); + int full_size = (flags & SPAWN_FULLSIZE), status; /* * If full_size is specified, add a new cell at the top of the window @@ -846,6 +846,7 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size, lc = wp->window->layout_root; else lc = wp->layout_cell; + status = options_get_number(wp->window->options, "pane-border-status"); /* Copy the old cell size. */ sx = lc->sx; @@ -860,7 +861,10 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size, return (NULL); break; case LAYOUT_TOPBOTTOM: - if (sy < PANE_MINIMUM * 2 + 1) + minimum = PANE_MINIMUM * 2 + 1; + if (status != 0) + minimum += layout_need_status(lc, status == 1); + if (sy < minimum) return (NULL); break; default: @@ -1012,22 +1016,29 @@ int layout_spread_cell(struct window *w, struct layout_cell *parent) { struct layout_cell *lc; - u_int number, each, size; - int change, changed; + u_int number, each, size, this; + int change, changed, status; number = 0; TAILQ_FOREACH (lc, &parent->cells, entry) number++; if (number <= 1) return (0); + status = options_get_number(w->options, "pane-border-status"); if (parent->type == LAYOUT_LEFTRIGHT) size = parent->sx; - else if (parent->type == LAYOUT_TOPBOTTOM) + else if (parent->type == LAYOUT_TOPBOTTOM) { size = parent->sy; - else + if (status != 0) + size -= layout_need_status(parent, status == 1); + } else + return (0); + if (size < number - 1) return (0); each = (size - (number - 1)) / number; + if (each == 0) + return (0); changed = 0; TAILQ_FOREACH (lc, &parent->cells, entry) { @@ -1038,7 +1049,10 @@ layout_spread_cell(struct window *w, struct layout_cell *parent) change = each - (int)lc->sx; layout_resize_adjust(w, lc, LAYOUT_LEFTRIGHT, change); } else if (parent->type == LAYOUT_TOPBOTTOM) { - change = each - (int)lc->sy; + this = each; + if (status != 0) + this += layout_need_status(lc, status == 1); + change = this - (int)lc->sy; layout_resize_adjust(w, lc, LAYOUT_TOPBOTTOM, change); } if (change != 0) From 2219f7cc73abc20407739e27ef191df393142795 Mon Sep 17 00:00:00 2001 From: jmc Date: Wed, 17 Apr 2019 16:34:35 +0000 Subject: [PATCH 7/9] mark up punctuation-as-macro-args properly; --- tmux.1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tmux.1 b/tmux.1 index 0d4a17eb..28fa5fbc 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1056,10 +1056,10 @@ to As a special case, .Fl t may refer to a pane (a target that contains -.Ql : , -.Ql . +.Ql \&: , +.Ql \&. or -.Ql % ) +.Ql % ) , in which case the session, window and pane are all changed. If .Fl l , From f3ab05e7cd9287d1e4be9a122687f568a8ef1f05 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 18 Apr 2019 10:11:52 +0000 Subject: [PATCH 8/9] Update session activity on focus event, from tafryn at gmail dot com. --- server-client.c | 1 + 1 file changed, 1 insertion(+) diff --git a/server-client.c b/server-client.c index 3aa159c3..4cd5be74 100644 --- a/server-client.c +++ b/server-client.c @@ -1369,6 +1369,7 @@ focused: if (wp->base.mode & MODE_FOCUSON) bufferevent_write(wp->event, "\033[I", 3); notify_pane("pane-focus-in", wp); + session_update_activity(c->session, NULL); } wp->flags |= PANE_FOCUSED; } From 3f189945d8838f98920a6d24a838bc8614ace636 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 18 Apr 2019 11:07:28 +0000 Subject: [PATCH 9/9] Pass target client and session to load_cfg from source-file so formats work. Reported by Thomas Sattler. --- cfg.c | 30 +++++++++++++++++++++--------- screen-write.c | 2 +- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/cfg.c b/cfg.c index 1a7e4375..0eadb6c3 100644 --- a/cfg.c +++ b/cfg.c @@ -116,7 +116,8 @@ start_cfg(void) } static int -cfg_check_condition(const char *path, size_t line, const char *p, int *skip) +cfg_check_cond(const char *path, size_t line, const char *p, int *skip, + struct client *c, struct cmd_find_state *fs) { struct format_tree *ft; char *s; @@ -131,6 +132,10 @@ cfg_check_condition(const char *path, size_t line, const char *p, int *skip) } ft = format_create(NULL, NULL, FORMAT_NONE, FORMAT_NOJOBS); + if (fs != NULL) + format_defaults(ft, c, fs->s, fs->wl, fs->wp); + else + format_defaults(ft, c, NULL, NULL, NULL); s = format_expand(ft, p); result = format_true(s); free(s); @@ -142,7 +147,7 @@ cfg_check_condition(const char *path, size_t line, const char *p, int *skip) static void cfg_handle_if(const char *path, size_t line, struct cfg_conds *conds, - const char *p) + const char *p, struct client *c, struct cmd_find_state *fs) { struct cfg_cond *cond; struct cfg_cond *parent = TAILQ_FIRST(conds); @@ -154,7 +159,7 @@ cfg_handle_if(const char *path, size_t line, struct cfg_conds *conds, cond = xcalloc(1, sizeof *cond); cond->line = line; if (parent == NULL || parent->met) - cond->met = cfg_check_condition(path, line, p, &cond->skip); + cond->met = cfg_check_cond(path, line, p, &cond->skip, c, fs); else cond->skip = 1; cond->saw_else = 0; @@ -163,7 +168,7 @@ cfg_handle_if(const char *path, size_t line, struct cfg_conds *conds, static void cfg_handle_elif(const char *path, size_t line, struct cfg_conds *conds, - const char *p) + const char *p, struct client *c, struct cmd_find_state *fs) { struct cfg_cond *cond = TAILQ_FIRST(conds); @@ -174,7 +179,7 @@ cfg_handle_elif(const char *path, size_t line, struct cfg_conds *conds, if (cond == NULL || cond->saw_else) cfg_add_cause("%s:%zu: unexpected %%elif", path, line); else if (!cond->skip) - cond->met = cfg_check_condition(path, line, p, &cond->skip); + cond->met = cfg_check_cond(path, line, p, &cond->skip, c, fs); else cond->met = 0; } @@ -215,16 +220,16 @@ cfg_handle_endif(const char *path, size_t line, struct cfg_conds *conds) static void cfg_handle_directive(const char *p, const char *path, size_t line, - struct cfg_conds *conds) + struct cfg_conds *conds, struct client *c, struct cmd_find_state *fs) { int n = 0; while (p[n] != '\0' && !isspace((u_char)p[n])) n++; if (strncmp(p, "%if", n) == 0) - cfg_handle_if(path, line, conds, p + n); + cfg_handle_if(path, line, conds, p + n, c, fs); else if (strncmp(p, "%elif", n) == 0) - cfg_handle_elif(path, line, conds, p + n); + cfg_handle_elif(path, line, conds, p + n, c, fs); else if (strcmp(p, "%else") == 0) cfg_handle_else(path, line, conds); else if (strcmp(p, "%endif") == 0) @@ -245,6 +250,13 @@ load_cfg(const char *path, struct client *c, struct cmdq_item *item, int quiet) struct cmdq_item *new_item; struct cfg_cond *cond, *cond1; struct cfg_conds conds; + struct cmd_find_state *fs = NULL; + struct client *fc = NULL; + + if (item != NULL) { + fs = &item->target; + fc = cmd_find_client(item, NULL, 1); + } TAILQ_INIT(&conds); @@ -271,7 +283,7 @@ load_cfg(const char *path, struct client *c, struct cmdq_item *item, int quiet) *q-- = '\0'; if (*p == '%') { - cfg_handle_directive(p, path, line, &conds); + cfg_handle_directive(p, path, line, &conds, fc, fs); continue; } cond = TAILQ_FIRST(&conds); diff --git a/screen-write.c b/screen-write.c index 566d4d14..237b6359 100644 --- a/screen-write.c +++ b/screen-write.c @@ -1139,7 +1139,7 @@ screen_write_collect_clear(struct screen_write_ctx *ctx, u_int y, u_int n) u_int i; size_t size; - for (i = y ; i < y + n; i++) { + for (i = y; i < y + n; i++) { if (TAILQ_EMPTY(&ctx->list[i].items)) continue; size = 0;