Merge branch 'obsd-master'
[tmux.git] / server-fn.c
blob60d4f16c27e1e3d3da1a7ff79a4dc20457b54366
1 /* $OpenBSD$ */
3 /*
4 * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 #include <sys/types.h>
20 #include <sys/wait.h>
21 #include <sys/uio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <time.h>
26 #include <unistd.h>
28 #include "tmux.h"
30 static void server_destroy_session_group(struct session *);
32 void
33 server_redraw_client(struct client *c)
35 c->flags |= CLIENT_ALLREDRAWFLAGS;
38 void
39 server_status_client(struct client *c)
41 c->flags |= CLIENT_REDRAWSTATUS;
44 void
45 server_redraw_session(struct session *s)
47 struct client *c;
49 TAILQ_FOREACH(c, &clients, entry) {
50 if (c->session == s)
51 server_redraw_client(c);
55 void
56 server_redraw_session_group(struct session *s)
58 struct session_group *sg;
60 if ((sg = session_group_contains(s)) == NULL)
61 server_redraw_session(s);
62 else {
63 TAILQ_FOREACH(s, &sg->sessions, gentry)
64 server_redraw_session(s);
68 void
69 server_status_session(struct session *s)
71 struct client *c;
73 TAILQ_FOREACH(c, &clients, entry) {
74 if (c->session == s)
75 server_status_client(c);
79 void
80 server_status_session_group(struct session *s)
82 struct session_group *sg;
84 if ((sg = session_group_contains(s)) == NULL)
85 server_status_session(s);
86 else {
87 TAILQ_FOREACH(s, &sg->sessions, gentry)
88 server_status_session(s);
92 void
93 server_redraw_window(struct window *w)
95 struct client *c;
97 TAILQ_FOREACH(c, &clients, entry) {
98 if (c->session != NULL && c->session->curw->window == w)
99 server_redraw_client(c);
103 void
104 server_redraw_window_borders(struct window *w)
106 struct client *c;
108 TAILQ_FOREACH(c, &clients, entry) {
109 if (c->session != NULL && c->session->curw->window == w)
110 c->flags |= CLIENT_REDRAWBORDERS;
114 void
115 server_status_window(struct window *w)
117 struct session *s;
120 * This is slightly different. We want to redraw the status line of any
121 * clients containing this window rather than anywhere it is the
122 * current window.
125 RB_FOREACH(s, sessions, &sessions) {
126 if (session_has(s, w))
127 server_status_session(s);
131 void
132 server_lock(void)
134 struct client *c;
136 TAILQ_FOREACH(c, &clients, entry) {
137 if (c->session != NULL)
138 server_lock_client(c);
142 void
143 server_lock_session(struct session *s)
145 struct client *c;
147 TAILQ_FOREACH(c, &clients, entry) {
148 if (c->session == s)
149 server_lock_client(c);
153 void
154 server_lock_client(struct client *c)
156 const char *cmd;
158 if (c->flags & CLIENT_CONTROL)
159 return;
161 if (c->flags & CLIENT_SUSPENDED)
162 return;
164 cmd = options_get_string(c->session->options, "lock-command");
165 if (*cmd == '\0' || strlen(cmd) + 1 > MAX_IMSGSIZE - IMSG_HEADER_SIZE)
166 return;
168 tty_stop_tty(&c->tty);
169 tty_raw(&c->tty, tty_term_string(c->tty.term, TTYC_SMCUP));
170 tty_raw(&c->tty, tty_term_string(c->tty.term, TTYC_CLEAR));
171 tty_raw(&c->tty, tty_term_string(c->tty.term, TTYC_E3));
173 c->flags |= CLIENT_SUSPENDED;
174 proc_send(c->peer, MSG_LOCK, -1, cmd, strlen(cmd) + 1);
177 void
178 server_kill_pane(struct window_pane *wp)
180 struct window *w = wp->window;
182 if (window_count_panes(w) == 1) {
183 server_kill_window(w, 1);
184 recalculate_sizes();
185 } else {
186 server_unzoom_window(w);
187 server_client_remove_pane(wp);
188 layout_close_pane(wp);
189 window_remove_pane(w, wp);
190 server_redraw_window(w);
194 void
195 server_kill_window(struct window *w, int renumber)
197 struct session *s, *s1;
198 struct winlink *wl;
200 RB_FOREACH_SAFE(s, sessions, &sessions, s1) {
201 if (!session_has(s, w))
202 continue;
204 server_unzoom_window(w);
205 while ((wl = winlink_find_by_window(&s->windows, w)) != NULL) {
206 if (session_detach(s, wl)) {
207 server_destroy_session_group(s);
208 break;
210 server_redraw_session_group(s);
213 if (renumber)
214 server_renumber_session(s);
216 recalculate_sizes();
219 void
220 server_renumber_session(struct session *s)
222 struct session_group *sg;
224 if (options_get_number(s->options, "renumber-windows")) {
225 if ((sg = session_group_contains(s)) != NULL) {
226 TAILQ_FOREACH(s, &sg->sessions, gentry)
227 session_renumber_windows(s);
228 } else
229 session_renumber_windows(s);
233 void
234 server_renumber_all(void)
236 struct session *s;
238 RB_FOREACH(s, sessions, &sessions)
239 server_renumber_session(s);
243 server_link_window(struct session *src, struct winlink *srcwl,
244 struct session *dst, int dstidx, int killflag, int selectflag,
245 char **cause)
247 struct winlink *dstwl;
248 struct session_group *srcsg, *dstsg;
250 srcsg = session_group_contains(src);
251 dstsg = session_group_contains(dst);
252 if (src != dst && srcsg != NULL && dstsg != NULL && srcsg == dstsg) {
253 xasprintf(cause, "sessions are grouped");
254 return (-1);
257 dstwl = NULL;
258 if (dstidx != -1)
259 dstwl = winlink_find_by_index(&dst->windows, dstidx);
260 if (dstwl != NULL) {
261 if (dstwl->window == srcwl->window) {
262 xasprintf(cause, "same index: %d", dstidx);
263 return (-1);
265 if (killflag) {
267 * Can't use session_detach as it will destroy session
268 * if this makes it empty.
270 notify_session_window("window-unlinked", dst,
271 dstwl->window);
272 dstwl->flags &= ~WINLINK_ALERTFLAGS;
273 winlink_stack_remove(&dst->lastw, dstwl);
274 winlink_remove(&dst->windows, dstwl);
276 /* Force select/redraw if current. */
277 if (dstwl == dst->curw) {
278 selectflag = 1;
279 dst->curw = NULL;
284 if (dstidx == -1)
285 dstidx = -1 - options_get_number(dst->options, "base-index");
286 dstwl = session_attach(dst, srcwl->window, dstidx, cause);
287 if (dstwl == NULL)
288 return (-1);
290 if (selectflag)
291 session_select(dst, dstwl->idx);
292 server_redraw_session_group(dst);
294 return (0);
297 void
298 server_unlink_window(struct session *s, struct winlink *wl)
300 if (session_detach(s, wl))
301 server_destroy_session_group(s);
302 else
303 server_redraw_session_group(s);
306 void
307 server_destroy_pane(struct window_pane *wp, int notify)
309 struct window *w = wp->window;
310 struct screen_write_ctx ctx;
311 struct grid_cell gc;
312 int remain_on_exit;
313 const char *s;
314 char *expanded;
315 u_int sx = screen_size_x(&wp->base);
316 u_int sy = screen_size_y(&wp->base);
318 if (wp->fd != -1) {
319 #ifdef HAVE_UTEMPTER
320 utempter_remove_record(wp->fd);
321 #endif
322 bufferevent_free(wp->event);
323 wp->event = NULL;
324 close(wp->fd);
325 wp->fd = -1;
328 remain_on_exit = options_get_number(wp->options, "remain-on-exit");
329 if (remain_on_exit != 0 && (~wp->flags & PANE_STATUSREADY))
330 return;
331 switch (remain_on_exit) {
332 case 0:
333 break;
334 case 2:
335 if (WIFEXITED(wp->status) && WEXITSTATUS(wp->status) == 0)
336 break;
337 /* FALLTHROUGH */
338 case 1:
339 if (wp->flags & PANE_STATUSDRAWN)
340 return;
341 wp->flags |= PANE_STATUSDRAWN;
343 gettimeofday(&wp->dead_time, NULL);
344 if (notify)
345 notify_pane("pane-died", wp);
347 s = options_get_string(wp->options, "remain-on-exit-format");
348 if (*s != '\0') {
349 screen_write_start_pane(&ctx, wp, &wp->base);
350 screen_write_scrollregion(&ctx, 0, sy - 1);
351 screen_write_cursormove(&ctx, 0, sy - 1, 0);
352 screen_write_linefeed(&ctx, 1, 8);
353 memcpy(&gc, &grid_default_cell, sizeof gc);
355 expanded = format_single(NULL, s, NULL, NULL, NULL, wp);
356 format_draw(&ctx, &gc, sx, expanded, NULL, 0);
357 free(expanded);
359 screen_write_stop(&ctx);
361 wp->base.mode &= ~MODE_CURSOR;
363 wp->flags |= PANE_REDRAW;
364 return;
367 if (notify)
368 notify_pane("pane-exited", wp);
370 server_unzoom_window(w);
371 server_client_remove_pane(wp);
372 layout_close_pane(wp);
373 window_remove_pane(w, wp);
375 if (TAILQ_EMPTY(&w->panes))
376 server_kill_window(w, 1);
377 else
378 server_redraw_window(w);
381 static void
382 server_destroy_session_group(struct session *s)
384 struct session_group *sg;
385 struct session *s1;
387 if ((sg = session_group_contains(s)) == NULL) {
388 server_destroy_session(s);
389 session_destroy(s, 1, __func__);
390 } else {
391 TAILQ_FOREACH_SAFE(s, &sg->sessions, gentry, s1) {
392 server_destroy_session(s);
393 session_destroy(s, 1, __func__);
398 static struct session *
399 server_find_session(struct session *s,
400 int (*f)(struct session *, struct session *))
402 struct session *s_loop, *s_out = NULL;
404 RB_FOREACH(s_loop, sessions, &sessions) {
405 if (s_loop != s && (s_out == NULL || f(s_loop, s_out)))
406 s_out = s_loop;
408 return (s_out);
411 static int
412 server_newer_session(struct session *s_loop, struct session *s_out)
414 return (timercmp(&s_loop->activity_time, &s_out->activity_time, >));
417 static int
418 server_newer_detached_session(struct session *s_loop, struct session *s_out)
420 if (s_loop->attached)
421 return (0);
422 return (server_newer_session(s_loop, s_out));
425 void
426 server_destroy_session(struct session *s)
428 struct client *c;
429 struct session *s_new = NULL, *cs_new, *use_s;
430 int detach_on_destroy;
432 detach_on_destroy = options_get_number(s->options, "detach-on-destroy");
433 if (detach_on_destroy == 0)
434 s_new = server_find_session(s, server_newer_session);
435 else if (detach_on_destroy == 2)
436 s_new = server_find_session(s, server_newer_detached_session);
437 else if (detach_on_destroy == 3)
438 s_new = session_previous_session(s);
439 else if (detach_on_destroy == 4)
440 s_new = session_next_session(s);
443 * If no suitable new session was found above, then look for any
444 * session as an alternative in case a client needs it.
446 if (s_new == NULL &&
447 (detach_on_destroy == 1 || detach_on_destroy == 2))
448 cs_new = server_find_session(s, server_newer_session);
450 TAILQ_FOREACH(c, &clients, entry) {
451 if (c->session != s)
452 continue;
453 use_s = s_new;
454 if (use_s == NULL && (c->flags & CLIENT_NO_DETACH_ON_DESTROY))
455 use_s = cs_new;
457 c->session = NULL;
458 c->last_session = NULL;
459 server_client_set_session(c, use_s);
460 if (use_s == NULL)
461 c->flags |= CLIENT_EXIT;
463 recalculate_sizes();
466 void
467 server_check_unattached(void)
469 struct session *s;
470 struct session_group *sg;
473 * If any sessions are no longer attached and have destroy-unattached
474 * set, collect them.
476 RB_FOREACH(s, sessions, &sessions) {
477 if (s->attached != 0)
478 continue;
479 switch (options_get_number(s->options, "destroy-unattached")) {
480 case 0: /* off */
481 continue;
482 case 1: /* on */
483 break;
484 case 2: /* keep-last */
485 sg = session_group_contains(s);
486 if (sg == NULL || session_group_count(sg) <= 1)
487 continue;
488 break;
489 case 3: /* keep-group */
490 sg = session_group_contains(s);
491 if (sg != NULL && session_group_count(sg) == 1)
492 continue;
493 break;
495 session_destroy(s, 1, __func__);
499 void
500 server_unzoom_window(struct window *w)
502 if (window_unzoom(w, 1) == 0)
503 server_redraw_window(w);