From ce53936a2b56521903d69bac565391e37ad7e115 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 17 Jul 2009 15:03:11 +0000 Subject: [PATCH] Tidy up new-session and attach-session and change them to work from inside tmux, switching the current client to the new or requested session. Written with Josh Elsasser. --- cmd-attach-session.c | 59 +++++++++++++------- cmd-new-session.c | 129 ++++++++++++++++++++++++++----------------- tmux.1 | 6 +- tmux.h | 10 ++++ 4 files changed, 133 insertions(+), 71 deletions(-) diff --git a/cmd-attach-session.c b/cmd-attach-session.c index 196d5d13..aae8abe7 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -44,10 +44,9 @@ cmd_attach_session_exec(struct cmd *self, struct cmd_ctx *ctx) { struct cmd_target_data *data = self->data; struct session *s; + struct client *c; char *cause; - - if (ctx->curclient != NULL) - return (0); + u_int i; if (ARRAY_LENGTH(&sessions) == 0) { ctx->error(ctx, "no sessions"); @@ -56,24 +55,44 @@ cmd_attach_session_exec(struct cmd *self, struct cmd_ctx *ctx) if ((s = cmd_find_session(ctx, data->target)) == NULL) return (-1); - if (!(ctx->cmdclient->flags & CLIENT_TERMINAL)) { - ctx->error(ctx, "not a terminal"); - return (-1); + if (ctx->cmdclient == NULL) { + if (data->chflags & CMD_CHFLAG('d')) { + /* + * Can't use server_write_session in case attaching to + * the same session as currently attached to. + */ + for (i = 0; i < ARRAY_LENGTH(&clients); i++) { + c = ARRAY_ITEM(&clients, i); + if (c == NULL || c->session != s) + continue; + if (c == ctx->curclient) + continue; + server_write_client(c, MSG_DETACH, NULL, 0); + } + } + + ctx->curclient->session = s; + server_redraw_client(ctx->curclient); + } else { + if (!(ctx->cmdclient->flags & CLIENT_TERMINAL)) { + ctx->error(ctx, "not a terminal"); + return (-1); + } + + if (tty_open(&ctx->cmdclient->tty, &cause) != 0) { + ctx->error(ctx, "terminal open failed: %s", cause); + xfree(cause); + return (-1); + } + + if (data->chflags & CMD_CHFLAG('d')) + server_write_session(s, MSG_DETACH, NULL, 0); + + ctx->cmdclient->session = s; + server_write_client(ctx->cmdclient, MSG_READY, NULL, 0); + server_redraw_client(ctx->cmdclient); } - - if (tty_open(&ctx->cmdclient->tty, &cause) != 0) { - ctx->error(ctx, "terminal open failed: %s", cause); - xfree(cause); - return (-1); - } - - if (data->chflags & CMD_CHFLAG('d')) - server_write_session(s, MSG_DETACH, NULL, 0); - ctx->cmdclient->session = s; - - server_write_client(ctx->cmdclient, MSG_READY, NULL, 0); recalculate_sizes(); - server_redraw_client(ctx->cmdclient); - return (1); + return (1); /* 1 means don't tell command client to exit */ } diff --git a/cmd-new-session.c b/cmd-new-session.c index cf086a87..53e752ac 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -111,65 +111,80 @@ int cmd_new_session_exec(struct cmd *self, struct cmd_ctx *ctx) { struct cmd_new_session_data *data = self->data; - struct client *c = ctx->cmdclient; struct session *s; char *cmd, *cwd, *cause; u_int sx, sy; - if (ctx->curclient != NULL) - return (0); - - if (!data->flag_detached) { - if (c == NULL) { - ctx->error(ctx, "no client to attach to"); - return (-1); - } - if (!(c->flags & CLIENT_TERMINAL)) { - ctx->error(ctx, "not a terminal"); - return (-1); - } - } - if (data->newname != NULL && session_find(data->newname) != NULL) { ctx->error(ctx, "duplicate session: %s", data->newname); return (-1); } - cmd = data->cmd; - if (cmd == NULL) - cmd = options_get_string(&global_s_options, "default-command"); - if (c == NULL || c->cwd == NULL) - cwd = options_get_string(&global_s_options, "default-path"); + /* + * There are two cases: + * + * 1. If cmdclient is non-NULL, new-session has been called from the + * command-line - cmdclient is to become a new attached, interactive + * client. Unless -d is given, the terminal must be opened and then + * the client sent MSG_READY. + * + * 2. If cmdclient is NULL, new-session has been called from an + * existing client (such as a key binding). + * + * In both cases, a new additional session needs to be created and + * (unless -d) set as the current session for the client. + */ + + /* Open the terminal if necessary. */ + if (!data->flag_detached && ctx->cmdclient != NULL) { + if (!(ctx->cmdclient->flags & CLIENT_TERMINAL)) { + ctx->error(ctx, "not a terminal"); + return (-1); + } + + if (tty_open(&ctx->cmdclient->tty, &cause) != 0) { + ctx->error(ctx, "open terminal failed: %s", cause); + xfree(cause); + return (-1); + } + } + + /* Find new session size and options. */ + if (data->flag_detached) { + sx = 80; + sy = 25; + } else { + if (ctx->cmdclient != NULL) { + sx = ctx->cmdclient->tty.sx; + sy = ctx->cmdclient->tty.sy; + } else { + sx = ctx->curclient->tty.sx; + sy = ctx->curclient->tty.sy; + } + } + if (sy > 0 && options_get_number(&global_s_options, "status")) + sy--; + if (sx == 0) + sx = 1; + if (sy == 0) + sy = 1; + if (ctx->cmdclient != NULL && ctx->cmdclient->cwd != NULL) + cwd = ctx->cmdclient->cwd; else - cwd = c->cwd; - - sx = 80; - sy = 25; - if (!data->flag_detached) { - sx = c->tty.sx; - sy = c->tty.sy; - } - - if (options_get_number(&global_s_options, "status")) { - if (sy == 0) - sy = 1; - else - sy--; - } - - if (!data->flag_detached && tty_open(&c->tty, &cause) != 0) { - ctx->error(ctx, "open terminal failed: %s", cause); - xfree(cause); - return (-1); - } - + cwd = options_get_string(&global_s_options, "default-path"); + if (data->cmd != NULL) + cmd = data->cmd; + else + cmd = options_get_string(&global_s_options, "default-command"); + /* Create the new session. */ s = session_create(data->newname, cmd, cwd, sx, sy, &cause); if (s == NULL) { ctx->error(ctx, "create session failed: %s", cause); xfree(cause); return (-1); } + if (data->winname != NULL) { xfree(s->curw->window->name); s->curw->window->name = xstrdup(data->winname); @@ -177,17 +192,31 @@ cmd_new_session_exec(struct cmd *self, struct cmd_ctx *ctx) &s->curw->window->options, "automatic-rename", 0); } - if (data->flag_detached) { - if (c != NULL) - server_write_client(c, MSG_EXIT, NULL, 0); - } else { - c->session = s; - server_write_client(c, MSG_READY, NULL, 0); - server_redraw_client(c); + /* + * If a command client exists, it is either taking this session (and + * needs to get MSG_READY and stay around), or -d is given and it needs + * to exit. + */ + if (ctx->cmdclient != NULL) { + if (!data->flag_detached) + server_write_client(ctx->cmdclient, MSG_READY, NULL, 0); + else + server_write_client(ctx->cmdclient, MSG_EXIT, NULL, 0); + } + + /* Set the client to the new session. */ + if (!data->flag_detached) { + if (ctx->cmdclient != NULL) { + ctx->cmdclient->session = s; + server_redraw_client(ctx->cmdclient); + } else { + ctx->curclient->session = s; + server_redraw_client(ctx->curclient); + } } recalculate_sizes(); - return (1); + return (1); /* 1 means don't tell command client to exit */ } void diff --git a/tmux.1 b/tmux.1 index 34501931..dcc30a25 100644 --- a/tmux.1 +++ b/tmux.1 @@ -596,7 +596,11 @@ The following commands are available: .Op Fl t Ar target-session .Xc .D1 (alias: Ic attach ) -Create a new client in the current terminal and attach it to a session. +If run from outside +.Nm , +create a new client in the current terminal and attach it to +.Ar target-session . +If used from inside, switch the current client. If .Fl d is specified, any other clients attached to the session are detached. diff --git a/tmux.h b/tmux.h index 0720b25b..60455df1 100644 --- a/tmux.h +++ b/tmux.h @@ -835,8 +835,18 @@ struct client_ctx { struct cmd_ctx { struct client *cmdclient; + /* + * curclient is the client where this command was executed if inside + * tmux. This is NULL if the command came from the command-line. + * + * cmdclient is the client which sent the MSG_COMMAND to the server, if + * any. This is NULL unless the command came from the command-line. + * + * One of curclient or cmdclient is always NULL and the other not. + */ struct client *curclient; struct session *cursession; + struct msg_command_data *msgdata; void (*print)(struct cmd_ctx *, const char *, ...);