diff --git a/client.c b/client.c index 7d997a7e..9c493dfa 100644 --- a/client.c +++ b/client.c @@ -352,9 +352,10 @@ client_main(int argc, char **argv, int flags) void client_send_identify(int flags) { - const char *s; + const char *s; char **ss; - int fd; + int fd; + pid_t pid; client_write_one(MSG_IDENTIFY_FLAGS, -1, &flags, sizeof flags); @@ -374,6 +375,9 @@ client_send_identify(int flags) fatal("dup failed"); client_write_one(MSG_IDENTIFY_STDIN, fd, NULL, 0); + pid = getpid(); + client_write_one(MSG_IDENTIFY_CLIENTPID, -1, &pid, sizeof pid); + for (ss = environ; *ss != NULL; ss++) client_write_one(MSG_IDENTIFY_ENVIRON, -1, *ss, strlen(*ss) + 1); diff --git a/cmd-break-pane.c b/cmd-break-pane.c index a1da0a3a..c7af7865 100644 --- a/cmd-break-pane.c +++ b/cmd-break-pane.c @@ -32,8 +32,8 @@ enum cmd_retval cmd_break_pane_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_break_pane_entry = { "break-pane", "breakp", - "dPF:t:", 0, 0, - "[-dP] [-F format] " CMD_TARGET_PANE_USAGE, + "dPF:s:t:", 0, 0, + "[-dP] [-F format] " CMD_SRCDST_PANE_USAGE, 0, cmd_break_pane_exec }; @@ -48,13 +48,19 @@ cmd_break_pane_exec(struct cmd *self, struct cmd_q *cmdq) struct window *w; char *name; char *cause; - int base_idx; + int idx; struct format_tree *ft; const char *template; char *cp; - if ((wl = cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp)) == NULL) + if ((wl = cmd_find_pane(cmdq, args_get(args, 's'), &s, &wp)) == NULL) return (CMD_RETURN_ERROR); + if ((idx = cmd_find_index(cmdq, args_get(args, 't'), &s)) == -2) + return (CMD_RETURN_ERROR); + if (idx != -1 && winlink_find_by_index(&s->windows, idx) != NULL) { + cmdq_error(cmdq, "index %d already in use", idx); + return (CMD_RETURN_ERROR); + } if (window_count_panes(wl->window) == 1) { cmdq_error(cmdq, "can't break with only one pane"); @@ -76,8 +82,9 @@ cmd_break_pane_exec(struct cmd *self, struct cmd_q *cmdq) free(name); layout_init(w, wp); - base_idx = options_get_number(&s->options, "base-index"); - wl = session_attach(s, w, -1 - base_idx, &cause); /* can't fail */ + if (idx == -1) + idx = -1 - options_get_number(&s->options, "base-index"); + wl = session_attach(s, w, idx, &cause); /* can't fail */ if (!args_has(self->args, 'd')) session_select(s, wl->idx); diff --git a/cmd-join-pane.c b/cmd-join-pane.c index 3d2a04b2..c384631c 100644 --- a/cmd-join-pane.c +++ b/cmd-join-pane.c @@ -35,7 +35,7 @@ enum cmd_retval join_pane(struct cmd *, struct cmd_q *, int); const struct cmd_entry cmd_join_pane_entry = { "join-pane", "joinp", "bdhvp:l:s:t:", 0, 0, - "[-bdhv] [-p percentage|-l size] [-s src-pane] [-t dst-pane]", + "[-bdhv] [-p percentage|-l size] " CMD_SRCDST_PANE_USAGE, 0, cmd_join_pane_exec }; @@ -43,7 +43,7 @@ const struct cmd_entry cmd_join_pane_entry = { const struct cmd_entry cmd_move_pane_entry = { "move-pane", "movep", "bdhvp:l:s:t:", 0, 0, - "[-bdhv] [-p percentage|-l size] [-s src-pane] [-t dst-pane]", + "[-bdhv] [-p percentage|-l size] " CMD_SRCDST_PANE_USAGE, 0, cmd_join_pane_exec }; diff --git a/cmd-move-window.c b/cmd-move-window.c index e765b625..b15df4f6 100644 --- a/cmd-move-window.c +++ b/cmd-move-window.c @@ -30,7 +30,7 @@ enum cmd_retval cmd_move_window_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_move_window_entry = { "move-window", "movew", - "dkrs:t:", 0, 0, + "adkrs:t:", 0, 0, "[-dkr] " CMD_SRCDST_WINDOW_USAGE, 0, cmd_move_window_exec @@ -38,7 +38,7 @@ const struct cmd_entry cmd_move_window_entry = { const struct cmd_entry cmd_link_window_entry = { "link-window", "linkw", - "dks:t:", 0, 0, + "adks:t:", 0, 0, "[-dk] " CMD_SRCDST_WINDOW_USAGE, 0, cmd_move_window_exec @@ -72,6 +72,15 @@ cmd_move_window_exec(struct cmd *self, struct cmd_q *cmdq) kflag = args_has(self->args, 'k'); dflag = args_has(self->args, 'd'); sflag = args_has(self->args, 's'); + + if (args_has(self->args, 'a')) { + s = cmd_find_session(cmdq, args_get(args, 't'), 0); + if (s == NULL) + return (CMD_RETURN_ERROR); + if ((idx = winlink_shuffle_up(s, s->curw)) == -1) + return (CMD_RETURN_ERROR); + } + if (server_link_window(src, wl, dst, idx, kflag, !dflag, &cause) != 0) { cmdq_error(cmdq, "can't link window: %s", cause); diff --git a/cmd-new-window.c b/cmd-new-window.c index c05a0ce8..9cead449 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -51,7 +51,7 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) struct winlink *wl; const char *cmd, *path, *template; char **argv, *cause, *cp; - int argc, idx, last, detached, cwd, fd = -1; + int argc, idx, detached, cwd, fd = -1; struct format_tree *ft; struct environ_entry *envent; @@ -59,24 +59,10 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) wl = cmd_find_window(cmdq, args_get(args, 't'), &s); if (wl == NULL) return (CMD_RETURN_ERROR); - idx = wl->idx + 1; - - /* Find the next free index. */ - for (last = idx; last < INT_MAX; last++) { - if (winlink_find_by_index(&s->windows, last) == NULL) - break; - } - if (last == INT_MAX) { + if ((idx = winlink_shuffle_up(s, wl)) == -1) { cmdq_error(cmdq, "no free window indexes"); return (CMD_RETURN_ERROR); } - - /* Move everything from last - 1 to idx up a bit. */ - for (; last > idx; last--) { - wl = winlink_find_by_index(&s->windows, last - 1); - server_link_window(s, wl, s, last, 0, 0, NULL); - server_unlink_window(s, wl); - } } else { idx = cmd_find_index(cmdq, args_get(args, 't'), &s); if (idx == -2) diff --git a/cmd-queue.c b/cmd-queue.c index 61b14147..af987f59 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -25,6 +25,8 @@ #include "tmux.h" +enum cmd_retval cmdq_continue_one(struct cmd_q *); + /* Create new command queue. */ struct cmd_q * cmdq_new(struct client *c) @@ -160,14 +162,39 @@ cmdq_append(struct cmd_q *cmdq, struct cmd_list *cmdlist, struct mouse_event *m) item->mouse.valid = 0; } +/* Process one command. */ +enum cmd_retval +cmdq_continue_one(struct cmd_q *cmdq) +{ + struct cmd *cmd = cmdq->cmd; + enum cmd_retval retval; + char tmp[1024]; + int flags = !!(cmd->flags & CMD_CONTROL); + + cmd_print(cmd, tmp, sizeof tmp); + log_debug("cmdq %p: %s", cmdq, tmp); + + cmdq->time = time(NULL); + cmdq->number++; + + cmdq_guard(cmdq, "begin", flags); + + retval = cmd->entry->exec(cmd, cmdq); + + if (retval == CMD_RETURN_ERROR) + cmdq_guard(cmdq, "error", flags); + else + cmdq_guard(cmdq, "end", flags); + return (retval); +} + /* Continue processing command queue. Returns 1 if finishes empty. */ int cmdq_continue(struct cmd_q *cmdq) { struct cmd_q_item *next; enum cmd_retval retval; - int empty, flags; - char s[1024]; + int empty; cmdq->references++; notify_disable(); @@ -184,23 +211,7 @@ cmdq_continue(struct cmd_q *cmdq) do { while (cmdq->cmd != NULL) { - cmd_print(cmdq->cmd, s, sizeof s); - log_debug("cmdq %p: %s (client %d)", cmdq, s, - cmdq->client != NULL ? cmdq->client->ibuf.fd : -1); - - cmdq->time = time(NULL); - cmdq->number++; - - flags = !!(cmdq->cmd->flags & CMD_CONTROL); - cmdq_guard(cmdq, "begin", flags); - - retval = cmdq->cmd->entry->exec(cmdq->cmd, cmdq); - - if (retval == CMD_RETURN_ERROR) - cmdq_guard(cmdq, "error", flags); - else - cmdq_guard(cmdq, "end", flags); - + retval = cmdq_continue_one(cmdq); if (retval == CMD_RETURN_ERROR) break; if (retval == CMD_RETURN_WAIT) @@ -209,7 +220,6 @@ cmdq_continue(struct cmd_q *cmdq) cmdq_flush(cmdq); goto empty; } - cmdq->cmd = TAILQ_NEXT(cmdq->cmd, qentry); } next = TAILQ_NEXT(cmdq->item, qentry); diff --git a/format.c b/format.c index 6eeb028e..59ef598c 100644 --- a/format.c +++ b/format.c @@ -272,6 +272,7 @@ format_create_status(int status) *ptr = '\0'; format_add(ft, "host_short", "%s", host); } + format_add(ft, "pid", "%ld", (long) getpid()); return (ft); } @@ -338,7 +339,7 @@ format_find(struct format_tree *ft, const char *key) case OPTIONS_STRING: return (o->str); case OPTIONS_NUMBER: - snprintf(s, sizeof s, "%lld", o->num); + xsnprintf(s, sizeof s, "%lld", o->num); return (s); case OPTIONS_STYLE: return (style_tostring(&o->style)); @@ -679,7 +680,7 @@ format_defaults_session(struct format_tree *ft, struct session *s) RB_FOREACH (wl, winlinks, &s->windows) { if ((wl->flags & WINLINK_ALERTFLAGS) == 0) continue; - snprintf(tmp, sizeof tmp, "%u", wl->idx); + xsnprintf(tmp, sizeof tmp, "%u", wl->idx); if (*alerts != '\0') strlcat(alerts, ",", sizeof alerts); @@ -704,6 +705,7 @@ format_defaults_client(struct format_tree *ft, struct client *c) if (ft->s == NULL) ft->s = c->session; + format_add(ft, "client_pid", "%ld", (long) c->pid); format_add(ft, "client_height", "%u", c->tty.sy); format_add(ft, "client_width", "%u", c->tty.sx); if (c->tty.path != NULL) @@ -748,6 +750,7 @@ void format_defaults_window(struct format_tree *ft, struct window *w) { char *layout; + time_t t; ft->w = w; @@ -756,6 +759,10 @@ format_defaults_window(struct format_tree *ft, struct window *w) else layout = layout_dump(w->layout_root); + t = w->activity_time.tv_sec; + format_add(ft, "window_activity", "%lld", (long long) t); + format_add(ft, "window_activity_string", "%s", format_time_string(t)); + format_add(ft, "window_id", "@%u", w->id); format_add(ft, "window_name", "%s", w->name); format_add(ft, "window_width", "%u", w->sx); @@ -873,8 +880,7 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp) format_add(ft, "pane_synchronized", "%d", !!options_get_number(&wp->window->options, "synchronize-panes")); - if (wp->tty != NULL) - format_add(ft, "pane_tty", "%s", wp->tty); + format_add(ft, "pane_tty", "%s", wp->tty); format_add(ft, "pane_pid", "%ld", (long) wp->pid); if ((cwd = osdep_get_cwd(wp->fd)) != NULL) format_add(ft, "pane_current_path", "%s", cwd); diff --git a/input.c b/input.c index 7a371c62..d1ff17fe 100644 --- a/input.c +++ b/input.c @@ -20,6 +20,7 @@ #include #include +#include #include "tmux.h" @@ -849,6 +850,9 @@ input_parse(struct window_pane *wp) wp->window->flags |= WINDOW_ACTIVITY; wp->window->flags &= ~WINDOW_SILENCE; + if (gettimeofday(&wp->window->activity_time, NULL) != 0) + fatal("gettimeofday failed"); + /* * Open the screen. Use NULL wp if there is a mode set as don't want to * update the tty. diff --git a/job.c b/job.c index 74b316ed..ff21b3b6 100644 --- a/job.c +++ b/job.c @@ -99,6 +99,8 @@ job_run(const char *cmd, struct session *s, int cwd, close(out[1]); job = xmalloc(sizeof *job); + job->state = JOB_RUNNING; + job->cmd = xstrdup(cmd); job->pid = pid; job->status = 0; @@ -166,14 +168,13 @@ job_callback(unused struct bufferevent *bufev, unused short events, void *data) log_debug("job error %p: %s, pid %ld", job, job->cmd, (long) job->pid); - if (job->pid == -1) { + if (job->state == JOB_DEAD) { if (job->callbackfn != NULL) job->callbackfn(job); job_free(job); } else { bufferevent_disable(job->event, EV_READ); - close(job->fd); - job->fd = -1; + job->state = JOB_CLOSED; } } @@ -185,10 +186,12 @@ job_died(struct job *job, int status) job->status = status; - if (job->fd == -1) { + if (job->state == JOB_CLOSED) { if (job->callbackfn != NULL) job->callbackfn(job); job_free(job); - } else + } else { job->pid = -1; + job->state = JOB_DEAD; + } } diff --git a/server-client.c b/server-client.c index 27efc57d..c9c0c3ef 100644 --- a/server-client.c +++ b/server-client.c @@ -1053,6 +1053,7 @@ server_client_msg_dispatch(struct client *c) case MSG_IDENTIFY_CWD: case MSG_IDENTIFY_STDIN: case MSG_IDENTIFY_ENVIRON: + case MSG_IDENTIFY_CLIENTPID: case MSG_IDENTIFY_DONE: server_client_msg_identify(c, &imsg); break; @@ -1227,6 +1228,11 @@ server_client_msg_identify(struct client *c, struct imsg *imsg) if (strchr(data, '=') != NULL) environ_put(&c->environ, data); break; + case MSG_IDENTIFY_CLIENTPID: + if (datalen != sizeof c->pid) + fatalx("bad MSG_IDENTIFY_CLIENTPID size"); + memcpy(&c->pid, data, sizeof c->pid); + break; default: break; } diff --git a/tmux.1 b/tmux.1 index f3eeabcc..20754551 100644 --- a/tmux.1 +++ b/tmux.1 @@ -408,14 +408,15 @@ An pattern which is matched against the session name. .El .Pp -If the session name is prefixed with a -.Ql = : , +If the session name is prefixed with an +.Ql = , only an exact match is accepted (so .Ql =mysess will only match exactly .Ql mysess , not .Ql mysession ) . +.Pp If a single session is found, it is used as the target session; multiple matches produce an error. If a session is omitted, the current session is used if available; if no @@ -1237,12 +1238,14 @@ Commands related to windows and panes are as follows: .It Xo Ic break-pane .Op Fl dP .Op Fl F Ar format -.Op Fl t Ar target-pane +.Op Fl s Ar src-pane +.Op Fl t Ar dst-pane .Xc .D1 (alias: Ic breakp ) Break -.Ar target-pane -off from its containing window to make it the only pane in a new window. +.Ar src-pane +off from its containing window to make it the only pane in +.Ar dst-window . If .Fl d is given, the new window does not become the current window. @@ -1548,7 +1551,7 @@ If no .Ar target-session is specified, select the last window of the current session. .It Xo Ic link-window -.Op Fl dk +.Op Fl adk .Op Fl s Ar src-window .Op Fl t Ar dst-window .Xc @@ -1562,6 +1565,10 @@ If is specified and no such window exists, the .Ar src-window is linked there. +With +.Fl a , +the window is moved to the next index up (following windows +are moved if necessary). If .Fl k is given and @@ -1627,7 +1634,7 @@ and .Ar dst-pane may belong to the same window. .It Xo Ic move-window -.Op Fl rdk +.Op Fl ardk .Op Fl s Ar src-window .Op Fl t Ar dst-window .Xc @@ -3357,6 +3364,7 @@ The following variables are available, where appropriate: .It Li "client_created_string" Ta "" Ta "String time client created" .It Li "client_height" Ta "" Ta "Height of client" .It Li "client_last_session" Ta "" Ta "Name of the client's last session" +.It Li "client_pid" Ta "" Ta "PID of client process" .It Li "client_prefix" Ta "" Ta "1 if prefix key has been pressed" .It Li "client_readonly" Ta "" Ta "1 if client is readonly" .It Li "client_session" Ta "" Ta "Name of the client's session" @@ -3401,6 +3409,7 @@ The following variables are available, where appropriate: .It Li "pane_top" Ta "" Ta "Top of pane" .It Li "pane_tty" Ta "" Ta "Pseudo terminal of pane" .It Li "pane_width" Ta "" Ta "Width of pane" +.It Li "pid" Ta "" Ta "Server PID" .It Li "scroll_region_lower" Ta "" Ta "Bottom of scroll region in pane" .It Li "scroll_region_upper" Ta "" Ta "Top of scroll region in pane" .It Li "session_alerts" Ta "" Ta "List of window indexes with alerts" @@ -3417,6 +3426,8 @@ The following variables are available, where appropriate: .It Li "session_name" Ta "#S" Ta "Name of session" .It Li "session_width" Ta "" Ta "Width of session" .It Li "session_windows" Ta "" Ta "Number of windows in session" +.It Li "window_activity" Ta "" Ta "Integer time of window last activity" +.It Li "window_activity_string" Ta "" Ta "String time of window last activity" .It Li "window_active" Ta "" Ta "1 if window active" .It Li "window_activity_flag" Ta "" Ta "1 if window has activity alert" .It Li "window_bell_flag" Ta "" Ta "1 if window has bell" diff --git a/tmux.h b/tmux.h index 210826ad..f0fef0d5 100644 --- a/tmux.h +++ b/tmux.h @@ -429,6 +429,7 @@ enum msgtype { MSG_IDENTIFY_STDIN, MSG_IDENTIFY_ENVIRON, MSG_IDENTIFY_DONE, + MSG_IDENTIFY_CLIENTPID, MSG_COMMAND = 200, MSG_DETACH, @@ -717,6 +718,12 @@ struct options { /* Scheduled job. */ struct job { + enum { + JOB_RUNNING, + JOB_DEAD, + JOB_CLOSED + } state; + char *cmd; pid_t pid; int status; @@ -895,6 +902,7 @@ struct window { char *name; struct event name_timer; struct timeval silence_timer; + struct timeval activity_time; struct window_pane *active; struct window_pane *last; @@ -1210,6 +1218,7 @@ RB_HEAD(status_out_tree, status_out); struct client { struct imsgbuf ibuf; + pid_t pid; int fd; struct event event; int retval; @@ -2161,6 +2170,7 @@ struct window_pane *window_pane_find_right(struct window_pane *); void window_set_name(struct window *, const char *); void window_remove_ref(struct window *); void winlink_clear_flags(struct winlink *); +int winlink_shuffle_up(struct session *, struct winlink *); /* layout.c */ u_int layout_count_cells(struct layout_cell *); diff --git a/window.c b/window.c index 721d0213..fab9949e 100644 --- a/window.c +++ b/window.c @@ -294,6 +294,9 @@ window_create1(u_int sx, u_int sy) w->sx = sx; w->sy = sy; + if (gettimeofday(&w->activity_time, NULL) != 0) + fatal("gettimeofday failed"); + options_init(&w->options, &global_w_options); if (options_get_number(&w->options, "automatic-rename")) queue_window_name(w); @@ -1389,3 +1392,28 @@ winlink_clear_flags(struct winlink *wl) } } } + +int +winlink_shuffle_up(struct session *s, struct winlink *wl) +{ + int idx, last; + + idx = wl->idx + 1; + + /* Find the next free index. */ + for (last = idx; last < INT_MAX; last++) { + if (winlink_find_by_index(&s->windows, last) == NULL) + break; + } + if (last == INT_MAX) + return (-1); + + /* Move everything from last - 1 to idx up a bit. */ + for (; last > idx; last--) { + wl = winlink_find_by_index(&s->windows, last - 1); + server_link_window(s, wl, s, last, 0, 0, NULL); + server_unlink_window(s, wl); + } + + return (idx); +}