xref: /openbsd-src/usr.bin/tmux/server-fn.c (revision 104e139db9e7dd9d6bdc9bc0eb930c38119df519)
1*104e139dSnicm /* $OpenBSD: server-fn.c,v 1.138 2024/11/15 14:09:04 nicm Exp $ */
2311827fbSnicm 
3311827fbSnicm /*
498ca8272Snicm  * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
5311827fbSnicm  *
6311827fbSnicm  * Permission to use, copy, modify, and distribute this software for any
7311827fbSnicm  * purpose with or without fee is hereby granted, provided that the above
8311827fbSnicm  * copyright notice and this permission notice appear in all copies.
9311827fbSnicm  *
10311827fbSnicm  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11311827fbSnicm  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12311827fbSnicm  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13311827fbSnicm  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14311827fbSnicm  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15311827fbSnicm  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16311827fbSnicm  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17311827fbSnicm  */
18311827fbSnicm 
19311827fbSnicm #include <sys/types.h>
205b8ac713Snicm #include <sys/queue.h>
21514fd64dSnicm #include <sys/wait.h>
225b8ac713Snicm #include <sys/uio.h>
23311827fbSnicm 
245b8ac713Snicm #include <imsg.h>
257d053cf9Snicm #include <stdlib.h>
26311827fbSnicm #include <string.h>
27b04643ffSnicm #include <time.h>
28311827fbSnicm #include <unistd.h>
29311827fbSnicm 
30311827fbSnicm #include "tmux.h"
31311827fbSnicm 
32ced21769Snicm static void	server_destroy_session_group(struct session *);
33c5e332b7Snicm 
346f7d62ebSnicm void
35311827fbSnicm server_redraw_client(struct client *c)
36311827fbSnicm {
37e7808201Snicm 	c->flags |= CLIENT_ALLREDRAWFLAGS;
38311827fbSnicm }
39311827fbSnicm 
40311827fbSnicm void
41311827fbSnicm server_status_client(struct client *c)
42311827fbSnicm {
43e7808201Snicm 	c->flags |= CLIENT_REDRAWSTATUS;
44311827fbSnicm }
45311827fbSnicm 
46311827fbSnicm void
47311827fbSnicm server_redraw_session(struct session *s)
48311827fbSnicm {
49311827fbSnicm 	struct client	*c;
50311827fbSnicm 
5182873134Snicm 	TAILQ_FOREACH(c, &clients, entry) {
52311827fbSnicm 		if (c->session == s)
53311827fbSnicm 			server_redraw_client(c);
54311827fbSnicm 	}
55311827fbSnicm }
56311827fbSnicm 
57311827fbSnicm void
5801b2421eSnicm server_redraw_session_group(struct session *s)
5901b2421eSnicm {
6001b2421eSnicm 	struct session_group	*sg;
6101b2421eSnicm 
6206440b28Snicm 	if ((sg = session_group_contains(s)) == NULL)
6301b2421eSnicm 		server_redraw_session(s);
6401b2421eSnicm 	else {
6501b2421eSnicm 		TAILQ_FOREACH(s, &sg->sessions, gentry)
6601b2421eSnicm 			server_redraw_session(s);
6701b2421eSnicm 	}
6801b2421eSnicm }
6901b2421eSnicm 
7001b2421eSnicm void
71311827fbSnicm server_status_session(struct session *s)
72311827fbSnicm {
73311827fbSnicm 	struct client	*c;
74311827fbSnicm 
7582873134Snicm 	TAILQ_FOREACH(c, &clients, entry) {
76311827fbSnicm 		if (c->session == s)
77311827fbSnicm 			server_status_client(c);
78311827fbSnicm 	}
79311827fbSnicm }
80311827fbSnicm 
81311827fbSnicm void
8201b2421eSnicm server_status_session_group(struct session *s)
8301b2421eSnicm {
8401b2421eSnicm 	struct session_group	*sg;
8501b2421eSnicm 
8606440b28Snicm 	if ((sg = session_group_contains(s)) == NULL)
8701b2421eSnicm 		server_status_session(s);
8801b2421eSnicm 	else {
8901b2421eSnicm 		TAILQ_FOREACH(s, &sg->sessions, gentry)
9001b2421eSnicm 			server_status_session(s);
9101b2421eSnicm 	}
9201b2421eSnicm }
9301b2421eSnicm 
9401b2421eSnicm void
95311827fbSnicm server_redraw_window(struct window *w)
96311827fbSnicm {
97311827fbSnicm 	struct client	*c;
98311827fbSnicm 
9982873134Snicm 	TAILQ_FOREACH(c, &clients, entry) {
10082873134Snicm 		if (c->session != NULL && c->session->curw->window == w)
101311827fbSnicm 			server_redraw_client(c);
102311827fbSnicm 	}
103311827fbSnicm }
104311827fbSnicm 
105311827fbSnicm void
106b9b22aa8Snicm server_redraw_window_borders(struct window *w)
107b9b22aa8Snicm {
108b9b22aa8Snicm 	struct client	*c;
109b9b22aa8Snicm 
11082873134Snicm 	TAILQ_FOREACH(c, &clients, entry) {
11182873134Snicm 		if (c->session != NULL && c->session->curw->window == w)
112e7808201Snicm 			c->flags |= CLIENT_REDRAWBORDERS;
113b9b22aa8Snicm 	}
114b9b22aa8Snicm }
115b9b22aa8Snicm 
116b9b22aa8Snicm void
117311827fbSnicm server_status_window(struct window *w)
118311827fbSnicm {
119311827fbSnicm 	struct session	*s;
120311827fbSnicm 
121311827fbSnicm 	/*
122311827fbSnicm 	 * This is slightly different. We want to redraw the status line of any
123311827fbSnicm 	 * clients containing this window rather than anywhere it is the
124311827fbSnicm 	 * current window.
125311827fbSnicm 	 */
126311827fbSnicm 
12759996dc3Snicm 	RB_FOREACH(s, sessions, &sessions) {
12852e9404eSnicm 		if (session_has(s, w))
129311827fbSnicm 			server_status_session(s);
130311827fbSnicm 	}
131311827fbSnicm }
132311827fbSnicm 
133311827fbSnicm void
134311827fbSnicm server_lock(void)
135311827fbSnicm {
136311827fbSnicm 	struct client	*c;
137311827fbSnicm 
13882873134Snicm 	TAILQ_FOREACH(c, &clients, entry) {
13982873134Snicm 		if (c->session != NULL)
140168ee6d3Snicm 			server_lock_client(c);
141168ee6d3Snicm 	}
142168ee6d3Snicm }
143168ee6d3Snicm 
144168ee6d3Snicm void
145168ee6d3Snicm server_lock_session(struct session *s)
146168ee6d3Snicm {
147168ee6d3Snicm 	struct client	*c;
148168ee6d3Snicm 
14982873134Snicm 	TAILQ_FOREACH(c, &clients, entry) {
15082873134Snicm 		if (c->session == s)
151168ee6d3Snicm 			server_lock_client(c);
152168ee6d3Snicm 	}
153168ee6d3Snicm }
154168ee6d3Snicm 
155168ee6d3Snicm void
156168ee6d3Snicm server_lock_client(struct client *c)
157168ee6d3Snicm {
158168ee6d3Snicm 	const char	*cmd;
159311827fbSnicm 
16043a67eb2Snicm 	if (c->flags & CLIENT_CONTROL)
161721350bcSnicm 		return;
162721350bcSnicm 
1635d8d7431Snicm 	if (c->flags & CLIENT_SUSPENDED)
1645d8d7431Snicm 		return;
1655d8d7431Snicm 
166d89252e5Snicm 	cmd = options_get_string(c->session->options, "lock-command");
167b70f7e17Snicm 	if (*cmd == '\0' || strlen(cmd) + 1 > MAX_IMSGSIZE - IMSG_HEADER_SIZE)
168168ee6d3Snicm 		return;
169311827fbSnicm 
1701c5425bdSnicm 	tty_stop_tty(&c->tty);
1711c5425bdSnicm 	tty_raw(&c->tty, tty_term_string(c->tty.term, TTYC_SMCUP));
1721c5425bdSnicm 	tty_raw(&c->tty, tty_term_string(c->tty.term, TTYC_CLEAR));
173786c44dcSnicm 	tty_raw(&c->tty, tty_term_string(c->tty.term, TTYC_E3));
174311827fbSnicm 
1751c5425bdSnicm 	c->flags |= CLIENT_SUSPENDED;
176f7772d13Snicm 	proc_send(c->peer, MSG_LOCK, -1, cmd, strlen(cmd) + 1);
177ced36c44Snicm }
178f1d65372Snicm 
179f1d65372Snicm void
180d0ca3a30Snicm server_kill_pane(struct window_pane *wp)
181d0ca3a30Snicm {
182d0ca3a30Snicm 	struct window	*w = wp->window;
183d0ca3a30Snicm 
184d0ca3a30Snicm 	if (window_count_panes(w) == 1) {
185fedaf9c8Snicm 		server_kill_window(w, 1);
186d0ca3a30Snicm 		recalculate_sizes();
187d0ca3a30Snicm 	} else {
188d0ca3a30Snicm 		server_unzoom_window(w);
189249e1654Snicm 		server_client_remove_pane(wp);
190d0ca3a30Snicm 		layout_close_pane(wp);
191d0ca3a30Snicm 		window_remove_pane(w, wp);
192d0ca3a30Snicm 		server_redraw_window(w);
193d0ca3a30Snicm 	}
194d0ca3a30Snicm }
195d0ca3a30Snicm 
196d0ca3a30Snicm void
197fedaf9c8Snicm server_kill_window(struct window *w, int renumber)
198f1d65372Snicm {
199fedaf9c8Snicm 	struct session	*s, *s1;
200f1d65372Snicm 	struct winlink	*wl;
201f1d65372Snicm 
202fedaf9c8Snicm 	RB_FOREACH_SAFE(s, sessions, &sessions, s1) {
20352e9404eSnicm 		if (!session_has(s, w))
204f1d65372Snicm 			continue;
205fedaf9c8Snicm 
20657ca3c78Snicm 		server_unzoom_window(w);
207f6cdf7ccSnicm 		while ((wl = winlink_find_by_window(&s->windows, w)) != NULL) {
208f6cdf7ccSnicm 			if (session_detach(s, wl)) {
20901b2421eSnicm 				server_destroy_session_group(s);
210f6cdf7ccSnicm 				break;
2116a041561Snicm 			}
212d2c17161Snicm 			server_redraw_session_group(s);
213f1d65372Snicm 		}
21453c15224Snicm 
215fedaf9c8Snicm 		if (renumber)
216fedaf9c8Snicm 			server_renumber_session(s);
217fedaf9c8Snicm 	}
218fedaf9c8Snicm 	recalculate_sizes();
219fedaf9c8Snicm }
220fedaf9c8Snicm 
221fedaf9c8Snicm void
222fedaf9c8Snicm server_renumber_session(struct session *s)
223fedaf9c8Snicm {
224fedaf9c8Snicm 	struct session_group	*sg;
225fedaf9c8Snicm 
226d89252e5Snicm 	if (options_get_number(s->options, "renumber-windows")) {
22706440b28Snicm 		if ((sg = session_group_contains(s)) != NULL) {
228fedaf9c8Snicm 			TAILQ_FOREACH(s, &sg->sessions, gentry)
229fedaf9c8Snicm 			    session_renumber_windows(s);
23087d95c92Snicm 		} else
23153c15224Snicm 			session_renumber_windows(s);
23221ced74dSnicm 	}
23387d95c92Snicm }
234fedaf9c8Snicm 
235fedaf9c8Snicm void
236fedaf9c8Snicm server_renumber_all(void)
237fedaf9c8Snicm {
238fedaf9c8Snicm 	struct session	*s;
239fedaf9c8Snicm 
240fedaf9c8Snicm 	RB_FOREACH(s, sessions, &sessions)
241fedaf9c8Snicm 		server_renumber_session(s);
242f6cdf7ccSnicm }
24321ced74dSnicm 
24421ced74dSnicm int
24501b2421eSnicm server_link_window(struct session *src, struct winlink *srcwl,
246bfcd10e2Snicm     struct session *dst, int dstidx, int killflag, int selectflag,
247bfcd10e2Snicm     char **cause)
24821ced74dSnicm {
24921ced74dSnicm 	struct winlink		*dstwl;
25001b2421eSnicm 	struct session_group	*srcsg, *dstsg;
25101b2421eSnicm 
25206440b28Snicm 	srcsg = session_group_contains(src);
25306440b28Snicm 	dstsg = session_group_contains(dst);
25401b2421eSnicm 	if (src != dst && srcsg != NULL && dstsg != NULL && srcsg == dstsg) {
25501b2421eSnicm 		xasprintf(cause, "sessions are grouped");
25601b2421eSnicm 		return (-1);
25701b2421eSnicm 	}
25821ced74dSnicm 
25921ced74dSnicm 	dstwl = NULL;
26021ced74dSnicm 	if (dstidx != -1)
26121ced74dSnicm 		dstwl = winlink_find_by_index(&dst->windows, dstidx);
26221ced74dSnicm 	if (dstwl != NULL) {
263795597afSnicm 		if (dstwl->window == srcwl->window) {
264795597afSnicm 			xasprintf(cause, "same index: %d", dstidx);
26502b834e4Snicm 			return (-1);
266795597afSnicm 		}
26721ced74dSnicm 		if (killflag) {
26821ced74dSnicm 			/*
26921ced74dSnicm 			 * Can't use session_detach as it will destroy session
27021ced74dSnicm 			 * if this makes it empty.
27121ced74dSnicm 			 */
2722ae124feSnicm 			notify_session_window("window-unlinked", dst,
2732ae124feSnicm 			    dstwl->window);
2744336fb18Snicm 			dstwl->flags &= ~WINLINK_ALERTFLAGS;
27521ced74dSnicm 			winlink_stack_remove(&dst->lastw, dstwl);
27621ced74dSnicm 			winlink_remove(&dst->windows, dstwl);
27721ced74dSnicm 
27821ced74dSnicm 			/* Force select/redraw if current. */
279cc715dcaSnicm 			if (dstwl == dst->curw) {
28021ced74dSnicm 				selectflag = 1;
281cc715dcaSnicm 				dst->curw = NULL;
282cc715dcaSnicm 			}
28321ced74dSnicm 		}
28421ced74dSnicm 	}
28521ced74dSnicm 
28621ced74dSnicm 	if (dstidx == -1)
287d89252e5Snicm 		dstidx = -1 - options_get_number(dst->options, "base-index");
28821ced74dSnicm 	dstwl = session_attach(dst, srcwl->window, dstidx, cause);
28921ced74dSnicm 	if (dstwl == NULL)
29021ced74dSnicm 		return (-1);
29121ced74dSnicm 
29201b2421eSnicm 	if (selectflag)
29321ced74dSnicm 		session_select(dst, dstwl->idx);
29401b2421eSnicm 	server_redraw_session_group(dst);
29521ced74dSnicm 
29621ced74dSnicm 	return (0);
29721ced74dSnicm }
29821ced74dSnicm 
29921ced74dSnicm void
30021ced74dSnicm server_unlink_window(struct session *s, struct winlink *wl)
30121ced74dSnicm {
30221ced74dSnicm 	if (session_detach(s, wl))
30301b2421eSnicm 		server_destroy_session_group(s);
30421ced74dSnicm 	else
30501b2421eSnicm 		server_redraw_session_group(s);
30601b2421eSnicm }
30701b2421eSnicm 
30801b2421eSnicm void
3092ae124feSnicm server_destroy_pane(struct window_pane *wp, int notify)
3107c6e570cSnicm {
3117c6e570cSnicm 	struct window		*w = wp->window;
31284604cd7Snicm 	struct screen_write_ctx	 ctx;
31384604cd7Snicm 	struct grid_cell	 gc;
314c93e2546Snicm 	int			 remain_on_exit;
3158d518d48Snicm 	const char		*s;
3168d518d48Snicm 	char			*expanded;
3178d518d48Snicm 	u_int			 sx = screen_size_x(&wp->base);
3188d518d48Snicm 	u_int			 sy = screen_size_y(&wp->base);
3197c6e570cSnicm 
320ca33238aSnicm 	if (wp->fd != -1) {
3217c6e570cSnicm 		bufferevent_free(wp->event);
3228ba21c53Snicm 		wp->event = NULL;
32384045c9aSnicm 		close(wp->fd);
3247c6e570cSnicm 		wp->fd = -1;
325ca33238aSnicm 	}
3267c6e570cSnicm 
327c93e2546Snicm 	remain_on_exit = options_get_number(wp->options, "remain-on-exit");
328c93e2546Snicm 	if (remain_on_exit != 0 && (~wp->flags & PANE_STATUSREADY))
3297c6e570cSnicm 		return;
330c93e2546Snicm 	switch (remain_on_exit) {
331c93e2546Snicm 	case 0:
332c93e2546Snicm 		break;
333c93e2546Snicm 	case 2:
334c93e2546Snicm 		if (WIFEXITED(wp->status) && WEXITSTATUS(wp->status) == 0)
335c93e2546Snicm 			break;
336c93e2546Snicm 		/* FALLTHROUGH */
337c93e2546Snicm 	case 1:
338514fd64dSnicm 		if (wp->flags & PANE_STATUSDRAWN)
339514fd64dSnicm 			return;
340514fd64dSnicm 		wp->flags |= PANE_STATUSDRAWN;
341514fd64dSnicm 
3428d518d48Snicm 		gettimeofday(&wp->dead_time, NULL);
3432ae124feSnicm 		if (notify)
3442ae124feSnicm 			notify_pane("pane-died", wp);
3452ae124feSnicm 
3468d518d48Snicm 		s = options_get_string(wp->options, "remain-on-exit-format");
3478d518d48Snicm 		if (*s != '\0') {
34883e83a91Snicm 			screen_write_start_pane(&ctx, wp, &wp->base);
3498d518d48Snicm 			screen_write_scrollregion(&ctx, 0, sy - 1);
3508d518d48Snicm 			screen_write_cursormove(&ctx, 0, sy - 1, 0);
3516d5c64a0Snicm 			screen_write_linefeed(&ctx, 1, 8);
35284604cd7Snicm 			memcpy(&gc, &grid_default_cell, sizeof gc);
353514fd64dSnicm 
3548d518d48Snicm 			expanded = format_single(NULL, s, NULL, NULL, NULL, wp);
3558d518d48Snicm 			format_draw(&ctx, &gc, sx, expanded, NULL, 0);
3568d518d48Snicm 			free(expanded);
357514fd64dSnicm 
35884604cd7Snicm 			screen_write_stop(&ctx);
3598d518d48Snicm 		}
3608d518d48Snicm 		wp->base.mode &= ~MODE_CURSOR;
3618d518d48Snicm 
36284604cd7Snicm 		wp->flags |= PANE_REDRAW;
36384604cd7Snicm 		return;
36484604cd7Snicm 	}
3657c6e570cSnicm 
3662ae124feSnicm 	if (notify)
3672ae124feSnicm 		notify_pane("pane-exited", wp);
3682ae124feSnicm 
369b4a3311eSnicm 	server_unzoom_window(w);
370249e1654Snicm 	server_client_remove_pane(wp);
3717c6e570cSnicm 	layout_close_pane(wp);
3727c6e570cSnicm 	window_remove_pane(w, wp);
3737c6e570cSnicm 
3747c6e570cSnicm 	if (TAILQ_EMPTY(&w->panes))
375fedaf9c8Snicm 		server_kill_window(w, 1);
3767c6e570cSnicm 	else
3777c6e570cSnicm 		server_redraw_window(w);
3787c6e570cSnicm }
3797c6e570cSnicm 
380ced21769Snicm static void
38101b2421eSnicm server_destroy_session_group(struct session *s)
38201b2421eSnicm {
38301b2421eSnicm 	struct session_group	*sg;
384a8dbd3acSnicm 	struct session		*s1;
38501b2421eSnicm 
3866a041561Snicm 	if ((sg = session_group_contains(s)) == NULL) {
38701b2421eSnicm 		server_destroy_session(s);
3886a041561Snicm 		session_destroy(s, 1, __func__);
3896a041561Snicm 	} else {
390a8dbd3acSnicm 		TAILQ_FOREACH_SAFE(s, &sg->sessions, gentry, s1) {
39101b2421eSnicm 			server_destroy_session(s);
392c26c4f79Snicm 			session_destroy(s, 1, __func__);
393a8dbd3acSnicm 		}
39401b2421eSnicm 	}
395f1d65372Snicm }
396669c539cSnicm 
3979883b791Snicm static struct session *
3986a041561Snicm server_find_session(struct session *s,
3996a041561Snicm     int (*f)(struct session *, struct session *))
400abc89c60Snicm {
401579115bdSnicm 	struct session *s_loop, *s_out = NULL;
402abc89c60Snicm 
40359996dc3Snicm 	RB_FOREACH(s_loop, sessions, &sessions) {
4046a041561Snicm 		if (s_loop != s && (s_out == NULL || f(s_loop, s_out)))
405abc89c60Snicm 			s_out = s_loop;
406abc89c60Snicm 	}
407abc89c60Snicm 	return (s_out);
408abc89c60Snicm }
409abc89c60Snicm 
4106a041561Snicm static int
4116a041561Snicm server_newer_session(struct session *s_loop, struct session *s_out)
412579115bdSnicm {
4135d6bcbf0Snicm 	return (timercmp(&s_loop->activity_time, &s_out->activity_time, >));
414579115bdSnicm }
4156a041561Snicm 
4166a041561Snicm static int
4176a041561Snicm server_newer_detached_session(struct session *s_loop, struct session *s_out)
4186a041561Snicm {
4196a041561Snicm 	if (s_loop->attached)
4206a041561Snicm 		return (0);
4216a041561Snicm 	return (server_newer_session(s_loop, s_out));
422579115bdSnicm }
423579115bdSnicm 
424669c539cSnicm void
4252d9a0fc7Snicm server_destroy_session(struct session *s)
4262d9a0fc7Snicm {
4272d9a0fc7Snicm 	struct client	*c;
428*104e139dSnicm 	struct session	*s_new = NULL, *cs_new, *use_s;
429579115bdSnicm 	int		 detach_on_destroy;
4302d9a0fc7Snicm 
431579115bdSnicm 	detach_on_destroy = options_get_number(s->options, "detach-on-destroy");
432579115bdSnicm 	if (detach_on_destroy == 0)
4336a041561Snicm 		s_new = server_find_session(s, server_newer_session);
434579115bdSnicm 	else if (detach_on_destroy == 2)
4356a041561Snicm 		s_new = server_find_session(s, server_newer_detached_session);
4366a041561Snicm 	else if (detach_on_destroy == 3)
4376a041561Snicm 		s_new = session_previous_session(s);
4386a041561Snicm 	else if (detach_on_destroy == 4)
4396a041561Snicm 		s_new = session_next_session(s);
440*104e139dSnicm 
441*104e139dSnicm 	/*
442*104e139dSnicm 	 * If no suitable new session was found above, then look for any
443*104e139dSnicm 	 * session as an alternative in case a client needs it.
444*104e139dSnicm 	 */
445*104e139dSnicm 	if (s_new == NULL &&
446*104e139dSnicm 	    (detach_on_destroy == 1 || detach_on_destroy == 2))
447*104e139dSnicm 		cs_new = server_find_session(s, server_newer_session);
448*104e139dSnicm 
44982873134Snicm 	TAILQ_FOREACH(c, &clients, entry) {
45082873134Snicm 		if (c->session != s)
4512d9a0fc7Snicm 			continue;
452*104e139dSnicm 		use_s = s_new;
453*104e139dSnicm 		if (use_s == NULL && (c->flags & CLIENT_NO_DETACH_ON_DESTROY))
454*104e139dSnicm 			use_s = cs_new;
455*104e139dSnicm 
4566a041561Snicm 		c->session = NULL;
4576a041561Snicm 		c->last_session = NULL;
458*104e139dSnicm 		server_client_set_session(c, use_s);
459*104e139dSnicm 		if (use_s == NULL)
460636a2c4aSnicm 			c->flags |= CLIENT_EXIT;
4612d9a0fc7Snicm 	}
462f61b6cb0Snicm 	recalculate_sizes();
4632d9a0fc7Snicm }
4642d9a0fc7Snicm 
4652d9a0fc7Snicm void
46635776674Snicm server_check_unattached(void)
46735776674Snicm {
46835776674Snicm 	struct session		*s;
4699461a728Snicm 	struct session_group	*sg;
47035776674Snicm 
47135776674Snicm 	/*
47235776674Snicm 	 * If any sessions are no longer attached and have destroy-unattached
47335776674Snicm 	 * set, collect them.
47435776674Snicm 	 */
47559996dc3Snicm 	RB_FOREACH(s, sessions, &sessions) {
476647c5c18Snicm 		if (s->attached != 0)
47735776674Snicm 			continue;
4789461a728Snicm 		switch (options_get_number(s->options, "destroy-unattached")) {
4799461a728Snicm 		case 0: /* off */
4809461a728Snicm 			continue;
4819461a728Snicm 		case 1: /* on */
4829461a728Snicm 			break;
4839461a728Snicm 		case 2: /* keep-last */
4849461a728Snicm 			sg = session_group_contains(s);
4859461a728Snicm 			if (sg == NULL || session_group_count(sg) <= 1)
4869461a728Snicm 				continue;
4879461a728Snicm 			break;
4889461a728Snicm 		case 3: /* keep-group */
4899461a728Snicm 			sg = session_group_contains(s);
4909461a728Snicm 			if (sg != NULL && session_group_count(sg) == 1)
4919461a728Snicm 				continue;
4929461a728Snicm 			break;
4939461a728Snicm 		}
494c26c4f79Snicm 		session_destroy(s, 1, __func__);
49535776674Snicm 	}
49635776674Snicm }
49735776674Snicm 
498b4a3311eSnicm void
499b4a3311eSnicm server_unzoom_window(struct window *w)
500b4a3311eSnicm {
5010ad0daf4Snicm 	if (window_unzoom(w, 1) == 0)
502b4a3311eSnicm 		server_redraw_window(w);
503e048bb79Snicm }
504