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