Make recalculate_sizes() handle an empty window with no active
[tmux-openbsd.git] / cmd-new-session.c
blob653db876d5e01f17de83750e0f8a79e5bef4c19c
1 /* $OpenBSD$ */
3 /*
4 * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 #include <sys/types.h>
21 #include <pwd.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <termios.h>
25 #include <unistd.h>
27 #include "tmux.h"
30 * Create a new session and attach to the current terminal unless -d is given.
33 enum cmd_retval cmd_new_session_check(struct args *);
34 enum cmd_retval cmd_new_session_exec(struct cmd *, struct cmd_q *);
36 const struct cmd_entry cmd_new_session_entry = {
37 "new-session", "new",
38 "AdDF:n:Ps:t:x:y:", 0, 1,
39 "[-AdDP] [-F format] [-n window-name] [-s session-name] "
40 CMD_TARGET_SESSION_USAGE " [-x width] [-y height] [command]",
41 CMD_STARTSERVER|CMD_CANTNEST|CMD_SENDENVIRON,
42 NULL,
43 cmd_new_session_check,
44 cmd_new_session_exec
47 enum cmd_retval
48 cmd_new_session_check(struct args *args)
50 if (args_has(args, 't') && (args->argc != 0 || args_has(args, 'n')))
51 return (CMD_RETURN_ERROR);
52 return (CMD_RETURN_NORMAL);
55 enum cmd_retval
56 cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq)
58 struct args *args = self->args;
59 struct client *c = cmdq->client;
60 struct session *s, *groupwith;
61 struct window *w;
62 struct environ env;
63 struct termios tio, *tiop;
64 struct passwd *pw;
65 const char *newname, *target, *update, *cwd, *errstr;
66 const char *template;
67 char *cmd, *cause, *cp;
68 int detached, idx;
69 u_int sx, sy;
70 int already_attached;
71 struct format_tree *ft;
73 newname = args_get(args, 's');
74 if (newname != NULL) {
75 if (!session_check_name(newname)) {
76 cmdq_error(cmdq, "bad session name: %s", newname);
77 return (CMD_RETURN_ERROR);
79 if (session_find(newname) != NULL) {
80 if (args_has(args, 'A')) {
81 return (cmd_attach_session(cmdq, newname,
82 args_has(args, 'D'), 0));
84 cmdq_error(cmdq, "duplicate session: %s", newname);
85 return (CMD_RETURN_ERROR);
89 target = args_get(args, 't');
90 if (target != NULL) {
91 groupwith = cmd_find_session(cmdq, target, 0);
92 if (groupwith == NULL)
93 return (CMD_RETURN_ERROR);
94 } else
95 groupwith = NULL;
97 /* Set -d if no client. */
98 detached = args_has(args, 'd');
99 if (c == NULL)
100 detached = 1;
102 /* Is this client already attached? */
103 already_attached = 0;
104 if (c != NULL && c->session != NULL)
105 already_attached = 1;
108 * Save the termios settings, part of which is used for new windows in
109 * this session.
111 * This is read again with tcgetattr() rather than using tty.tio as if
112 * detached, tty_open won't be called. Because of this, it must be done
113 * before opening the terminal as that calls tcsetattr() to prepare for
114 * tmux taking over.
116 if (!detached && !already_attached && c->tty.fd != -1) {
117 if (tcgetattr(c->tty.fd, &tio) != 0)
118 fatal("tcgetattr failed");
119 tiop = &tio;
120 } else
121 tiop = NULL;
123 /* Open the terminal if necessary. */
124 if (!detached && !already_attached) {
125 if (server_client_open(c, NULL, &cause) != 0) {
126 cmdq_error(cmdq, "open terminal failed: %s", cause);
127 free(cause);
128 return (CMD_RETURN_ERROR);
132 /* Get the new session working directory. */
133 if (c != NULL && c->cwd != NULL)
134 cwd = c->cwd;
135 else {
136 pw = getpwuid(getuid());
137 if (pw->pw_dir != NULL && *pw->pw_dir != '\0')
138 cwd = pw->pw_dir;
139 else
140 cwd = "/";
143 /* Find new session size. */
144 if (c != NULL) {
145 sx = c->tty.sx;
146 sy = c->tty.sy;
147 } else {
148 sx = 80;
149 sy = 24;
151 if (detached && args_has(args, 'x')) {
152 sx = strtonum(args_get(args, 'x'), 1, USHRT_MAX, &errstr);
153 if (errstr != NULL) {
154 cmdq_error(cmdq, "width %s", errstr);
155 return (CMD_RETURN_ERROR);
158 if (detached && args_has(args, 'y')) {
159 sy = strtonum(args_get(args, 'y'), 1, USHRT_MAX, &errstr);
160 if (errstr != NULL) {
161 cmdq_error(cmdq, "height %s", errstr);
162 return (CMD_RETURN_ERROR);
165 if (sy > 0 && options_get_number(&global_s_options, "status"))
166 sy--;
167 if (sx == 0)
168 sx = 1;
169 if (sy == 0)
170 sy = 1;
172 /* Figure out the command for the new window. */
173 if (target != NULL)
174 cmd = NULL;
175 else if (args->argc != 0)
176 cmd = args->argv[0];
177 else
178 cmd = options_get_string(&global_s_options, "default-command");
180 /* Construct the environment. */
181 environ_init(&env);
182 update = options_get_string(&global_s_options, "update-environment");
183 if (c != NULL)
184 environ_update(update, &c->environ, &env);
186 /* Create the new session. */
187 idx = -1 - options_get_number(&global_s_options, "base-index");
188 s = session_create(newname, cmd, cwd, &env, tiop, idx, sx, sy, &cause);
189 if (s == NULL) {
190 cmdq_error(cmdq, "create session failed: %s", cause);
191 free(cause);
192 return (CMD_RETURN_ERROR);
194 environ_free(&env);
196 /* Set the initial window name if one given. */
197 if (cmd != NULL && args_has(args, 'n')) {
198 w = s->curw->window;
199 window_set_name(w, args_get(args, 'n'));
200 options_set_number(&w->options, "automatic-rename", 0);
204 * If a target session is given, this is to be part of a session group,
205 * so add it to the group and synchronize.
207 if (groupwith != NULL) {
208 session_group_add(groupwith, s);
209 session_group_synchronize_to(s);
210 session_select(s, RB_ROOT(&s->windows)->idx);
214 * Set the client to the new session. If a command client exists, it is
215 * taking this session and needs to get MSG_READY and stay around.
217 if (!detached) {
218 if (!already_attached)
219 server_write_ready(c);
220 else if (c->session != NULL)
221 c->last_session = c->session;
222 c->session = s;
223 notify_attached_session_changed(c);
224 session_update_activity(s);
225 server_redraw_client(c);
227 recalculate_sizes();
228 server_update_socket();
231 * If there are still configuration file errors to display, put the new
232 * session's current window into more mode and display them now.
234 if (cfg_finished)
235 cfg_show_causes(s);
237 /* Print if requested. */
238 if (args_has(args, 'P')) {
239 if ((template = args_get(args, 'F')) == NULL)
240 template = NEW_SESSION_TEMPLATE;
242 ft = format_create();
243 if ((c = cmd_find_client(cmdq, NULL, 1)) != NULL)
244 format_client(ft, c);
245 format_session(ft, s);
247 cp = format_expand(ft, template);
248 cmdq_print(cmdq, "%s", cp);
249 free(cp);
251 format_free(ft);
254 if (!detached)
255 cmdq->client_exit = 0;
256 return (CMD_RETURN_NORMAL);