tmux  3.2a
About: tmux is a terminal multiplexer that lets you switch easily between several programs in one terminal.
  Fossies Dox: tmux-3.2a.tar.gz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

spawn.c
Go to the documentation of this file.
1 /* $OpenBSD$ */
2 
3 /*
4  * Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
5  *
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.
9  *
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.
17  */
18 
19 #include <sys/types.h>
20 
21 #include <errno.h>
22 #include <signal.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <unistd.h>
26 
27 #include "tmux.h"
28 
29 /*
30  * Set up the environment and create a new window and pane or a new pane.
31  *
32  * We need to set up the following items:
33  *
34  * - history limit, comes from the session;
35  *
36  * - base index, comes from the session;
37  *
38  * - current working directory, may be specified - if it isn't it comes from
39  * either the client or the session;
40  *
41  * - PATH variable, comes from the client if any, otherwise from the session
42  * environment;
43  *
44  * - shell, comes from default-shell;
45  *
46  * - termios, comes from the session;
47  *
48  * - remaining environment, comes from the session.
49  */
50 
51 static void
52 spawn_log(const char *from, struct spawn_context *sc)
53 {
54  struct session *s = sc->s;
55  struct winlink *wl = sc->wl;
56  struct window_pane *wp0 = sc->wp0;
57  const char *name = cmdq_get_name(sc->item);
58  char tmp[128];
59 
60  log_debug("%s: %s, flags=%#x", from, name, sc->flags);
61 
62  if (wl != NULL && wp0 != NULL)
63  xsnprintf(tmp, sizeof tmp, "wl=%d wp0=%%%u", wl->idx, wp0->id);
64  else if (wl != NULL)
65  xsnprintf(tmp, sizeof tmp, "wl=%d wp0=none", wl->idx);
66  else if (wp0 != NULL)
67  xsnprintf(tmp, sizeof tmp, "wl=none wp0=%%%u", wp0->id);
68  else
69  xsnprintf(tmp, sizeof tmp, "wl=none wp0=none");
70  log_debug("%s: s=$%u %s idx=%d", from, s->id, tmp, sc->idx);
71  log_debug("%s: name=%s", from, sc->name == NULL ? "none" : sc->name);
72 }
73 
74 struct winlink *
75 spawn_window(struct spawn_context *sc, char **cause)
76 {
77  struct cmdq_item *item = sc->item;
78  struct client *c = cmdq_get_client(item);
79  struct session *s = sc->s;
80  struct window *w;
81  struct window_pane *wp;
82  struct winlink *wl;
83  int idx = sc->idx;
84  u_int sx, sy, xpixel, ypixel;
85 
86  spawn_log(__func__, sc);
87 
88  /*
89  * If the window already exists, we are respawning, so destroy all the
90  * panes except one.
91  */
92  if (sc->flags & SPAWN_RESPAWN) {
93  w = sc->wl->window;
94  if (~sc->flags & SPAWN_KILL) {
95  TAILQ_FOREACH(wp, &w->panes, entry) {
96  if (wp->fd != -1)
97  break;
98  }
99  if (wp != NULL) {
100  xasprintf(cause, "window %s:%d still active",
101  s->name, sc->wl->idx);
102  return (NULL);
103  }
104  }
105 
106  sc->wp0 = TAILQ_FIRST(&w->panes);
107  TAILQ_REMOVE(&w->panes, sc->wp0, entry);
108 
109  layout_free(w);
111 
112  TAILQ_INSERT_HEAD(&w->panes, sc->wp0, entry);
113  window_pane_resize(sc->wp0, w->sx, w->sy);
114 
115  layout_init(w, sc->wp0);
116  window_set_active_pane(w, sc->wp0, 0);
117  }
118 
119  /*
120  * Otherwise we have no window so we will need to create one. First
121  * check if the given index already exists and destroy it if so.
122  */
123  if ((~sc->flags & SPAWN_RESPAWN) && idx != -1) {
124  wl = winlink_find_by_index(&s->windows, idx);
125  if (wl != NULL && (~sc->flags & SPAWN_KILL)) {
126  xasprintf(cause, "index %d in use", idx);
127  return (NULL);
128  }
129  if (wl != NULL) {
130  /*
131  * Can't use session_detach as it will destroy session
132  * if this makes it empty.
133  */
134  wl->flags &= ~~WINLINK_ALERTFLAGS;
135  notify_session_window("window-unlinked", s, wl->window);
136  winlink_stack_remove(&s->lastw, wl);
137  winlink_remove(&s->windows, wl);
138 
139  if (s->curw == wl) {
140  s->curw = NULL;
141  sc->flags &= ~~SPAWN_DETACHED;
142  }
143  }
144  }
145 
146  /* Then create a window if needed. */
147  if (~sc->flags & SPAWN_RESPAWN) {
148  if (idx == -1)
149  idx = -1 - options_get_number(s->options, "base-index");
150  if ((sc->wl = winlink_add(&s->windows, idx)) == NULL) {
151  xasprintf(cause, "couldn't add window %d", idx);
152  return (NULL);
153  }
154  default_window_size(sc->tc, s, NULL, &sx, &sy, &xpixel, &ypixel,
155  -1);
156  if ((w = window_create(sx, sy, xpixel, ypixel)) == NULL) {
157  winlink_remove(&s->windows, sc->wl);
158  xasprintf(cause, "couldn't create window %d", idx);
159  return (NULL);
160  }
161  if (s->curw == NULL)
162  s->curw = sc->wl;
163  sc->wl->session = s;
164  w->latest = sc->tc;
165  winlink_set_window(sc->wl, w);
166  } else
167  w = NULL;
168  sc->flags |= SPAWN_NONOTIFY;
169 
170  /* Spawn the pane. */
171  wp = spawn_pane(sc, cause);
172  if (wp == NULL) {
173  if (~sc->flags & SPAWN_RESPAWN)
174  winlink_remove(&s->windows, sc->wl);
175  return (NULL);
176  }
177 
178  /* Set the name of the new window. */
179  if (~sc->flags & SPAWN_RESPAWN) {
180  if (sc->name != NULL) {
181  w->name = format_single(item, sc->name, c, s, NULL,
182  NULL);
183  options_set_number(w->options, "automatic-rename", 0);
184  } else
185  w->name = default_window_name(w);
186  }
187 
188  /* Switch to the new window if required. */
189  if (~sc->flags & SPAWN_DETACHED)
190  session_select(s, sc->wl->idx);
191 
192  /* Fire notification if new window. */
193  if (~sc->flags & SPAWN_RESPAWN)
194  notify_session_window("window-linked", s, w);
195 
197  return (sc->wl);
198 }
199 
200 struct window_pane *
201 spawn_pane(struct spawn_context *sc, char **cause)
202 {
203  struct cmdq_item *item = sc->item;
204  struct cmd_find_state *target = cmdq_get_target(item);
205  struct client *c = cmdq_get_client(item);
206  struct session *s = sc->s;
207  struct window *w = sc->wl->window;
208  struct window_pane *new_wp;
209  struct environ *child;
210  struct environ_entry *ee;
211  char **argv, *cp, **argvp, *argv0, *cwd;
212  const char *cmd, *tmp;
213  int argc;
214  u_int idx;
215  struct termios now;
216  u_int hlimit;
217  struct winsize ws;
218  sigset_t set, oldset;
219  key_code key;
220 
221  spawn_log(__func__, sc);
222 
223  /*
224  * Work out the current working directory. If respawning, use
225  * the pane's stored one unless specified.
226  */
227  if (sc->cwd != NULL)
228  cwd = format_single(item, sc->cwd, c, target->s, NULL, NULL);
229  else if (~sc->flags & SPAWN_RESPAWN)
230  cwd = xstrdup(server_client_get_cwd(c, target->s));
231  else
232  cwd = NULL;
233 
234  /*
235  * If we are respawning then get rid of the old process. Otherwise
236  * either create a new cell or assign to the one we are given.
237  */
238  hlimit = options_get_number(s->options, "history-limit");
239  if (sc->flags & SPAWN_RESPAWN) {
240  if (sc->wp0->fd != -1 && (~sc->flags & SPAWN_KILL)) {
241  window_pane_index(sc->wp0, &idx);
242  xasprintf(cause, "pane %s:%d.%u still active",
243  s->name, sc->wl->idx, idx);
244  free(cwd);
245  return (NULL);
246  }
247  if (sc->wp0->fd != -1) {
248  bufferevent_free(sc->wp0->event);
249  close(sc->wp0->fd);
250  }
252  screen_reinit(&sc->wp0->base);
253  input_free(sc->wp0->ictx);
254  sc->wp0->ictx = NULL;
255  new_wp = sc->wp0;
257  } else if (sc->lc == NULL) {
258  new_wp = window_add_pane(w, NULL, hlimit, sc->flags);
259  layout_init(w, new_wp);
260  } else {
261  new_wp = window_add_pane(w, sc->wp0, hlimit, sc->flags);
262  if (sc->flags & SPAWN_ZOOM)
263  layout_assign_pane(sc->lc, new_wp, 1);
264  else
265  layout_assign_pane(sc->lc, new_wp, 0);
266  }
267 
268  /*
269  * Now we have a pane with nothing running in it ready for the new
270  * process. Work out the command and arguments and store the working
271  * directory.
272  */
273  if (sc->argc == 0 && (~sc->flags & SPAWN_RESPAWN)) {
274  cmd = options_get_string(s->options, "default-command");
275  if (cmd != NULL && *cmd != '\0') {
276  argc = 1;
277  argv = (char **)&cmd;
278  } else {
279  argc = 0;
280  argv = NULL;
281  }
282  } else {
283  argc = sc->argc;
284  argv = sc->argv;
285  }
286  if (cwd != NULL) {
287  free(new_wp->cwd);
288  new_wp->cwd = cwd;
289  }
290 
291  /*
292  * Replace the stored arguments if there are new ones. If not, the
293  * existing ones will be used (they will only exist for respawn).
294  */
295  if (argc > 0) {
296  cmd_free_argv(new_wp->argc, new_wp->argv);
297  new_wp->argc = argc;
298  new_wp->argv = cmd_copy_argv(argc, argv);
299  }
300 
301  /* Create an environment for this pane. */
302  child = environ_for_session(s, 0);
303  if (sc->environ != NULL)
304  environ_copy(sc->environ, child);
305  environ_set(child, "TMUX_PANE", 0, "%%%u", new_wp->id);
306 
307  /*
308  * Then the PATH environment variable. The session one is replaced from
309  * the client if there is one because otherwise running "tmux new
310  * myprogram" wouldn't work if myprogram isn't in the session's path.
311  */
312  if (c != NULL && c->session == NULL) { /* only unattached clients */
313  ee = environ_find(c->environ, "PATH");
314  if (ee != NULL)
315  environ_set(child, "PATH", 0, "%s", ee->value);
316  }
317  if (environ_find(child, "PATH") == NULL)
318  environ_set(child, "PATH", 0, "%s", _PATH_DEFPATH);
319 
320  /* Then the shell. If respawning, use the old one. */
321  if (~sc->flags & SPAWN_RESPAWN) {
322  tmp = options_get_string(s->options, "default-shell");
323  if (!checkshell(tmp))
324  tmp = _PATH_BSHELL;
325  free(new_wp->shell);
326  new_wp->shell = xstrdup(tmp);
327  }
328  environ_set(child, "SHELL", 0, "%s", new_wp->shell);
329 
330  /* Log the arguments we are going to use. */
331  log_debug("%s: shell=%s", __func__, new_wp->shell);
332  if (new_wp->argc != 0) {
333  cp = cmd_stringify_argv(new_wp->argc, new_wp->argv);
334  log_debug("%s: cmd=%s", __func__, cp);
335  free(cp);
336  }
337  if (cwd != NULL)
338  log_debug("%s: cwd=%s", __func__, cwd);
339  cmd_log_argv(new_wp->argc, new_wp->argv, "%s", __func__);
340  environ_log(child, "%s: environment ", __func__);
341 
342  /* Initialize the window size. */
343  memset(&ws, 0, sizeof ws);
344  ws.ws_col = screen_size_x(&new_wp->base);
345  ws.ws_row = screen_size_y(&new_wp->base);
346  ws.ws_xpixel = w->xpixel * ws.ws_col;
347  ws.ws_ypixel = w->ypixel * ws.ws_row;
348 
349  /* Block signals until fork has completed. */
350  sigfillset(&set);
351  sigprocmask(SIG_BLOCK, &set, &oldset);
352 
353  /* If the command is empty, don't fork a child process. */
354  if (sc->flags & SPAWN_EMPTY) {
355  new_wp->flags |= PANE_EMPTY;
356  new_wp->base.mode &= ~~MODE_CURSOR;
357  new_wp->base.mode |= MODE_CRLF;
358  goto complete;
359  }
360 
361  /* Fork the new process. */
362  new_wp->pid = fdforkpty(ptm_fd, &new_wp->fd, new_wp->tty, NULL, &ws);
363  if (new_wp->pid == -1) {
364  xasprintf(cause, "fork failed: %s", strerror(errno));
365  new_wp->fd = -1;
366  if (~sc->flags & SPAWN_RESPAWN) {
368  layout_close_pane(new_wp);
369  window_remove_pane(w, new_wp);
370  }
371  sigprocmask(SIG_SETMASK, &oldset, NULL);
372  environ_free(child);
373  return (NULL);
374  }
375 
376  /* In the parent process, everything is done now. */
377  if (new_wp->pid != 0)
378  goto complete;
379 
380  /*
381  * Child process. Change to the working directory or home if that
382  * fails.
383  */
384  if (chdir(new_wp->cwd) != 0 &&
385  ((tmp = find_home()) == NULL || chdir(tmp) != 0) &&
386  chdir("/") != 0)
387  fatal("chdir failed");
388 
389  /*
390  * Update terminal escape characters from the session if available and
391  * force VERASE to tmux's backspace.
392  */
393  if (tcgetattr(STDIN_FILENO, &now) != 0)
394  _exit(1);
395  if (s->tio != NULL)
396  memcpy(now.c_cc, s->tio->c_cc, sizeof now.c_cc);
397  key = options_get_number(global_options, "backspace");
398  if (key >= 0x7f)
399  now.c_cc[VERASE] = '\177';
400  else
401  now.c_cc[VERASE] = key;
402 #ifdef IUTF8
403  now.c_iflag |= IUTF8;
404 #endif
405  if (tcsetattr(STDIN_FILENO, TCSANOW, &now) != 0)
406  _exit(1);
407 
408  /* Clean up file descriptors and signals and update the environment. */
409  closefrom(STDERR_FILENO + 1);
411  sigprocmask(SIG_SETMASK, &oldset, NULL);
412  log_close();
413  environ_push(child);
414 
415  /*
416  * If given multiple arguments, use execvp(). Copy the arguments to
417  * ensure they end in a NULL.
418  */
419  if (new_wp->argc != 0 && new_wp->argc != 1) {
420  argvp = cmd_copy_argv(new_wp->argc, new_wp->argv);
421  execvp(argvp[0], argvp);
422  _exit(1);
423  }
424 
425  /*
426  * If one argument, pass it to $SHELL -c. Otherwise create a login
427  * shell.
428  */
429  cp = strrchr(new_wp->shell, '/');
430  if (new_wp->argc == 1) {
431  tmp = new_wp->argv[0];
432  if (cp != NULL && cp[1] != '\0')
433  xasprintf(&argv0, "%s", cp + 1);
434  else
435  xasprintf(&argv0, "%s", new_wp->shell);
436  execl(new_wp->shell, argv0, "-c", tmp, (char *)NULL);
437  _exit(1);
438  }
439  if (cp != NULL && cp[1] != '\0')
440  xasprintf(&argv0, "-%s", cp + 1);
441  else
442  xasprintf(&argv0, "-%s", new_wp->shell);
443  execl(new_wp->shell, argv0, (char *)NULL);
444  _exit(1);
445 
446 complete:
447 #ifdef HAVE_UTEMPTER
448  if (~new_wp->flags & PANE_EMPTY) {
449  xasprintf(&cp, "tmux(%lu).%%%u", (long)getpid(), new_wp->id);
450  utempter_add_record(new_wp->fd, cp);
451  kill(getpid(), SIGCHLD);
452  free(cp);
453  }
454 #endif
455 
456  new_wp->flags &= ~~PANE_EXITED;
457 
458  sigprocmask(SIG_SETMASK, &oldset, NULL);
459  window_pane_set_event(new_wp);
460 
461  environ_free(child);
462 
463  if (sc->flags & SPAWN_RESPAWN)
464  return (new_wp);
465  if ((~sc->flags & SPAWN_DETACHED) || w->active == NULL) {
466  if (sc->flags & SPAWN_NONOTIFY)
467  window_set_active_pane(w, new_wp, 0);
468  else
469  window_set_active_pane(w, new_wp, 1);
470  }
471  if (~sc->flags & SPAWN_NONOTIFY)
472  notify_window("window-layout-changed", w);
473  return (new_wp);
474 }
const char * cmdq_get_name(struct cmdq_item *item)
Definition: cmd-queue.c:143
struct client * cmdq_get_client(struct cmdq_item *item)
Definition: cmd-queue.c:150
struct cmd_find_state * cmdq_get_target(struct cmdq_item *item)
Definition: cmd-queue.c:171
char ** cmd_copy_argv(int argc, char **argv)
Definition: cmd.c:327
char * cmd_stringify_argv(int argc, char **argv)
Definition: cmd.c:357
void cmd_free_argv(int argc, char **argv)
Definition: cmd.c:344
void cmd_log_argv(int argc, char **argv, const char *fmt,...)
Definition: cmd.c:233
#define _PATH_DEFPATH
Definition: compat.h:118
void closefrom(int)
pid_t fdforkpty(int, int *, char *, struct termios *, struct winsize *)
#define _PATH_BSHELL
Definition: compat.h:98
struct environ_entry * environ_find(struct environ *env, const char *name)
Definition: environ.c:99
void environ_push(struct environ *env)
Definition: environ.c:209
struct environ * environ_for_session(struct session *s, int no_TERM)
Definition: environ.c:246
void environ_set(struct environ *env, const char *name, int flags, const char *fmt,...)
Definition: environ.c:109
void environ_copy(struct environ *srcenv, struct environ *dstenv)
Definition: environ.c:83
void environ_log(struct environ *env, const char *fmt,...)
Definition: environ.c:224
void environ_free(struct environ *env)
Definition: environ.c:56
char * format_single(struct cmdq_item *item, const char *fmt, struct client *c, struct session *s, struct winlink *wl, struct window_pane *wp)
Definition: format.c:4621
void input_free(struct input_ctx *ictx)
Definition: input.c:823
key_code key
Definition: key-string.c:32
const char * name
Definition: layout-set.c:38
void layout_init(struct window *w, struct window_pane *wp)
Definition: layout.c:478
void layout_assign_pane(struct layout_cell *lc, struct window_pane *wp, int do_not_resize)
Definition: layout.c:707
void layout_close_pane(struct window_pane *wp)
Definition: layout.c:1037
void layout_free(struct window *w)
Definition: layout.c:489
void fatal(const char *msg,...)
Definition: log.c:144
void log_close(void)
Definition: log.c:93
void log_debug(const char *msg,...)
Definition: log.c:130
char * default_window_name(struct window *w)
Definition: names.c:108
void notify_session_window(const char *name, struct session *s, struct window *w)
Definition: notify.c:244
void notify_window(const char *name, struct window *w)
Definition: notify.c:253
const char * options_get_string(struct options *oo, const char *name)
Definition: options.c:686
long long options_get_number(struct options *oo, const char *name)
Definition: options.c:699
struct options_entry * options_set_number(struct options *oo, const char *name, long long value)
Definition: options.c:752
void proc_clear_signals(struct tmuxproc *tp, int defaults)
Definition: proc.c:272
void default_window_size(struct client *c, struct session *s, struct window *w, u_int *sx, u_int *sy, u_int *xpixel, u_int *ypixel, int type)
Definition: resize.c:221
void screen_reinit(struct screen *s)
Definition: screen.c:95
void server_client_remove_pane(struct window_pane *wp)
const char * server_client_get_cwd(struct client *c, struct session *s)
struct tmuxproc * server_proc
Definition: server.c:44
void session_group_synchronize_from(struct session *target)
Definition: session.c:617
int session_select(struct session *s, int idx)
Definition: session.c:461
struct window_pane * spawn_pane(struct spawn_context *sc, char **cause)
Definition: spawn.c:201
struct winlink * spawn_window(struct spawn_context *sc, char **cause)
Definition: spawn.c:75
static void spawn_log(const char *from, struct spawn_context *sc)
Definition: spawn.c:52
Definition: tmux.h:1608
struct session * session
Definition: tmux.h:1743
struct environ * environ
Definition: tmux.h:1627
struct session * s
Definition: tmux.h:1451
Definition: cmd.c:212
Definition: tmux.h:1160
char * value
Definition: tmux.h:1162
int mode
Definition: tmux.h:832
Definition: tmux.h:1179
char * name
Definition: tmux.h:1182
u_int id
Definition: tmux.h:1180
struct winlinks windows
Definition: tmux.h:1194
struct winlink_stack lastw
Definition: tmux.h:1193
struct winlink * curw
Definition: tmux.h:1192
struct termios * tio
Definition: tmux.h:1207
struct options * options
Definition: tmux.h:1199
struct layout_cell * lc
Definition: tmux.h:1877
struct client * tc
Definition: tmux.h:1874
struct window_pane * wp0
Definition: tmux.h:1876
struct environ * environ
Definition: tmux.h:1882
struct session * s
Definition: tmux.h:1872
struct cmdq_item * item
Definition: tmux.h:1870
char ** argv
Definition: tmux.h:1880
const char * name
Definition: tmux.h:1879
int flags
Definition: tmux.h:1887
const char * cwd
Definition: tmux.h:1885
struct winlink * wl
Definition: tmux.h:1873
int argc
Definition: tmux.h:1881
pid_t pid
Definition: tmux.h:997
char * shell
Definition: tmux.h:994
int argc
Definition: tmux.h:992
int fd
Definition: tmux.h:1001
char tty[32]
Definition: tmux.h:998
char ** argv
Definition: tmux.h:993
int flags
Definition: tmux.h:977
char * cwd
Definition: tmux.h:995
struct input_ctx * ictx
Definition: tmux.h:1010
struct screen base
Definition: tmux.h:1021
struct bufferevent * event
Definition: tmux.h:1002
u_int id
Definition: tmux.h:959
Definition: tmux.h:1041
u_int ypixel
Definition: tmux.h:1066
char * name
Definition: tmux.h:1045
struct options * options
Definition: tmux.h:1085
u_int sx
Definition: tmux.h:1063
void * latest
Definition: tmux.h:1043
struct window_panes panes
Definition: tmux.h:1056
u_int xpixel
Definition: tmux.h:1065
struct window_pane * active
Definition: tmux.h:1054
u_int sy
Definition: tmux.h:1064
int ptm_fd
Definition: tmux.c:43
const char * find_home(void)
Definition: tmux.c:299
int checkshell(const char *shell)
Definition: tmux.c:80
struct options * global_options
Definition: tmux.c:36
#define SPAWN_NONOTIFY
Definition: tmux.h:1892
#define SPAWN_DETACHED
Definition: tmux.h:1889
void window_destroy_panes(struct window *)
Definition: window.c:785
void window_pane_set_event(struct window_pane *)
Definition: window.c:975
int window_pane_index(struct window_pane *, u_int *)
Definition: window.c:756
#define PANE_STATUSREADY
Definition: tmux.h:987
#define SPAWN_ZOOM
Definition: tmux.h:1895
#define SPAWN_EMPTY
Definition: tmux.h:1894
struct window_pane * window_add_pane(struct window *, struct window_pane *, u_int, int)
Definition: window.c:656
unsigned long long key_code
Definition: tmux.h:177
void winlink_stack_remove(struct winlink_stack *, struct winlink *)
Definition: window.c:251
#define SPAWN_RESPAWN
Definition: tmux.h:1890
#define PANE_EMPTY
Definition: tmux.h:989
void winlink_set_window(struct winlink *, struct window *)
Definition: window.c:181
void window_pane_resize(struct window_pane *, u_int, u_int)
Definition: window.c:987
#define PANE_STATUSDRAWN
Definition: tmux.h:988
#define screen_size_y(s)
Definition: tmux.h:882
#define SPAWN_KILL
Definition: tmux.h:1888
char ** environ
#define screen_size_x(s)
Definition: tmux.h:881
struct winlink * winlink_add(struct winlinks *, int)
Definition: window.c:163
#define MODE_CRLF
Definition: tmux.h:612
void winlink_remove(struct winlinks *, struct winlink *)
Definition: window.c:193
struct winlink * winlink_find_by_index(struct winlinks *, int)
Definition: window.c:109
struct window * window_create(u_int, u_int, u_int, u_int)
Definition: window.c:298
void window_pane_reset_mode_all(struct window_pane *)
Definition: window.c:1135
void window_remove_pane(struct window *, struct window_pane *)
Definition: window.c:709
int window_set_active_pane(struct window *, struct window_pane *, int)
Definition: window.c:469
int xsnprintf(char *str, size_t len, const char *fmt,...)
Definition: xmalloc.c:135
int xasprintf(char **ret, const char *fmt,...)
Definition: xmalloc.c:109
char * xstrdup(const char *str)
Definition: xmalloc.c:89