xref: /netbsd-src/external/bsd/tmux/dist/server-fn.c (revision 890b6d91a44b7fcb2dfbcbc1e93463086e462d2d)
199e242abSchristos /* $OpenBSD$ */
2698d5317Sjmmv 
3698d5317Sjmmv /*
4f26e8bc9Schristos  * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
5698d5317Sjmmv  *
6698d5317Sjmmv  * Permission to use, copy, modify, and distribute this software for any
7698d5317Sjmmv  * purpose with or without fee is hereby granted, provided that the above
8698d5317Sjmmv  * copyright notice and this permission notice appear in all copies.
9698d5317Sjmmv  *
10698d5317Sjmmv  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11698d5317Sjmmv  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12698d5317Sjmmv  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13698d5317Sjmmv  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14698d5317Sjmmv  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15698d5317Sjmmv  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16698d5317Sjmmv  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17698d5317Sjmmv  */
18698d5317Sjmmv 
19698d5317Sjmmv #include <sys/types.h>
20c7e17de0Schristos #include <sys/wait.h>
21f26e8bc9Schristos #include <sys/uio.h>
22698d5317Sjmmv 
2361fba46bSchristos #include <stdlib.h>
24698d5317Sjmmv #include <string.h>
25698d5317Sjmmv #include <time.h>
26698d5317Sjmmv #include <unistd.h>
27698d5317Sjmmv 
28698d5317Sjmmv #include "tmux.h"
29698d5317Sjmmv 
30e9a2d6faSchristos static void	server_destroy_session_group(struct session *);
31698d5317Sjmmv 
32698d5317Sjmmv void
33698d5317Sjmmv server_redraw_client(struct client *c)
34698d5317Sjmmv {
350a274e86Schristos 	c->flags |= CLIENT_ALLREDRAWFLAGS;
36698d5317Sjmmv }
37698d5317Sjmmv 
38698d5317Sjmmv void
39698d5317Sjmmv server_status_client(struct client *c)
40698d5317Sjmmv {
410a274e86Schristos 	c->flags |= CLIENT_REDRAWSTATUS;
42698d5317Sjmmv }
43698d5317Sjmmv 
44698d5317Sjmmv void
45698d5317Sjmmv server_redraw_session(struct session *s)
46698d5317Sjmmv {
47698d5317Sjmmv 	struct client	*c;
48698d5317Sjmmv 
4999e242abSchristos 	TAILQ_FOREACH(c, &clients, entry) {
50698d5317Sjmmv 		if (c->session == s)
51698d5317Sjmmv 			server_redraw_client(c);
52698d5317Sjmmv 	}
53698d5317Sjmmv }
54698d5317Sjmmv 
55698d5317Sjmmv void
56698d5317Sjmmv server_redraw_session_group(struct session *s)
57698d5317Sjmmv {
58698d5317Sjmmv 	struct session_group	*sg;
59698d5317Sjmmv 
60e9a2d6faSchristos 	if ((sg = session_group_contains(s)) == NULL)
61698d5317Sjmmv 		server_redraw_session(s);
62698d5317Sjmmv 	else {
63698d5317Sjmmv 		TAILQ_FOREACH(s, &sg->sessions, gentry)
64698d5317Sjmmv 			server_redraw_session(s);
65698d5317Sjmmv 	}
66698d5317Sjmmv }
67698d5317Sjmmv 
68698d5317Sjmmv void
69698d5317Sjmmv server_status_session(struct session *s)
70698d5317Sjmmv {
71698d5317Sjmmv 	struct client	*c;
72698d5317Sjmmv 
7399e242abSchristos 	TAILQ_FOREACH(c, &clients, entry) {
74698d5317Sjmmv 		if (c->session == s)
75698d5317Sjmmv 			server_status_client(c);
76698d5317Sjmmv 	}
77698d5317Sjmmv }
78698d5317Sjmmv 
79698d5317Sjmmv void
80698d5317Sjmmv server_status_session_group(struct session *s)
81698d5317Sjmmv {
82698d5317Sjmmv 	struct session_group	*sg;
83698d5317Sjmmv 
84e9a2d6faSchristos 	if ((sg = session_group_contains(s)) == NULL)
85698d5317Sjmmv 		server_status_session(s);
86698d5317Sjmmv 	else {
87698d5317Sjmmv 		TAILQ_FOREACH(s, &sg->sessions, gentry)
88698d5317Sjmmv 			server_status_session(s);
89698d5317Sjmmv 	}
90698d5317Sjmmv }
91698d5317Sjmmv 
92698d5317Sjmmv void
93698d5317Sjmmv server_redraw_window(struct window *w)
94698d5317Sjmmv {
95698d5317Sjmmv 	struct client	*c;
96698d5317Sjmmv 
9799e242abSchristos 	TAILQ_FOREACH(c, &clients, entry) {
9899e242abSchristos 		if (c->session != NULL && c->session->curw->window == w)
99698d5317Sjmmv 			server_redraw_client(c);
100698d5317Sjmmv 	}
101698d5317Sjmmv }
102698d5317Sjmmv 
103698d5317Sjmmv void
104698d5317Sjmmv server_redraw_window_borders(struct window *w)
105698d5317Sjmmv {
106698d5317Sjmmv 	struct client	*c;
107698d5317Sjmmv 
10899e242abSchristos 	TAILQ_FOREACH(c, &clients, entry) {
10999e242abSchristos 		if (c->session != NULL && c->session->curw->window == w)
1100a274e86Schristos 			c->flags |= CLIENT_REDRAWBORDERS;
111698d5317Sjmmv 	}
112698d5317Sjmmv }
113698d5317Sjmmv 
114698d5317Sjmmv void
115698d5317Sjmmv server_status_window(struct window *w)
116698d5317Sjmmv {
117698d5317Sjmmv 	struct session	*s;
118698d5317Sjmmv 
119698d5317Sjmmv 	/*
120698d5317Sjmmv 	 * This is slightly different. We want to redraw the status line of any
121698d5317Sjmmv 	 * clients containing this window rather than anywhere it is the
122698d5317Sjmmv 	 * current window.
123698d5317Sjmmv 	 */
124698d5317Sjmmv 
125698d5317Sjmmv 	RB_FOREACH(s, sessions, &sessions) {
12699e242abSchristos 		if (session_has(s, w))
127698d5317Sjmmv 			server_status_session(s);
128698d5317Sjmmv 	}
129698d5317Sjmmv }
130698d5317Sjmmv 
131698d5317Sjmmv void
132698d5317Sjmmv server_lock(void)
133698d5317Sjmmv {
134698d5317Sjmmv 	struct client	*c;
135698d5317Sjmmv 
13699e242abSchristos 	TAILQ_FOREACH(c, &clients, entry) {
13799e242abSchristos 		if (c->session != NULL)
138698d5317Sjmmv 			server_lock_client(c);
139698d5317Sjmmv 	}
140698d5317Sjmmv }
141698d5317Sjmmv 
142698d5317Sjmmv void
143698d5317Sjmmv server_lock_session(struct session *s)
144698d5317Sjmmv {
145698d5317Sjmmv 	struct client	*c;
146698d5317Sjmmv 
14799e242abSchristos 	TAILQ_FOREACH(c, &clients, entry) {
14899e242abSchristos 		if (c->session == s)
149698d5317Sjmmv 			server_lock_client(c);
150698d5317Sjmmv 	}
151698d5317Sjmmv }
152698d5317Sjmmv 
153698d5317Sjmmv void
154698d5317Sjmmv server_lock_client(struct client *c)
155698d5317Sjmmv {
156698d5317Sjmmv 	const char	*cmd;
15761fba46bSchristos 
15861fba46bSchristos 	if (c->flags & CLIENT_CONTROL)
15961fba46bSchristos 		return;
160698d5317Sjmmv 
161698d5317Sjmmv 	if (c->flags & CLIENT_SUSPENDED)
162698d5317Sjmmv 		return;
163698d5317Sjmmv 
164f26e8bc9Schristos 	cmd = options_get_string(c->session->options, "lock-command");
165fe99a117Schristos 	if (*cmd == '\0' || strlen(cmd) + 1 > MAX_IMSGSIZE - IMSG_HEADER_SIZE)
166698d5317Sjmmv 		return;
167698d5317Sjmmv 
168698d5317Sjmmv 	tty_stop_tty(&c->tty);
169698d5317Sjmmv 	tty_raw(&c->tty, tty_term_string(c->tty.term, TTYC_SMCUP));
170698d5317Sjmmv 	tty_raw(&c->tty, tty_term_string(c->tty.term, TTYC_CLEAR));
17161fba46bSchristos 	tty_raw(&c->tty, tty_term_string(c->tty.term, TTYC_E3));
172698d5317Sjmmv 
173698d5317Sjmmv 	c->flags |= CLIENT_SUSPENDED;
174fe99a117Schristos 	proc_send(c->peer, MSG_LOCK, -1, cmd, strlen(cmd) + 1);
175698d5317Sjmmv }
176698d5317Sjmmv 
177698d5317Sjmmv void
178c7e17de0Schristos server_kill_pane(struct window_pane *wp)
179c7e17de0Schristos {
180c7e17de0Schristos 	struct window	*w = wp->window;
181c7e17de0Schristos 
182c7e17de0Schristos 	if (window_count_panes(w) == 1) {
183e271dbb8Schristos 		server_kill_window(w, 1);
184c7e17de0Schristos 		recalculate_sizes();
185c7e17de0Schristos 	} else {
186c7e17de0Schristos 		server_unzoom_window(w);
187e271dbb8Schristos 		server_client_remove_pane(wp);
188c7e17de0Schristos 		layout_close_pane(wp);
189c7e17de0Schristos 		window_remove_pane(w, wp);
190c7e17de0Schristos 		server_redraw_window(w);
191c7e17de0Schristos 	}
192c7e17de0Schristos }
193c7e17de0Schristos 
194c7e17de0Schristos void
195e271dbb8Schristos server_kill_window(struct window *w, int renumber)
196698d5317Sjmmv {
197e271dbb8Schristos 	struct session	*s, *s1;
198698d5317Sjmmv 	struct winlink	*wl;
199698d5317Sjmmv 
200e271dbb8Schristos 	RB_FOREACH_SAFE(s, sessions, &sessions, s1) {
20199e242abSchristos 		if (!session_has(s, w))
202698d5317Sjmmv 			continue;
203e271dbb8Schristos 
20499e242abSchristos 		server_unzoom_window(w);
205698d5317Sjmmv 		while ((wl = winlink_find_by_window(&s->windows, w)) != NULL) {
206698d5317Sjmmv 			if (session_detach(s, wl)) {
207698d5317Sjmmv 				server_destroy_session_group(s);
208698d5317Sjmmv 				break;
209f844e94eSwiz 			}
210698d5317Sjmmv 			server_redraw_session_group(s);
211698d5317Sjmmv 		}
21261fba46bSchristos 
213e271dbb8Schristos 		if (renumber)
214e271dbb8Schristos 			server_renumber_session(s);
215e271dbb8Schristos 	}
216e271dbb8Schristos 	recalculate_sizes();
217e271dbb8Schristos }
218e271dbb8Schristos 
219e271dbb8Schristos void
220e271dbb8Schristos server_renumber_session(struct session *s)
221e271dbb8Schristos {
222e271dbb8Schristos 	struct session_group	*sg;
223e271dbb8Schristos 
224f26e8bc9Schristos 	if (options_get_number(s->options, "renumber-windows")) {
225e9a2d6faSchristos 		if ((sg = session_group_contains(s)) != NULL) {
226e271dbb8Schristos 			TAILQ_FOREACH(s, &sg->sessions, gentry)
227e271dbb8Schristos 			    session_renumber_windows(s);
22861fba46bSchristos 		} else
22961fba46bSchristos 			session_renumber_windows(s);
230698d5317Sjmmv 	}
231698d5317Sjmmv }
232e271dbb8Schristos 
233e271dbb8Schristos void
234e271dbb8Schristos server_renumber_all(void)
235e271dbb8Schristos {
236e271dbb8Schristos 	struct session	*s;
237e271dbb8Schristos 
238e271dbb8Schristos 	RB_FOREACH(s, sessions, &sessions)
239e271dbb8Schristos 		server_renumber_session(s);
24061fba46bSchristos }
241698d5317Sjmmv 
242698d5317Sjmmv int
243698d5317Sjmmv server_link_window(struct session *src, struct winlink *srcwl,
24499e242abSchristos     struct session *dst, int dstidx, int killflag, int selectflag,
24599e242abSchristos     char **cause)
246698d5317Sjmmv {
247698d5317Sjmmv 	struct winlink		*dstwl;
248698d5317Sjmmv 	struct session_group	*srcsg, *dstsg;
249698d5317Sjmmv 
250e9a2d6faSchristos 	srcsg = session_group_contains(src);
251e9a2d6faSchristos 	dstsg = session_group_contains(dst);
252698d5317Sjmmv 	if (src != dst && srcsg != NULL && dstsg != NULL && srcsg == dstsg) {
253698d5317Sjmmv 		xasprintf(cause, "sessions are grouped");
254698d5317Sjmmv 		return (-1);
255698d5317Sjmmv 	}
256698d5317Sjmmv 
257698d5317Sjmmv 	dstwl = NULL;
258698d5317Sjmmv 	if (dstidx != -1)
259698d5317Sjmmv 		dstwl = winlink_find_by_index(&dst->windows, dstidx);
260698d5317Sjmmv 	if (dstwl != NULL) {
261698d5317Sjmmv 		if (dstwl->window == srcwl->window) {
262698d5317Sjmmv 			xasprintf(cause, "same index: %d", dstidx);
263698d5317Sjmmv 			return (-1);
264698d5317Sjmmv 		}
265698d5317Sjmmv 		if (killflag) {
266698d5317Sjmmv 			/*
267698d5317Sjmmv 			 * Can't use session_detach as it will destroy session
268698d5317Sjmmv 			 * if this makes it empty.
269698d5317Sjmmv 			 */
270e9a2d6faSchristos 			notify_session_window("window-unlinked", dst,
271e9a2d6faSchristos 			    dstwl->window);
272698d5317Sjmmv 			dstwl->flags &= ~WINLINK_ALERTFLAGS;
273698d5317Sjmmv 			winlink_stack_remove(&dst->lastw, dstwl);
274698d5317Sjmmv 			winlink_remove(&dst->windows, dstwl);
275698d5317Sjmmv 
276698d5317Sjmmv 			/* Force select/redraw if current. */
277698d5317Sjmmv 			if (dstwl == dst->curw) {
278698d5317Sjmmv 				selectflag = 1;
279698d5317Sjmmv 				dst->curw = NULL;
280698d5317Sjmmv 			}
281698d5317Sjmmv 		}
282698d5317Sjmmv 	}
283698d5317Sjmmv 
284698d5317Sjmmv 	if (dstidx == -1)
285f26e8bc9Schristos 		dstidx = -1 - options_get_number(dst->options, "base-index");
286698d5317Sjmmv 	dstwl = session_attach(dst, srcwl->window, dstidx, cause);
287698d5317Sjmmv 	if (dstwl == NULL)
288698d5317Sjmmv 		return (-1);
289698d5317Sjmmv 
290698d5317Sjmmv 	if (selectflag)
291698d5317Sjmmv 		session_select(dst, dstwl->idx);
292698d5317Sjmmv 	server_redraw_session_group(dst);
293698d5317Sjmmv 
294698d5317Sjmmv 	return (0);
295698d5317Sjmmv }
296698d5317Sjmmv 
297698d5317Sjmmv void
298698d5317Sjmmv server_unlink_window(struct session *s, struct winlink *wl)
299698d5317Sjmmv {
300698d5317Sjmmv 	if (session_detach(s, wl))
301698d5317Sjmmv 		server_destroy_session_group(s);
302698d5317Sjmmv 	else
303698d5317Sjmmv 		server_redraw_session_group(s);
304698d5317Sjmmv }
305698d5317Sjmmv 
306698d5317Sjmmv void
307e9a2d6faSchristos server_destroy_pane(struct window_pane *wp, int notify)
308698d5317Sjmmv {
309698d5317Sjmmv 	struct window		*w = wp->window;
31061fba46bSchristos 	struct screen_write_ctx	 ctx;
31161fba46bSchristos 	struct grid_cell	 gc;
312e271dbb8Schristos 	int			 remain_on_exit;
31346548964Swiz 	const char		*s;
31446548964Swiz 	char			*expanded;
31546548964Swiz 	u_int			 sx = screen_size_x(&wp->base);
31646548964Swiz 	u_int			 sy = screen_size_y(&wp->base);
317698d5317Sjmmv 
318698d5317Sjmmv 	if (wp->fd != -1) {
31999e242abSchristos #ifdef HAVE_UTEMPTER
32099e242abSchristos 		utempter_remove_record(wp->fd);
32199e242abSchristos #endif
322698d5317Sjmmv 		bufferevent_free(wp->event);
3230a274e86Schristos 		wp->event = NULL;
32461fba46bSchristos 		close(wp->fd);
325698d5317Sjmmv 		wp->fd = -1;
326698d5317Sjmmv 	}
327698d5317Sjmmv 
328e271dbb8Schristos 	remain_on_exit = options_get_number(wp->options, "remain-on-exit");
329e271dbb8Schristos 	if (remain_on_exit != 0 && (~wp->flags & PANE_STATUSREADY))
330698d5317Sjmmv 		return;
331e271dbb8Schristos 	switch (remain_on_exit) {
332e271dbb8Schristos 	case 0:
333e271dbb8Schristos 		break;
334e271dbb8Schristos 	case 2:
335e271dbb8Schristos 		if (WIFEXITED(wp->status) && WEXITSTATUS(wp->status) == 0)
336e271dbb8Schristos 			break;
337e271dbb8Schristos 		/* FALLTHROUGH */
338e271dbb8Schristos 	case 1:
339c7e17de0Schristos 		if (wp->flags & PANE_STATUSDRAWN)
340c7e17de0Schristos 			return;
341c7e17de0Schristos 		wp->flags |= PANE_STATUSDRAWN;
342c7e17de0Schristos 
34346548964Swiz 		gettimeofday(&wp->dead_time, NULL);
344e9a2d6faSchristos 		if (notify)
345e9a2d6faSchristos 			notify_pane("pane-died", wp);
346e9a2d6faSchristos 
34746548964Swiz 		s = options_get_string(wp->options, "remain-on-exit-format");
34846548964Swiz 		if (*s != '\0') {
349e271dbb8Schristos 			screen_write_start_pane(&ctx, wp, &wp->base);
35046548964Swiz 			screen_write_scrollregion(&ctx, 0, sy - 1);
35146548964Swiz 			screen_write_cursormove(&ctx, 0, sy - 1, 0);
352fe99a117Schristos 			screen_write_linefeed(&ctx, 1, 8);
35361fba46bSchristos 			memcpy(&gc, &grid_default_cell, sizeof gc);
354c7e17de0Schristos 
35546548964Swiz 			expanded = format_single(NULL, s, NULL, NULL, NULL, wp);
35646548964Swiz 			format_draw(&ctx, &gc, sx, expanded, NULL, 0);
35746548964Swiz 			free(expanded);
358c7e17de0Schristos 
35961fba46bSchristos 			screen_write_stop(&ctx);
36046548964Swiz 		}
36146548964Swiz 		wp->base.mode &= ~MODE_CURSOR;
36246548964Swiz 
36361fba46bSchristos 		wp->flags |= PANE_REDRAW;
36461fba46bSchristos 		return;
36561fba46bSchristos 	}
366698d5317Sjmmv 
367e9a2d6faSchristos 	if (notify)
368e9a2d6faSchristos 		notify_pane("pane-exited", wp);
369e9a2d6faSchristos 
37061fba46bSchristos 	server_unzoom_window(w);
371e271dbb8Schristos 	server_client_remove_pane(wp);
372698d5317Sjmmv 	layout_close_pane(wp);
373698d5317Sjmmv 	window_remove_pane(w, wp);
374698d5317Sjmmv 
375698d5317Sjmmv 	if (TAILQ_EMPTY(&w->panes))
376e271dbb8Schristos 		server_kill_window(w, 1);
377698d5317Sjmmv 	else
378698d5317Sjmmv 		server_redraw_window(w);
379698d5317Sjmmv }
380698d5317Sjmmv 
381e9a2d6faSchristos static void
382698d5317Sjmmv server_destroy_session_group(struct session *s)
383698d5317Sjmmv {
384698d5317Sjmmv 	struct session_group	*sg;
38561fba46bSchristos 	struct session		*s1;
386698d5317Sjmmv 
387f844e94eSwiz 	if ((sg = session_group_contains(s)) == NULL) {
388698d5317Sjmmv 		server_destroy_session(s);
389f844e94eSwiz 		session_destroy(s, 1, __func__);
390f844e94eSwiz 	} else {
39161fba46bSchristos 		TAILQ_FOREACH_SAFE(s, &sg->sessions, gentry, s1) {
392698d5317Sjmmv 			server_destroy_session(s);
39330744affSchristos 			session_destroy(s, 1, __func__);
39461fba46bSchristos 		}
395698d5317Sjmmv 	}
396698d5317Sjmmv }
397698d5317Sjmmv 
398e9a2d6faSchristos static struct session *
399f844e94eSwiz server_find_session(struct session *s,
400f844e94eSwiz     int (*f)(struct session *, struct session *))
401698d5317Sjmmv {
402e271dbb8Schristos 	struct session *s_loop, *s_out = NULL;
403698d5317Sjmmv 
404698d5317Sjmmv 	RB_FOREACH(s_loop, sessions, &sessions) {
405f844e94eSwiz 		if (s_loop != s && (s_out == NULL || f(s_loop, s_out)))
406698d5317Sjmmv 			s_out = s_loop;
407698d5317Sjmmv 	}
408698d5317Sjmmv 	return (s_out);
409698d5317Sjmmv }
410698d5317Sjmmv 
411f844e94eSwiz static int
412f844e94eSwiz server_newer_session(struct session *s_loop, struct session *s_out)
413e271dbb8Schristos {
414*890b6d91Swiz 	return (timercmp(&s_loop->activity_time, &s_out->activity_time, >));
415e271dbb8Schristos }
416f844e94eSwiz 
417f844e94eSwiz static int
418f844e94eSwiz server_newer_detached_session(struct session *s_loop, struct session *s_out)
419f844e94eSwiz {
420f844e94eSwiz 	if (s_loop->attached)
421f844e94eSwiz 		return (0);
422f844e94eSwiz 	return (server_newer_session(s_loop, s_out));
423e271dbb8Schristos }
424e271dbb8Schristos 
425698d5317Sjmmv void
426698d5317Sjmmv server_destroy_session(struct session *s)
427698d5317Sjmmv {
428698d5317Sjmmv 	struct client	*c;
429f844e94eSwiz 	struct session	*s_new = NULL;
430e271dbb8Schristos 	int		 detach_on_destroy;
431698d5317Sjmmv 
432e271dbb8Schristos 	detach_on_destroy = options_get_number(s->options, "detach-on-destroy");
433e271dbb8Schristos 	if (detach_on_destroy == 0)
434f844e94eSwiz 		s_new = server_find_session(s, server_newer_session);
435e271dbb8Schristos 	else if (detach_on_destroy == 2)
436f844e94eSwiz 		s_new = server_find_session(s, server_newer_detached_session);
437f844e94eSwiz 	else if (detach_on_destroy == 3)
438f844e94eSwiz 		s_new = session_previous_session(s);
439f844e94eSwiz 	else if (detach_on_destroy == 4)
440f844e94eSwiz 		s_new = session_next_session(s);
441f844e94eSwiz 	if (s_new == s)
442698d5317Sjmmv 		s_new = NULL;
44399e242abSchristos 	TAILQ_FOREACH(c, &clients, entry) {
44499e242abSchristos 		if (c->session != s)
445698d5317Sjmmv 			continue;
446f844e94eSwiz 		c->session = NULL;
447f844e94eSwiz 		c->last_session = NULL;
44846548964Swiz 		server_client_set_session(c, s_new);
44946548964Swiz 		if (s_new == NULL)
450698d5317Sjmmv 			c->flags |= CLIENT_EXIT;
451698d5317Sjmmv 	}
452698d5317Sjmmv 	recalculate_sizes();
453698d5317Sjmmv }
454698d5317Sjmmv 
455698d5317Sjmmv void
456698d5317Sjmmv server_check_unattached(void)
457698d5317Sjmmv {
458698d5317Sjmmv 	struct session		*s;
459f844e94eSwiz 	struct session_group	*sg;
460698d5317Sjmmv 
461698d5317Sjmmv 	/*
462698d5317Sjmmv 	 * If any sessions are no longer attached and have destroy-unattached
463698d5317Sjmmv 	 * set, collect them.
464698d5317Sjmmv 	 */
465698d5317Sjmmv 	RB_FOREACH(s, sessions, &sessions) {
4660a274e86Schristos 		if (s->attached != 0)
467698d5317Sjmmv 			continue;
468f844e94eSwiz 		switch (options_get_number(s->options, "destroy-unattached")) {
469f844e94eSwiz 		case 0: /* off */
470f844e94eSwiz 			continue;
471f844e94eSwiz 		case 1: /* on */
472f844e94eSwiz 			break;
473f844e94eSwiz 		case 2: /* keep-last */
474f844e94eSwiz 			sg = session_group_contains(s);
475f844e94eSwiz 			if (sg == NULL || session_group_count(sg) <= 1)
476f844e94eSwiz 				continue;
477f844e94eSwiz 			break;
478f844e94eSwiz 		case 3: /* keep-group */
479f844e94eSwiz 			sg = session_group_contains(s);
480f844e94eSwiz 			if (sg != NULL && session_group_count(sg) == 1)
481f844e94eSwiz 				continue;
482f844e94eSwiz 			break;
483f844e94eSwiz 		}
48430744affSchristos 		session_destroy(s, 1, __func__);
485698d5317Sjmmv 	}
486698d5317Sjmmv }
487698d5317Sjmmv 
48861fba46bSchristos void
48961fba46bSchristos server_unzoom_window(struct window *w)
49061fba46bSchristos {
491*890b6d91Swiz 	if (window_unzoom(w, 1) == 0)
49261fba46bSchristos 		server_redraw_window(w);
49399e242abSchristos }
494