Annotation of src/usr.bin/tmux/session.c, Revision 1.6
1.6 ! nicm 1: /* $OpenBSD: session.c,v 1.5 2009/08/13 20:11:58 nicm Exp $ */
1.1 nicm 2:
3: /*
4: * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
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: #include <sys/time.h>
21:
1.6 ! nicm 22: #include <paths.h>
1.1 nicm 23: #include <string.h>
24: #include <stdlib.h>
25: #include <unistd.h>
26:
27: #include "tmux.h"
28:
29: /* Global session list. */
30: struct sessions sessions;
31:
32: struct winlink *session_next_activity(struct session *, struct winlink *);
33: struct winlink *session_previous_activity(struct session *, struct winlink *);
34:
35: void
36: session_alert_cancel(struct session *s, struct winlink *wl)
37: {
38: struct session_alert *sa, *sb;
39:
40: sa = SLIST_FIRST(&s->alerts);
41: while (sa != NULL) {
42: sb = sa;
43: sa = SLIST_NEXT(sa, entry);
44:
45: if (wl == NULL || sb->wl == wl) {
46: SLIST_REMOVE(&s->alerts, sb, session_alert, entry);
47: xfree(sb);
48: }
49: }
50: }
51:
52: void
53: session_alert_add(struct session *s, struct window *w, int type)
54: {
55: struct session_alert *sa;
56: struct winlink *wl;
57:
58: RB_FOREACH(wl, winlinks, &s->windows) {
59: if (wl == s->curw)
60: continue;
61:
62: if (wl->window == w &&
63: !session_alert_has(s, wl, type)) {
64: sa = xmalloc(sizeof *sa);
65: sa->wl = wl;
66: sa->type = type;
67: SLIST_INSERT_HEAD(&s->alerts, sa, entry);
68: }
69: }
70: }
71:
72: int
73: session_alert_has(struct session *s, struct winlink *wl, int type)
74: {
75: struct session_alert *sa;
76:
77: SLIST_FOREACH(sa, &s->alerts, entry) {
78: if (sa->wl == wl && sa->type == type)
79: return (1);
80: }
81:
82: return (0);
83: }
84:
85: int
86: session_alert_has_window(struct session *s, struct window *w, int type)
87: {
88: struct session_alert *sa;
89:
90: SLIST_FOREACH(sa, &s->alerts, entry) {
91: if (sa->wl->window == w && sa->type == type)
92: return (1);
93: }
94:
95: return (0);
96: }
97:
98: /* Find session by name. */
99: struct session *
100: session_find(const char *name)
101: {
102: struct session *s;
103: u_int i;
104:
105: for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
106: s = ARRAY_ITEM(&sessions, i);
107: if (s != NULL && strcmp(s->name, name) == 0)
108: return (s);
109: }
110:
111: return (NULL);
112: }
113:
114: /* Create a new session. */
115: struct session *
1.3 nicm 116: session_create(const char *name, const char *cmd, const char *cwd,
1.5 nicm 117: struct environ *env, struct termios *tio, int idx, u_int sx, u_int sy,
118: char **cause)
1.1 nicm 119: {
120: struct session *s;
121: u_int i;
122:
123: s = xmalloc(sizeof *s);
124: s->flags = 0;
125: if (gettimeofday(&s->tv, NULL) != 0)
126: fatal("gettimeofday");
127: s->curw = NULL;
128: SLIST_INIT(&s->lastw);
129: RB_INIT(&s->windows);
130: SLIST_INIT(&s->alerts);
131: paste_init_stack(&s->buffers);
1.2 nicm 132: options_init(&s->options, &global_s_options);
1.3 nicm 133: environ_init(&s->environ);
134: if (env != NULL)
135: environ_copy(env, &s->environ);
1.4 nicm 136: memcpy(&s->tio, tio, sizeof s->tio);
1.1 nicm 137:
138: s->sx = sx;
139: s->sy = sy;
140:
141: for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
142: if (ARRAY_ITEM(&sessions, i) == NULL) {
143: ARRAY_SET(&sessions, i, s);
144: break;
145: }
146: }
147: if (i == ARRAY_LENGTH(&sessions))
148: ARRAY_ADD(&sessions, s);
149:
150: if (name != NULL)
151: s->name = xstrdup(name);
152: else
153: xasprintf(&s->name, "%u", i);
1.5 nicm 154: if (session_new(s, NULL, cmd, cwd, idx, cause) == NULL) {
1.1 nicm 155: session_destroy(s);
156: return (NULL);
157: }
1.5 nicm 158: session_select(s, RB_ROOT(&s->windows)->idx);
1.1 nicm 159:
160: log_debug("session %s created", s->name);
161:
162: return (s);
163: }
164:
165: /* Destroy a session. */
166: void
167: session_destroy(struct session *s)
168: {
169: u_int i;
170:
171: log_debug("session %s destroyed", s->name);
172:
173: if (session_index(s, &i) != 0)
174: fatalx("session not found");
175: ARRAY_SET(&sessions, i, NULL);
176: while (!ARRAY_EMPTY(&sessions) && ARRAY_LAST(&sessions) == NULL)
177: ARRAY_TRUNC(&sessions, 1);
178:
179: session_alert_cancel(s, NULL);
1.3 nicm 180: environ_free(&s->environ);
1.1 nicm 181: options_free(&s->options);
182: paste_free_stack(&s->buffers);
183:
184: while (!SLIST_EMPTY(&s->lastw))
185: winlink_stack_remove(&s->lastw, SLIST_FIRST(&s->lastw));
186: while (!RB_EMPTY(&s->windows))
187: winlink_remove(&s->windows, RB_ROOT(&s->windows));
188:
189: xfree(s->name);
190: xfree(s);
191: }
192:
193: /* Find session index. */
194: int
195: session_index(struct session *s, u_int *i)
196: {
197: for (*i = 0; *i < ARRAY_LENGTH(&sessions); (*i)++) {
198: if (s == ARRAY_ITEM(&sessions, *i))
199: return (0);
200: }
201: return (-1);
202: }
203:
204: /* Create a new window on a session. */
205: struct winlink *
1.4 nicm 206: session_new(struct session *s,
1.1 nicm 207: const char *name, const char *cmd, const char *cwd, int idx, char **cause)
208: {
209: struct window *w;
1.3 nicm 210: struct environ env;
1.6 ! nicm 211: const char *shell;
1.1 nicm 212: u_int hlimit;
213:
1.3 nicm 214: environ_init(&env);
215: environ_copy(&global_environ, &env);
216: environ_copy(&s->environ, &env);
217: server_fill_environ(s, &env);
1.1 nicm 218:
1.6 ! nicm 219: shell = options_get_string(&s->options, "default-shell");
! 220: if (*shell == '\0' || areshell(shell))
! 221: shell = _PATH_BSHELL;
! 222:
1.1 nicm 223: hlimit = options_get_number(&s->options, "history-limit");
1.4 nicm 224: w = window_create(
1.6 ! nicm 225: name, cmd, shell, cwd, &env, &s->tio, s->sx, s->sy, hlimit, cause);
1.3 nicm 226: if (w == NULL) {
227: environ_free(&env);
1.1 nicm 228: return (NULL);
1.3 nicm 229: }
230: environ_free(&env);
1.1 nicm 231:
232: if (options_get_number(&s->options, "set-remain-on-exit"))
233: options_set_number(&w->options, "remain-on-exit", 1);
234:
235: return (session_attach(s, w, idx, cause));
236: }
237:
238: /* Attach a window to a session. */
239: struct winlink *
240: session_attach(struct session *s, struct window *w, int idx, char **cause)
241: {
242: struct winlink *wl;
243:
244: if ((wl = winlink_add(&s->windows, w, idx)) == NULL)
245: xasprintf(cause, "index in use: %d", idx);
246: return (wl);
247: }
248:
249: /* Detach a window from a session. */
250: int
251: session_detach(struct session *s, struct winlink *wl)
252: {
253: if (s->curw == wl &&
254: session_last(s) != 0 && session_previous(s, 0) != 0)
255: session_next(s, 0);
256:
257: session_alert_cancel(s, wl);
258: winlink_stack_remove(&s->lastw, wl);
259: winlink_remove(&s->windows, wl);
260: if (RB_EMPTY(&s->windows)) {
261: session_destroy(s);
262: return (1);
263: }
264: return (0);
265: }
266:
267: /* Return if session has window. */
268: int
269: session_has(struct session *s, struct window *w)
270: {
271: struct winlink *wl;
272:
273: RB_FOREACH(wl, winlinks, &s->windows) {
274: if (wl->window == w)
275: return (1);
276: }
277: return (0);
278: }
279:
280: struct winlink *
281: session_next_activity(struct session *s, struct winlink *wl)
282: {
283: while (wl != NULL) {
284: if (session_alert_has(s, wl, WINDOW_BELL))
285: break;
286: if (session_alert_has(s, wl, WINDOW_ACTIVITY))
287: break;
288: if (session_alert_has(s, wl, WINDOW_CONTENT))
289: break;
290: wl = winlink_next(&s->windows, wl);
291: }
292: return (wl);
293: }
294:
295: /* Move session to next window. */
296: int
297: session_next(struct session *s, int activity)
298: {
299: struct winlink *wl;
300:
301: if (s->curw == NULL)
302: return (-1);
303:
304: wl = winlink_next(&s->windows, s->curw);
305: if (activity)
306: wl = session_next_activity(s, wl);
307: if (wl == NULL) {
308: wl = RB_MIN(winlinks, &s->windows);
309: if (activity && ((wl = session_next_activity(s, wl)) == NULL))
310: return (-1);
311: }
312: if (wl == s->curw)
313: return (1);
314: winlink_stack_remove(&s->lastw, wl);
315: winlink_stack_push(&s->lastw, s->curw);
316: s->curw = wl;
317: session_alert_cancel(s, wl);
318: return (0);
319: }
320:
321: struct winlink *
322: session_previous_activity(struct session *s, struct winlink *wl)
323: {
324: while (wl != NULL) {
325: if (session_alert_has(s, wl, WINDOW_BELL))
326: break;
327: if (session_alert_has(s, wl, WINDOW_ACTIVITY))
328: break;
329: if (session_alert_has(s, wl, WINDOW_CONTENT))
330: break;
331: wl = winlink_previous(&s->windows, wl);
332: }
333: return (wl);
334: }
335:
336: /* Move session to previous window. */
337: int
338: session_previous(struct session *s, int activity)
339: {
340: struct winlink *wl;
341:
342: if (s->curw == NULL)
343: return (-1);
344:
345: wl = winlink_previous(&s->windows, s->curw);
346: if (activity)
347: wl = session_previous_activity(s, wl);
348: if (wl == NULL) {
349: wl = RB_MAX(winlinks, &s->windows);
350: if (activity && (wl = session_previous_activity(s, wl)) == NULL)
351: return (-1);
352: }
353: if (wl == s->curw)
354: return (1);
355: winlink_stack_remove(&s->lastw, wl);
356: winlink_stack_push(&s->lastw, s->curw);
357: s->curw = wl;
358: session_alert_cancel(s, wl);
359: return (0);
360: }
361:
362: /* Move session to specific window. */
363: int
364: session_select(struct session *s, int idx)
365: {
366: struct winlink *wl;
367:
368: wl = winlink_find_by_index(&s->windows, idx);
369: if (wl == NULL)
370: return (-1);
371: if (wl == s->curw)
372: return (1);
373: winlink_stack_remove(&s->lastw, wl);
374: winlink_stack_push(&s->lastw, s->curw);
375: s->curw = wl;
376: session_alert_cancel(s, wl);
377: return (0);
378: }
379:
380: /* Move session to last used window. */
381: int
382: session_last(struct session *s)
383: {
384: struct winlink *wl;
385:
386: wl = SLIST_FIRST(&s->lastw);
387: if (wl == NULL)
388: return (-1);
389: if (wl == s->curw)
390: return (1);
391:
392: winlink_stack_remove(&s->lastw, wl);
393: winlink_stack_push(&s->lastw, s->curw);
394: s->curw = wl;
395: session_alert_cancel(s, wl);
396: return (0);
397: }