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