xref: /minix3/external/bsd/tmux/dist/session.c (revision 0a6a1f1d05b60e214de2f05a7310ddd1f0e590e7)
1*0a6a1f1dSLionel Sambuc /* Id */
2eda6f593SDavid van Moolenbroek 
3eda6f593SDavid van Moolenbroek /*
4eda6f593SDavid van Moolenbroek  * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
5eda6f593SDavid van Moolenbroek  *
6eda6f593SDavid van Moolenbroek  * Permission to use, copy, modify, and distribute this software for any
7eda6f593SDavid van Moolenbroek  * purpose with or without fee is hereby granted, provided that the above
8eda6f593SDavid van Moolenbroek  * copyright notice and this permission notice appear in all copies.
9eda6f593SDavid van Moolenbroek  *
10eda6f593SDavid van Moolenbroek  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11eda6f593SDavid van Moolenbroek  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12eda6f593SDavid van Moolenbroek  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13eda6f593SDavid van Moolenbroek  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14eda6f593SDavid van Moolenbroek  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15eda6f593SDavid van Moolenbroek  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16eda6f593SDavid van Moolenbroek  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17eda6f593SDavid van Moolenbroek  */
18eda6f593SDavid van Moolenbroek 
19eda6f593SDavid van Moolenbroek #include <sys/types.h>
20eda6f593SDavid van Moolenbroek #include <sys/time.h>
21eda6f593SDavid van Moolenbroek 
22eda6f593SDavid van Moolenbroek #include <string.h>
23eda6f593SDavid van Moolenbroek #include <stdlib.h>
24eda6f593SDavid van Moolenbroek #include <unistd.h>
25eda6f593SDavid van Moolenbroek #include <time.h>
26eda6f593SDavid van Moolenbroek 
27eda6f593SDavid van Moolenbroek #include "tmux.h"
28eda6f593SDavid van Moolenbroek 
29eda6f593SDavid van Moolenbroek /* Global session list. */
30eda6f593SDavid van Moolenbroek struct sessions	sessions;
31eda6f593SDavid van Moolenbroek struct sessions dead_sessions;
32*0a6a1f1dSLionel Sambuc u_int		next_session_id;
33eda6f593SDavid van Moolenbroek struct session_groups session_groups;
34eda6f593SDavid van Moolenbroek 
35eda6f593SDavid van Moolenbroek struct winlink *session_next_alert(struct winlink *);
36eda6f593SDavid van Moolenbroek struct winlink *session_previous_alert(struct winlink *);
37eda6f593SDavid van Moolenbroek 
38eda6f593SDavid van Moolenbroek RB_GENERATE(sessions, session, entry, session_cmp);
39eda6f593SDavid van Moolenbroek 
40eda6f593SDavid van Moolenbroek int
session_cmp(struct session * s1,struct session * s2)41eda6f593SDavid van Moolenbroek session_cmp(struct session *s1, struct session *s2)
42eda6f593SDavid van Moolenbroek {
43eda6f593SDavid van Moolenbroek 	return (strcmp(s1->name, s2->name));
44eda6f593SDavid van Moolenbroek }
45eda6f593SDavid van Moolenbroek 
46eda6f593SDavid van Moolenbroek /*
47eda6f593SDavid van Moolenbroek  * Find if session is still alive. This is true if it is still on the global
48eda6f593SDavid van Moolenbroek  * sessions list.
49eda6f593SDavid van Moolenbroek  */
50eda6f593SDavid van Moolenbroek int
session_alive(struct session * s)51eda6f593SDavid van Moolenbroek session_alive(struct session *s)
52eda6f593SDavid van Moolenbroek {
53eda6f593SDavid van Moolenbroek 	struct session *s_loop;
54eda6f593SDavid van Moolenbroek 
55eda6f593SDavid van Moolenbroek 	RB_FOREACH(s_loop, sessions, &sessions) {
56eda6f593SDavid van Moolenbroek 		if (s_loop == s)
57eda6f593SDavid van Moolenbroek 			return (1);
58eda6f593SDavid van Moolenbroek 	}
59eda6f593SDavid van Moolenbroek 	return (0);
60eda6f593SDavid van Moolenbroek }
61eda6f593SDavid van Moolenbroek 
62eda6f593SDavid van Moolenbroek /* Find session by name. */
63eda6f593SDavid van Moolenbroek struct session *
session_find(const char * name)64eda6f593SDavid van Moolenbroek session_find(const char *name)
65eda6f593SDavid van Moolenbroek {
66eda6f593SDavid van Moolenbroek 	struct session	s;
67eda6f593SDavid van Moolenbroek 
68eda6f593SDavid van Moolenbroek 	s.name = __UNCONST(name);
69eda6f593SDavid van Moolenbroek 	return (RB_FIND(sessions, &sessions, &s));
70eda6f593SDavid van Moolenbroek }
71eda6f593SDavid van Moolenbroek 
72*0a6a1f1dSLionel Sambuc /* Find session by id. */
73eda6f593SDavid van Moolenbroek struct session *
session_find_by_id(u_int id)74*0a6a1f1dSLionel Sambuc session_find_by_id(u_int id)
75eda6f593SDavid van Moolenbroek {
76eda6f593SDavid van Moolenbroek 	struct session	*s;
77eda6f593SDavid van Moolenbroek 
78eda6f593SDavid van Moolenbroek 	RB_FOREACH(s, sessions, &sessions) {
79*0a6a1f1dSLionel Sambuc 		if (s->id == id)
80eda6f593SDavid van Moolenbroek 			return (s);
81eda6f593SDavid van Moolenbroek 	}
82eda6f593SDavid van Moolenbroek 	return (NULL);
83eda6f593SDavid van Moolenbroek }
84eda6f593SDavid van Moolenbroek 
85eda6f593SDavid van Moolenbroek /* Create a new session. */
86eda6f593SDavid van Moolenbroek struct session *
session_create(const char * name,const char * cmd,int cwd,struct environ * env,struct termios * tio,int idx,u_int sx,u_int sy,char ** cause)87*0a6a1f1dSLionel Sambuc session_create(const char *name, const char *cmd, int cwd, struct environ *env,
88*0a6a1f1dSLionel Sambuc     struct termios *tio, int idx, u_int sx, u_int sy, char **cause)
89eda6f593SDavid van Moolenbroek {
90eda6f593SDavid van Moolenbroek 	struct session	*s;
91eda6f593SDavid van Moolenbroek 
92eda6f593SDavid van Moolenbroek 	s = xmalloc(sizeof *s);
93eda6f593SDavid van Moolenbroek 	s->references = 0;
94eda6f593SDavid van Moolenbroek 	s->flags = 0;
95eda6f593SDavid van Moolenbroek 
96eda6f593SDavid van Moolenbroek 	if (gettimeofday(&s->creation_time, NULL) != 0)
97eda6f593SDavid van Moolenbroek 		fatal("gettimeofday failed");
98eda6f593SDavid van Moolenbroek 	session_update_activity(s);
99eda6f593SDavid van Moolenbroek 
100*0a6a1f1dSLionel Sambuc 	s->cwd = dup(cwd);
101eda6f593SDavid van Moolenbroek 
102eda6f593SDavid van Moolenbroek 	s->curw = NULL;
103eda6f593SDavid van Moolenbroek 	TAILQ_INIT(&s->lastw);
104eda6f593SDavid van Moolenbroek 	RB_INIT(&s->windows);
105eda6f593SDavid van Moolenbroek 
106eda6f593SDavid van Moolenbroek 	options_init(&s->options, &global_s_options);
107eda6f593SDavid van Moolenbroek 	environ_init(&s->environ);
108eda6f593SDavid van Moolenbroek 	if (env != NULL)
109eda6f593SDavid van Moolenbroek 		environ_copy(env, &s->environ);
110eda6f593SDavid van Moolenbroek 
111eda6f593SDavid van Moolenbroek 	s->tio = NULL;
112eda6f593SDavid van Moolenbroek 	if (tio != NULL) {
113eda6f593SDavid van Moolenbroek 		s->tio = xmalloc(sizeof *s->tio);
114eda6f593SDavid van Moolenbroek 		memcpy(s->tio, tio, sizeof *s->tio);
115eda6f593SDavid van Moolenbroek 	}
116eda6f593SDavid van Moolenbroek 
117eda6f593SDavid van Moolenbroek 	s->sx = sx;
118eda6f593SDavid van Moolenbroek 	s->sy = sy;
119eda6f593SDavid van Moolenbroek 
120*0a6a1f1dSLionel Sambuc 	if (name != NULL) {
121eda6f593SDavid van Moolenbroek 		s->name = xstrdup(name);
122*0a6a1f1dSLionel Sambuc 		s->id = next_session_id++;
123*0a6a1f1dSLionel Sambuc 	} else {
124*0a6a1f1dSLionel Sambuc 		s->name = NULL;
125*0a6a1f1dSLionel Sambuc 		do {
126*0a6a1f1dSLionel Sambuc 			s->id = next_session_id++;
127*0a6a1f1dSLionel Sambuc 			free (s->name);
128*0a6a1f1dSLionel Sambuc 			xasprintf(&s->name, "%u", s->id);
129*0a6a1f1dSLionel Sambuc 		} while (RB_FIND(sessions, &sessions, s) != NULL);
130*0a6a1f1dSLionel Sambuc 	}
131eda6f593SDavid van Moolenbroek 	RB_INSERT(sessions, &sessions, s);
132eda6f593SDavid van Moolenbroek 
133eda6f593SDavid van Moolenbroek 	if (cmd != NULL) {
134eda6f593SDavid van Moolenbroek 		if (session_new(s, NULL, cmd, cwd, idx, cause) == NULL) {
135eda6f593SDavid van Moolenbroek 			session_destroy(s);
136eda6f593SDavid van Moolenbroek 			return (NULL);
137eda6f593SDavid van Moolenbroek 		}
138eda6f593SDavid van Moolenbroek 		session_select(s, RB_ROOT(&s->windows)->idx);
139eda6f593SDavid van Moolenbroek 	}
140eda6f593SDavid van Moolenbroek 
141eda6f593SDavid van Moolenbroek 	log_debug("session %s created", s->name);
142*0a6a1f1dSLionel Sambuc 	notify_session_created(s);
143eda6f593SDavid van Moolenbroek 
144eda6f593SDavid van Moolenbroek 	return (s);
145eda6f593SDavid van Moolenbroek }
146eda6f593SDavid van Moolenbroek 
147eda6f593SDavid van Moolenbroek /* Destroy a session. */
148eda6f593SDavid van Moolenbroek void
session_destroy(struct session * s)149eda6f593SDavid van Moolenbroek session_destroy(struct session *s)
150eda6f593SDavid van Moolenbroek {
151*0a6a1f1dSLionel Sambuc 	struct winlink	*wl;
152*0a6a1f1dSLionel Sambuc 
153eda6f593SDavid van Moolenbroek 	log_debug("session %s destroyed", s->name);
154eda6f593SDavid van Moolenbroek 
155eda6f593SDavid van Moolenbroek 	RB_REMOVE(sessions, &sessions, s);
156*0a6a1f1dSLionel Sambuc 	notify_session_closed(s);
157eda6f593SDavid van Moolenbroek 
158*0a6a1f1dSLionel Sambuc 	free(s->tio);
159eda6f593SDavid van Moolenbroek 
160eda6f593SDavid van Moolenbroek 	session_group_remove(s);
161eda6f593SDavid van Moolenbroek 	environ_free(&s->environ);
162eda6f593SDavid van Moolenbroek 	options_free(&s->options);
163eda6f593SDavid van Moolenbroek 
164eda6f593SDavid van Moolenbroek 	while (!TAILQ_EMPTY(&s->lastw))
165eda6f593SDavid van Moolenbroek 		winlink_stack_remove(&s->lastw, TAILQ_FIRST(&s->lastw));
166*0a6a1f1dSLionel Sambuc 	while (!RB_EMPTY(&s->windows)) {
167*0a6a1f1dSLionel Sambuc 		wl = RB_ROOT(&s->windows);
168*0a6a1f1dSLionel Sambuc 		notify_window_unlinked(s, wl->window);
169*0a6a1f1dSLionel Sambuc 		winlink_remove(&s->windows, wl);
170*0a6a1f1dSLionel Sambuc 	}
171eda6f593SDavid van Moolenbroek 
172*0a6a1f1dSLionel Sambuc 	close(s->cwd);
173eda6f593SDavid van Moolenbroek 
174eda6f593SDavid van Moolenbroek 	RB_INSERT(sessions, &dead_sessions, s);
175eda6f593SDavid van Moolenbroek }
176eda6f593SDavid van Moolenbroek 
177*0a6a1f1dSLionel Sambuc /* Check a session name is valid: not empty and no colons or periods. */
178eda6f593SDavid van Moolenbroek int
session_check_name(const char * name)179eda6f593SDavid van Moolenbroek session_check_name(const char *name)
180eda6f593SDavid van Moolenbroek {
181*0a6a1f1dSLionel Sambuc 	return (*name != '\0' && name[strcspn(name, ":.")] == '\0');
182eda6f593SDavid van Moolenbroek }
183eda6f593SDavid van Moolenbroek 
184eda6f593SDavid van Moolenbroek /* Update session active time. */
185eda6f593SDavid van Moolenbroek void
session_update_activity(struct session * s)186eda6f593SDavid van Moolenbroek session_update_activity(struct session *s)
187eda6f593SDavid van Moolenbroek {
188eda6f593SDavid van Moolenbroek 	if (gettimeofday(&s->activity_time, NULL) != 0)
189eda6f593SDavid van Moolenbroek 		fatal("gettimeofday");
190eda6f593SDavid van Moolenbroek }
191eda6f593SDavid van Moolenbroek 
192eda6f593SDavid van Moolenbroek /* Find the next usable session. */
193eda6f593SDavid van Moolenbroek struct session *
session_next_session(struct session * s)194eda6f593SDavid van Moolenbroek session_next_session(struct session *s)
195eda6f593SDavid van Moolenbroek {
196eda6f593SDavid van Moolenbroek 	struct session *s2;
197eda6f593SDavid van Moolenbroek 
198eda6f593SDavid van Moolenbroek 	if (RB_EMPTY(&sessions) || !session_alive(s))
199eda6f593SDavid van Moolenbroek 		return (NULL);
200eda6f593SDavid van Moolenbroek 
201eda6f593SDavid van Moolenbroek 	s2 = RB_NEXT(sessions, &sessions, s);
202eda6f593SDavid van Moolenbroek 	if (s2 == NULL)
203eda6f593SDavid van Moolenbroek 		s2 = RB_MIN(sessions, &sessions);
204eda6f593SDavid van Moolenbroek 	if (s2 == s)
205eda6f593SDavid van Moolenbroek 		return (NULL);
206eda6f593SDavid van Moolenbroek 	return (s2);
207eda6f593SDavid van Moolenbroek }
208eda6f593SDavid van Moolenbroek 
209eda6f593SDavid van Moolenbroek /* Find the previous usable session. */
210eda6f593SDavid van Moolenbroek struct session *
session_previous_session(struct session * s)211eda6f593SDavid van Moolenbroek session_previous_session(struct session *s)
212eda6f593SDavid van Moolenbroek {
213eda6f593SDavid van Moolenbroek 	struct session *s2;
214eda6f593SDavid van Moolenbroek 
215eda6f593SDavid van Moolenbroek 	if (RB_EMPTY(&sessions) || !session_alive(s))
216eda6f593SDavid van Moolenbroek 		return (NULL);
217eda6f593SDavid van Moolenbroek 
218eda6f593SDavid van Moolenbroek 	s2 = RB_PREV(sessions, &sessions, s);
219eda6f593SDavid van Moolenbroek 	if (s2 == NULL)
220eda6f593SDavid van Moolenbroek 		s2 = RB_MAX(sessions, &sessions);
221eda6f593SDavid van Moolenbroek 	if (s2 == s)
222eda6f593SDavid van Moolenbroek 		return (NULL);
223eda6f593SDavid van Moolenbroek 	return (s2);
224eda6f593SDavid van Moolenbroek }
225eda6f593SDavid van Moolenbroek 
226eda6f593SDavid van Moolenbroek /* Create a new window on a session. */
227eda6f593SDavid van Moolenbroek struct winlink *
session_new(struct session * s,const char * name,const char * cmd,int cwd,int idx,char ** cause)228*0a6a1f1dSLionel Sambuc session_new(struct session *s, const char *name, const char *cmd, int cwd,
229*0a6a1f1dSLionel Sambuc     int idx, char **cause)
230eda6f593SDavid van Moolenbroek {
231eda6f593SDavid van Moolenbroek 	struct window	*w;
232eda6f593SDavid van Moolenbroek 	struct winlink	*wl;
233eda6f593SDavid van Moolenbroek 	struct environ	 env;
234eda6f593SDavid van Moolenbroek 	const char	*shell;
235eda6f593SDavid van Moolenbroek 	u_int		 hlimit;
236eda6f593SDavid van Moolenbroek 
237eda6f593SDavid van Moolenbroek 	if ((wl = winlink_add(&s->windows, idx)) == NULL) {
238eda6f593SDavid van Moolenbroek 		xasprintf(cause, "index in use: %d", idx);
239eda6f593SDavid van Moolenbroek 		return (NULL);
240eda6f593SDavid van Moolenbroek 	}
241eda6f593SDavid van Moolenbroek 
242eda6f593SDavid van Moolenbroek 	environ_init(&env);
243eda6f593SDavid van Moolenbroek 	environ_copy(&global_environ, &env);
244eda6f593SDavid van Moolenbroek 	environ_copy(&s->environ, &env);
245eda6f593SDavid van Moolenbroek 	server_fill_environ(s, &env);
246eda6f593SDavid van Moolenbroek 
247eda6f593SDavid van Moolenbroek 	shell = options_get_string(&s->options, "default-shell");
248eda6f593SDavid van Moolenbroek 	if (*shell == '\0' || areshell(shell))
249eda6f593SDavid van Moolenbroek 		shell = _PATH_BSHELL;
250eda6f593SDavid van Moolenbroek 
251eda6f593SDavid van Moolenbroek 	hlimit = options_get_number(&s->options, "history-limit");
252*0a6a1f1dSLionel Sambuc 	w = window_create(name, cmd, shell, cwd, &env, s->tio, s->sx, s->sy,
253*0a6a1f1dSLionel Sambuc 	    hlimit, cause);
254eda6f593SDavid van Moolenbroek 	if (w == NULL) {
255eda6f593SDavid van Moolenbroek 		winlink_remove(&s->windows, wl);
256eda6f593SDavid van Moolenbroek 		environ_free(&env);
257eda6f593SDavid van Moolenbroek 		return (NULL);
258eda6f593SDavid van Moolenbroek 	}
259eda6f593SDavid van Moolenbroek 	winlink_set_window(wl, w);
260*0a6a1f1dSLionel Sambuc 	notify_window_linked(s, w);
261eda6f593SDavid van Moolenbroek 	environ_free(&env);
262eda6f593SDavid van Moolenbroek 
263eda6f593SDavid van Moolenbroek 	if (options_get_number(&s->options, "set-remain-on-exit"))
264eda6f593SDavid van Moolenbroek 		options_set_number(&w->options, "remain-on-exit", 1);
265eda6f593SDavid van Moolenbroek 
266eda6f593SDavid van Moolenbroek 	session_group_synchronize_from(s);
267eda6f593SDavid van Moolenbroek 	return (wl);
268eda6f593SDavid van Moolenbroek }
269eda6f593SDavid van Moolenbroek 
270eda6f593SDavid van Moolenbroek /* Attach a window to a session. */
271eda6f593SDavid van Moolenbroek struct winlink *
session_attach(struct session * s,struct window * w,int idx,char ** cause)272eda6f593SDavid van Moolenbroek session_attach(struct session *s, struct window *w, int idx, char **cause)
273eda6f593SDavid van Moolenbroek {
274eda6f593SDavid van Moolenbroek 	struct winlink	*wl;
275eda6f593SDavid van Moolenbroek 
276eda6f593SDavid van Moolenbroek 	if ((wl = winlink_add(&s->windows, idx)) == NULL) {
277eda6f593SDavid van Moolenbroek 		xasprintf(cause, "index in use: %d", idx);
278eda6f593SDavid van Moolenbroek 		return (NULL);
279eda6f593SDavid van Moolenbroek 	}
280eda6f593SDavid van Moolenbroek 	winlink_set_window(wl, w);
281*0a6a1f1dSLionel Sambuc 	notify_window_linked(s, w);
282eda6f593SDavid van Moolenbroek 
283eda6f593SDavid van Moolenbroek 	session_group_synchronize_from(s);
284eda6f593SDavid van Moolenbroek 	return (wl);
285eda6f593SDavid van Moolenbroek }
286eda6f593SDavid van Moolenbroek 
287eda6f593SDavid van Moolenbroek /* Detach a window from a session. */
288eda6f593SDavid van Moolenbroek int
session_detach(struct session * s,struct winlink * wl)289eda6f593SDavid van Moolenbroek session_detach(struct session *s, struct winlink *wl)
290eda6f593SDavid van Moolenbroek {
291eda6f593SDavid van Moolenbroek 	if (s->curw == wl &&
292eda6f593SDavid van Moolenbroek 	    session_last(s) != 0 && session_previous(s, 0) != 0)
293eda6f593SDavid van Moolenbroek 		session_next(s, 0);
294eda6f593SDavid van Moolenbroek 
295eda6f593SDavid van Moolenbroek 	wl->flags &= ~WINLINK_ALERTFLAGS;
296*0a6a1f1dSLionel Sambuc 	notify_window_unlinked(s, wl->window);
297eda6f593SDavid van Moolenbroek 	winlink_stack_remove(&s->lastw, wl);
298eda6f593SDavid van Moolenbroek 	winlink_remove(&s->windows, wl);
299eda6f593SDavid van Moolenbroek 	session_group_synchronize_from(s);
300eda6f593SDavid van Moolenbroek 	if (RB_EMPTY(&s->windows)) {
301eda6f593SDavid van Moolenbroek 		session_destroy(s);
302eda6f593SDavid van Moolenbroek 		return (1);
303eda6f593SDavid van Moolenbroek 	}
304eda6f593SDavid van Moolenbroek 	return (0);
305eda6f593SDavid van Moolenbroek }
306eda6f593SDavid van Moolenbroek 
307eda6f593SDavid van Moolenbroek /* Return if session has window. */
308eda6f593SDavid van Moolenbroek struct winlink *
session_has(struct session * s,struct window * w)309eda6f593SDavid van Moolenbroek session_has(struct session *s, struct window *w)
310eda6f593SDavid van Moolenbroek {
311eda6f593SDavid van Moolenbroek 	struct winlink	*wl;
312eda6f593SDavid van Moolenbroek 
313eda6f593SDavid van Moolenbroek 	RB_FOREACH(wl, winlinks, &s->windows) {
314eda6f593SDavid van Moolenbroek 		if (wl->window == w)
315eda6f593SDavid van Moolenbroek 			return (wl);
316eda6f593SDavid van Moolenbroek 	}
317eda6f593SDavid van Moolenbroek 	return (NULL);
318eda6f593SDavid van Moolenbroek }
319eda6f593SDavid van Moolenbroek 
320eda6f593SDavid van Moolenbroek struct winlink *
session_next_alert(struct winlink * wl)321eda6f593SDavid van Moolenbroek session_next_alert(struct winlink *wl)
322eda6f593SDavid van Moolenbroek {
323eda6f593SDavid van Moolenbroek 	while (wl != NULL) {
324eda6f593SDavid van Moolenbroek 		if (wl->flags & WINLINK_ALERTFLAGS)
325eda6f593SDavid van Moolenbroek 			break;
326eda6f593SDavid van Moolenbroek 		wl = winlink_next(wl);
327eda6f593SDavid van Moolenbroek 	}
328eda6f593SDavid van Moolenbroek 	return (wl);
329eda6f593SDavid van Moolenbroek }
330eda6f593SDavid van Moolenbroek 
331eda6f593SDavid van Moolenbroek /* Move session to next window. */
332eda6f593SDavid van Moolenbroek int
session_next(struct session * s,int alert)333eda6f593SDavid van Moolenbroek session_next(struct session *s, int alert)
334eda6f593SDavid van Moolenbroek {
335eda6f593SDavid van Moolenbroek 	struct winlink	*wl;
336eda6f593SDavid van Moolenbroek 
337eda6f593SDavid van Moolenbroek 	if (s->curw == NULL)
338eda6f593SDavid van Moolenbroek 		return (-1);
339eda6f593SDavid van Moolenbroek 
340eda6f593SDavid van Moolenbroek 	wl = winlink_next(s->curw);
341eda6f593SDavid van Moolenbroek 	if (alert)
342eda6f593SDavid van Moolenbroek 		wl = session_next_alert(wl);
343eda6f593SDavid van Moolenbroek 	if (wl == NULL) {
344eda6f593SDavid van Moolenbroek 		wl = RB_MIN(winlinks, &s->windows);
345eda6f593SDavid van Moolenbroek 		if (alert && ((wl = session_next_alert(wl)) == NULL))
346eda6f593SDavid van Moolenbroek 			return (-1);
347eda6f593SDavid van Moolenbroek 	}
348*0a6a1f1dSLionel Sambuc 	return (session_set_current(s, wl));
349eda6f593SDavid van Moolenbroek }
350eda6f593SDavid van Moolenbroek 
351eda6f593SDavid van Moolenbroek struct winlink *
session_previous_alert(struct winlink * wl)352eda6f593SDavid van Moolenbroek session_previous_alert(struct winlink *wl)
353eda6f593SDavid van Moolenbroek {
354eda6f593SDavid van Moolenbroek 	while (wl != NULL) {
355eda6f593SDavid van Moolenbroek 		if (wl->flags & WINLINK_ALERTFLAGS)
356eda6f593SDavid van Moolenbroek 			break;
357eda6f593SDavid van Moolenbroek 		wl = winlink_previous(wl);
358eda6f593SDavid van Moolenbroek 	}
359eda6f593SDavid van Moolenbroek 	return (wl);
360eda6f593SDavid van Moolenbroek }
361eda6f593SDavid van Moolenbroek 
362eda6f593SDavid van Moolenbroek /* Move session to previous window. */
363eda6f593SDavid van Moolenbroek int
session_previous(struct session * s,int alert)364eda6f593SDavid van Moolenbroek session_previous(struct session *s, int alert)
365eda6f593SDavid van Moolenbroek {
366eda6f593SDavid van Moolenbroek 	struct winlink	*wl;
367eda6f593SDavid van Moolenbroek 
368eda6f593SDavid van Moolenbroek 	if (s->curw == NULL)
369eda6f593SDavid van Moolenbroek 		return (-1);
370eda6f593SDavid van Moolenbroek 
371eda6f593SDavid van Moolenbroek 	wl = winlink_previous(s->curw);
372eda6f593SDavid van Moolenbroek 	if (alert)
373eda6f593SDavid van Moolenbroek 		wl = session_previous_alert(wl);
374eda6f593SDavid van Moolenbroek 	if (wl == NULL) {
375eda6f593SDavid van Moolenbroek 		wl = RB_MAX(winlinks, &s->windows);
376eda6f593SDavid van Moolenbroek 		if (alert && (wl = session_previous_alert(wl)) == NULL)
377eda6f593SDavid van Moolenbroek 			return (-1);
378eda6f593SDavid van Moolenbroek 	}
379*0a6a1f1dSLionel Sambuc 	return (session_set_current(s, wl));
380eda6f593SDavid van Moolenbroek }
381eda6f593SDavid van Moolenbroek 
382eda6f593SDavid van Moolenbroek /* Move session to specific window. */
383eda6f593SDavid van Moolenbroek int
session_select(struct session * s,int idx)384eda6f593SDavid van Moolenbroek session_select(struct session *s, int idx)
385eda6f593SDavid van Moolenbroek {
386eda6f593SDavid van Moolenbroek 	struct winlink	*wl;
387eda6f593SDavid van Moolenbroek 
388eda6f593SDavid van Moolenbroek 	wl = winlink_find_by_index(&s->windows, idx);
389*0a6a1f1dSLionel Sambuc 	return (session_set_current(s, wl));
390eda6f593SDavid van Moolenbroek }
391eda6f593SDavid van Moolenbroek 
392eda6f593SDavid van Moolenbroek /* Move session to last used window. */
393eda6f593SDavid van Moolenbroek int
session_last(struct session * s)394eda6f593SDavid van Moolenbroek session_last(struct session *s)
395eda6f593SDavid van Moolenbroek {
396eda6f593SDavid van Moolenbroek 	struct winlink	*wl;
397eda6f593SDavid van Moolenbroek 
398eda6f593SDavid van Moolenbroek 	wl = TAILQ_FIRST(&s->lastw);
399eda6f593SDavid van Moolenbroek 	if (wl == NULL)
400eda6f593SDavid van Moolenbroek 		return (-1);
401eda6f593SDavid van Moolenbroek 	if (wl == s->curw)
402eda6f593SDavid van Moolenbroek 		return (1);
403eda6f593SDavid van Moolenbroek 
404*0a6a1f1dSLionel Sambuc 	return (session_set_current(s, wl));
405*0a6a1f1dSLionel Sambuc }
406*0a6a1f1dSLionel Sambuc 
407*0a6a1f1dSLionel Sambuc /* Set current winlink to wl .*/
408*0a6a1f1dSLionel Sambuc int
session_set_current(struct session * s,struct winlink * wl)409*0a6a1f1dSLionel Sambuc session_set_current(struct session *s, struct winlink *wl)
410*0a6a1f1dSLionel Sambuc {
411*0a6a1f1dSLionel Sambuc 	if (wl == NULL)
412*0a6a1f1dSLionel Sambuc 		return (-1);
413*0a6a1f1dSLionel Sambuc 	if (wl == s->curw)
414*0a6a1f1dSLionel Sambuc 		return (1);
415*0a6a1f1dSLionel Sambuc 
416eda6f593SDavid van Moolenbroek 	winlink_stack_remove(&s->lastw, wl);
417eda6f593SDavid van Moolenbroek 	winlink_stack_push(&s->lastw, s->curw);
418eda6f593SDavid van Moolenbroek 	s->curw = wl;
419*0a6a1f1dSLionel Sambuc 	winlink_clear_flags(wl);
420eda6f593SDavid van Moolenbroek 	return (0);
421eda6f593SDavid van Moolenbroek }
422eda6f593SDavid van Moolenbroek 
423eda6f593SDavid van Moolenbroek /* Find the session group containing a session. */
424eda6f593SDavid van Moolenbroek struct session_group *
session_group_find(struct session * target)425eda6f593SDavid van Moolenbroek session_group_find(struct session *target)
426eda6f593SDavid van Moolenbroek {
427eda6f593SDavid van Moolenbroek 	struct session_group	*sg;
428eda6f593SDavid van Moolenbroek 	struct session		*s;
429eda6f593SDavid van Moolenbroek 
430eda6f593SDavid van Moolenbroek 	TAILQ_FOREACH(sg, &session_groups, entry) {
431eda6f593SDavid van Moolenbroek 		TAILQ_FOREACH(s, &sg->sessions, gentry) {
432eda6f593SDavid van Moolenbroek 			if (s == target)
433eda6f593SDavid van Moolenbroek 				return (sg);
434eda6f593SDavid van Moolenbroek 		}
435eda6f593SDavid van Moolenbroek 	}
436eda6f593SDavid van Moolenbroek 	return (NULL);
437eda6f593SDavid van Moolenbroek }
438eda6f593SDavid van Moolenbroek 
439eda6f593SDavid van Moolenbroek /* Find session group index. */
440eda6f593SDavid van Moolenbroek u_int
session_group_index(struct session_group * sg)441eda6f593SDavid van Moolenbroek session_group_index(struct session_group *sg)
442eda6f593SDavid van Moolenbroek {
443eda6f593SDavid van Moolenbroek 	struct session_group   *sg2;
444eda6f593SDavid van Moolenbroek 	u_int			i;
445eda6f593SDavid van Moolenbroek 
446eda6f593SDavid van Moolenbroek 	i = 0;
447eda6f593SDavid van Moolenbroek 	TAILQ_FOREACH(sg2, &session_groups, entry) {
448eda6f593SDavid van Moolenbroek 		if (sg == sg2)
449eda6f593SDavid van Moolenbroek 			return (i);
450eda6f593SDavid van Moolenbroek 		i++;
451eda6f593SDavid van Moolenbroek 	}
452eda6f593SDavid van Moolenbroek 
453eda6f593SDavid van Moolenbroek 	fatalx("session group not found");
454eda6f593SDavid van Moolenbroek }
455eda6f593SDavid van Moolenbroek 
456eda6f593SDavid van Moolenbroek /*
457eda6f593SDavid van Moolenbroek  * Add a session to the session group containing target, creating it if
458eda6f593SDavid van Moolenbroek  * necessary.
459eda6f593SDavid van Moolenbroek  */
460eda6f593SDavid van Moolenbroek void
session_group_add(struct session * target,struct session * s)461eda6f593SDavid van Moolenbroek session_group_add(struct session *target, struct session *s)
462eda6f593SDavid van Moolenbroek {
463eda6f593SDavid van Moolenbroek 	struct session_group	*sg;
464eda6f593SDavid van Moolenbroek 
465eda6f593SDavid van Moolenbroek 	if ((sg = session_group_find(target)) == NULL) {
466eda6f593SDavid van Moolenbroek 		sg = xmalloc(sizeof *sg);
467eda6f593SDavid van Moolenbroek 		TAILQ_INSERT_TAIL(&session_groups, sg, entry);
468eda6f593SDavid van Moolenbroek 		TAILQ_INIT(&sg->sessions);
469eda6f593SDavid van Moolenbroek 		TAILQ_INSERT_TAIL(&sg->sessions, target, gentry);
470eda6f593SDavid van Moolenbroek 	}
471eda6f593SDavid van Moolenbroek 	TAILQ_INSERT_TAIL(&sg->sessions, s, gentry);
472eda6f593SDavid van Moolenbroek }
473eda6f593SDavid van Moolenbroek 
474eda6f593SDavid van Moolenbroek /* Remove a session from its group and destroy the group if empty. */
475eda6f593SDavid van Moolenbroek void
session_group_remove(struct session * s)476eda6f593SDavid van Moolenbroek session_group_remove(struct session *s)
477eda6f593SDavid van Moolenbroek {
478eda6f593SDavid van Moolenbroek 	struct session_group	*sg;
479eda6f593SDavid van Moolenbroek 
480eda6f593SDavid van Moolenbroek 	if ((sg = session_group_find(s)) == NULL)
481eda6f593SDavid van Moolenbroek 		return;
482eda6f593SDavid van Moolenbroek 	TAILQ_REMOVE(&sg->sessions, s, gentry);
483eda6f593SDavid van Moolenbroek 	if (TAILQ_NEXT(TAILQ_FIRST(&sg->sessions), gentry) == NULL)
484eda6f593SDavid van Moolenbroek 		TAILQ_REMOVE(&sg->sessions, TAILQ_FIRST(&sg->sessions), gentry);
485eda6f593SDavid van Moolenbroek 	if (TAILQ_EMPTY(&sg->sessions)) {
486eda6f593SDavid van Moolenbroek 		TAILQ_REMOVE(&session_groups, sg, entry);
487*0a6a1f1dSLionel Sambuc 		free(sg);
488eda6f593SDavid van Moolenbroek 	}
489eda6f593SDavid van Moolenbroek }
490eda6f593SDavid van Moolenbroek 
491eda6f593SDavid van Moolenbroek /* Synchronize a session to its session group. */
492eda6f593SDavid van Moolenbroek void
session_group_synchronize_to(struct session * s)493eda6f593SDavid van Moolenbroek session_group_synchronize_to(struct session *s)
494eda6f593SDavid van Moolenbroek {
495eda6f593SDavid van Moolenbroek 	struct session_group	*sg;
496eda6f593SDavid van Moolenbroek 	struct session		*target;
497eda6f593SDavid van Moolenbroek 
498eda6f593SDavid van Moolenbroek 	if ((sg = session_group_find(s)) == NULL)
499eda6f593SDavid van Moolenbroek 		return;
500eda6f593SDavid van Moolenbroek 
501eda6f593SDavid van Moolenbroek 	target = NULL;
502eda6f593SDavid van Moolenbroek 	TAILQ_FOREACH(target, &sg->sessions, gentry) {
503eda6f593SDavid van Moolenbroek 		if (target != s)
504eda6f593SDavid van Moolenbroek 			break;
505eda6f593SDavid van Moolenbroek 	}
506eda6f593SDavid van Moolenbroek 	session_group_synchronize1(target, s);
507eda6f593SDavid van Moolenbroek }
508eda6f593SDavid van Moolenbroek 
509eda6f593SDavid van Moolenbroek /* Synchronize a session group to a session. */
510eda6f593SDavid van Moolenbroek void
session_group_synchronize_from(struct session * target)511eda6f593SDavid van Moolenbroek session_group_synchronize_from(struct session *target)
512eda6f593SDavid van Moolenbroek {
513eda6f593SDavid van Moolenbroek 	struct session_group	*sg;
514eda6f593SDavid van Moolenbroek 	struct session		*s;
515eda6f593SDavid van Moolenbroek 
516eda6f593SDavid van Moolenbroek 	if ((sg = session_group_find(target)) == NULL)
517eda6f593SDavid van Moolenbroek 		return;
518eda6f593SDavid van Moolenbroek 
519eda6f593SDavid van Moolenbroek 	TAILQ_FOREACH(s, &sg->sessions, gentry) {
520eda6f593SDavid van Moolenbroek 		if (s != target)
521eda6f593SDavid van Moolenbroek 			session_group_synchronize1(target, s);
522eda6f593SDavid van Moolenbroek 	}
523eda6f593SDavid van Moolenbroek }
524eda6f593SDavid van Moolenbroek 
525eda6f593SDavid van Moolenbroek /*
526eda6f593SDavid van Moolenbroek  * Synchronize a session with a target session. This means destroying all
527eda6f593SDavid van Moolenbroek  * winlinks then recreating them, then updating the current window, last window
528eda6f593SDavid van Moolenbroek  * stack and alerts.
529eda6f593SDavid van Moolenbroek  */
530eda6f593SDavid van Moolenbroek void
session_group_synchronize1(struct session * target,struct session * s)531eda6f593SDavid van Moolenbroek session_group_synchronize1(struct session *target, struct session *s)
532eda6f593SDavid van Moolenbroek {
533eda6f593SDavid van Moolenbroek 	struct winlinks		 old_windows, *ww;
534eda6f593SDavid van Moolenbroek 	struct winlink_stack	 old_lastw;
535eda6f593SDavid van Moolenbroek 	struct winlink		*wl, *wl2;
536eda6f593SDavid van Moolenbroek 
537eda6f593SDavid van Moolenbroek 	/* Don't do anything if the session is empty (it'll be destroyed). */
538eda6f593SDavid van Moolenbroek 	ww = &target->windows;
539eda6f593SDavid van Moolenbroek 	if (RB_EMPTY(ww))
540eda6f593SDavid van Moolenbroek 		return;
541eda6f593SDavid van Moolenbroek 
542eda6f593SDavid van Moolenbroek 	/* If the current window has vanished, move to the next now. */
543eda6f593SDavid van Moolenbroek 	if (s->curw != NULL &&
544eda6f593SDavid van Moolenbroek 	    winlink_find_by_index(ww, s->curw->idx) == NULL &&
545eda6f593SDavid van Moolenbroek 	    session_last(s) != 0 && session_previous(s, 0) != 0)
546eda6f593SDavid van Moolenbroek 		session_next(s, 0);
547eda6f593SDavid van Moolenbroek 
548eda6f593SDavid van Moolenbroek 	/* Save the old pointer and reset it. */
549eda6f593SDavid van Moolenbroek 	memcpy(&old_windows, &s->windows, sizeof old_windows);
550eda6f593SDavid van Moolenbroek 	RB_INIT(&s->windows);
551eda6f593SDavid van Moolenbroek 
552eda6f593SDavid van Moolenbroek 	/* Link all the windows from the target. */
553eda6f593SDavid van Moolenbroek 	RB_FOREACH(wl, winlinks, ww) {
554eda6f593SDavid van Moolenbroek 		wl2 = winlink_add(&s->windows, wl->idx);
555eda6f593SDavid van Moolenbroek 		winlink_set_window(wl2, wl->window);
556*0a6a1f1dSLionel Sambuc 		notify_window_linked(s, wl2->window);
557eda6f593SDavid van Moolenbroek 		wl2->flags |= wl->flags & WINLINK_ALERTFLAGS;
558eda6f593SDavid van Moolenbroek 	}
559eda6f593SDavid van Moolenbroek 
560eda6f593SDavid van Moolenbroek 	/* Fix up the current window. */
561eda6f593SDavid van Moolenbroek 	if (s->curw != NULL)
562eda6f593SDavid van Moolenbroek 		s->curw = winlink_find_by_index(&s->windows, s->curw->idx);
563eda6f593SDavid van Moolenbroek 	else
564eda6f593SDavid van Moolenbroek 		s->curw = winlink_find_by_index(&s->windows, target->curw->idx);
565eda6f593SDavid van Moolenbroek 
566eda6f593SDavid van Moolenbroek 	/* Fix up the last window stack. */
567eda6f593SDavid van Moolenbroek 	memcpy(&old_lastw, &s->lastw, sizeof old_lastw);
568eda6f593SDavid van Moolenbroek 	TAILQ_INIT(&s->lastw);
569eda6f593SDavid van Moolenbroek 	TAILQ_FOREACH(wl, &old_lastw, sentry) {
570eda6f593SDavid van Moolenbroek 		wl2 = winlink_find_by_index(&s->windows, wl->idx);
571eda6f593SDavid van Moolenbroek 		if (wl2 != NULL)
572eda6f593SDavid van Moolenbroek 			TAILQ_INSERT_TAIL(&s->lastw, wl2, sentry);
573eda6f593SDavid van Moolenbroek 	}
574eda6f593SDavid van Moolenbroek 
575eda6f593SDavid van Moolenbroek 	/* Then free the old winlinks list. */
576eda6f593SDavid van Moolenbroek 	while (!RB_EMPTY(&old_windows)) {
577eda6f593SDavid van Moolenbroek 		wl = RB_ROOT(&old_windows);
578*0a6a1f1dSLionel Sambuc 		if (winlink_find_by_window_id(&s->windows, wl->window->id) == NULL)
579*0a6a1f1dSLionel Sambuc 		    notify_window_unlinked(s, wl->window);
580eda6f593SDavid van Moolenbroek 		winlink_remove(&old_windows, wl);
581eda6f593SDavid van Moolenbroek 	}
582eda6f593SDavid van Moolenbroek }
583*0a6a1f1dSLionel Sambuc 
584*0a6a1f1dSLionel Sambuc /* Renumber the windows across winlinks attached to a specific session. */
585*0a6a1f1dSLionel Sambuc void
session_renumber_windows(struct session * s)586*0a6a1f1dSLionel Sambuc session_renumber_windows(struct session *s)
587*0a6a1f1dSLionel Sambuc {
588*0a6a1f1dSLionel Sambuc 	struct winlink		*wl, *wl1, *wl_new;
589*0a6a1f1dSLionel Sambuc 	struct winlinks		 old_wins;
590*0a6a1f1dSLionel Sambuc 	struct winlink_stack	 old_lastw;
591*0a6a1f1dSLionel Sambuc 	int			 new_idx, new_curw_idx;
592*0a6a1f1dSLionel Sambuc 
593*0a6a1f1dSLionel Sambuc 	/* Save and replace old window list. */
594*0a6a1f1dSLionel Sambuc 	memcpy(&old_wins, &s->windows, sizeof old_wins);
595*0a6a1f1dSLionel Sambuc 	RB_INIT(&s->windows);
596*0a6a1f1dSLionel Sambuc 
597*0a6a1f1dSLionel Sambuc 	/* Start renumbering from the base-index if it's set. */
598*0a6a1f1dSLionel Sambuc 	new_idx = options_get_number(&s->options, "base-index");
599*0a6a1f1dSLionel Sambuc 	new_curw_idx = 0;
600*0a6a1f1dSLionel Sambuc 
601*0a6a1f1dSLionel Sambuc 	/* Go through the winlinks and assign new indexes. */
602*0a6a1f1dSLionel Sambuc 	RB_FOREACH(wl, winlinks, &old_wins) {
603*0a6a1f1dSLionel Sambuc 		wl_new = winlink_add(&s->windows, new_idx);
604*0a6a1f1dSLionel Sambuc 		winlink_set_window(wl_new, wl->window);
605*0a6a1f1dSLionel Sambuc 		wl_new->flags |= wl->flags & WINLINK_ALERTFLAGS;
606*0a6a1f1dSLionel Sambuc 
607*0a6a1f1dSLionel Sambuc 		if (wl == s->curw)
608*0a6a1f1dSLionel Sambuc 			new_curw_idx = wl_new->idx;
609*0a6a1f1dSLionel Sambuc 
610*0a6a1f1dSLionel Sambuc 		new_idx++;
611*0a6a1f1dSLionel Sambuc 	}
612*0a6a1f1dSLionel Sambuc 
613*0a6a1f1dSLionel Sambuc 	/* Fix the stack of last windows now. */
614*0a6a1f1dSLionel Sambuc 	memcpy(&old_lastw, &s->lastw, sizeof old_lastw);
615*0a6a1f1dSLionel Sambuc 	TAILQ_INIT(&s->lastw);
616*0a6a1f1dSLionel Sambuc 	TAILQ_FOREACH(wl, &old_lastw, sentry) {
617*0a6a1f1dSLionel Sambuc 		wl_new = winlink_find_by_window(&s->windows, wl->window);
618*0a6a1f1dSLionel Sambuc 		if (wl_new != NULL)
619*0a6a1f1dSLionel Sambuc 			TAILQ_INSERT_TAIL(&s->lastw, wl_new, sentry);
620*0a6a1f1dSLionel Sambuc 	}
621*0a6a1f1dSLionel Sambuc 
622*0a6a1f1dSLionel Sambuc 	/* Set the current window. */
623*0a6a1f1dSLionel Sambuc 	s->curw = winlink_find_by_index(&s->windows, new_curw_idx);
624*0a6a1f1dSLionel Sambuc 
625*0a6a1f1dSLionel Sambuc 	/* Free the old winlinks (reducing window references too). */
626*0a6a1f1dSLionel Sambuc 	RB_FOREACH_SAFE(wl, winlinks, &old_wins, wl1)
627*0a6a1f1dSLionel Sambuc 		winlink_remove(&old_wins, wl);
628*0a6a1f1dSLionel Sambuc }
629