Make recalculate_sizes() handle an empty window with no active
[tmux-openbsd.git] / server-fn.c
blobefb95acd5a3d13d42cbb8e4aa53815999625b93b
1 /* $OpenBSD$ */
3 /*
4 * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
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>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <time.h>
24 #include <unistd.h>
26 #include "tmux.h"
28 struct session *server_next_session(struct session *);
29 void server_callback_identify(int, short, void *);
31 void
32 server_fill_environ(struct session *s, struct environ *env)
34 char var[MAXPATHLEN], *term;
35 u_int idx;
36 long pid;
38 if (s != NULL) {
39 term = options_get_string(&s->options, "default-terminal");
40 environ_set(env, "TERM", term);
42 idx = s->id;
43 } else
44 idx = -1;
45 pid = getpid();
46 xsnprintf(var, sizeof var, "%s,%ld,%d", socket_path, pid, idx);
47 environ_set(env, "TMUX", var);
50 void
51 server_write_ready(struct client *c)
53 if (c->flags & CLIENT_CONTROL)
54 return;
55 server_write_client(c, MSG_READY, NULL, 0);
58 int
59 server_write_client(
60 struct client *c, enum msgtype type, const void *buf, size_t len)
62 struct imsgbuf *ibuf = &c->ibuf;
63 int error;
65 if (c->flags & CLIENT_BAD)
66 return (-1);
67 log_debug("writing %d to client %d", type, c->ibuf.fd);
68 error = imsg_compose(ibuf, type, PROTOCOL_VERSION, -1, -1,
69 (void *) buf, len);
70 if (error == 1)
71 server_update_event(c);
72 return (error == 1 ? 0 : -1);
75 void
76 server_write_session(
77 struct session *s, enum msgtype type, const void *buf, size_t len)
79 struct client *c;
80 u_int i;
82 for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
83 c = ARRAY_ITEM(&clients, i);
84 if (c == NULL || c->session == NULL)
85 continue;
86 if (c->session == s)
87 server_write_client(c, type, buf, len);
91 void
92 server_redraw_client(struct client *c)
94 c->flags |= CLIENT_REDRAW;
97 void
98 server_status_client(struct client *c)
100 c->flags |= CLIENT_STATUS;
103 void
104 server_redraw_session(struct session *s)
106 struct client *c;
107 u_int i;
109 for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
110 c = ARRAY_ITEM(&clients, i);
111 if (c == NULL || c->session == NULL)
112 continue;
113 if (c->session == s)
114 server_redraw_client(c);
118 void
119 server_redraw_session_group(struct session *s)
121 struct session_group *sg;
123 if ((sg = session_group_find(s)) == NULL)
124 server_redraw_session(s);
125 else {
126 TAILQ_FOREACH(s, &sg->sessions, gentry)
127 server_redraw_session(s);
131 void
132 server_status_session(struct session *s)
134 struct client *c;
135 u_int i;
137 for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
138 c = ARRAY_ITEM(&clients, i);
139 if (c == NULL || c->session == NULL)
140 continue;
141 if (c->session == s)
142 server_status_client(c);
146 void
147 server_status_session_group(struct session *s)
149 struct session_group *sg;
151 if ((sg = session_group_find(s)) == NULL)
152 server_status_session(s);
153 else {
154 TAILQ_FOREACH(s, &sg->sessions, gentry)
155 server_status_session(s);
159 void
160 server_redraw_window(struct window *w)
162 struct client *c;
163 u_int i;
165 for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
166 c = ARRAY_ITEM(&clients, i);
167 if (c == NULL || c->session == NULL)
168 continue;
169 if (c->session->curw->window == w)
170 server_redraw_client(c);
172 w->flags |= WINDOW_REDRAW;
175 void
176 server_redraw_window_borders(struct window *w)
178 struct client *c;
179 u_int i;
181 for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
182 c = ARRAY_ITEM(&clients, i);
183 if (c == NULL || c->session == NULL)
184 continue;
185 if (c->session->curw->window == w)
186 c->flags |= CLIENT_BORDERS;
190 void
191 server_status_window(struct window *w)
193 struct session *s;
196 * This is slightly different. We want to redraw the status line of any
197 * clients containing this window rather than anywhere it is the
198 * current window.
201 RB_FOREACH(s, sessions, &sessions) {
202 if (session_has(s, w) != NULL)
203 server_status_session(s);
207 void
208 server_lock(void)
210 struct client *c;
211 u_int i;
213 for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
214 c = ARRAY_ITEM(&clients, i);
215 if (c == NULL || c->session == NULL)
216 continue;
217 server_lock_client(c);
221 void
222 server_lock_session(struct session *s)
224 struct client *c;
225 u_int i;
227 for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
228 c = ARRAY_ITEM(&clients, i);
229 if (c == NULL || c->session == NULL || c->session != s)
230 continue;
231 server_lock_client(c);
235 void
236 server_lock_client(struct client *c)
238 const char *cmd;
239 size_t cmdlen;
240 struct msg_lock_data lockdata;
242 if (c->flags & CLIENT_CONTROL)
243 return;
245 if (c->flags & CLIENT_SUSPENDED)
246 return;
248 cmd = options_get_string(&c->session->options, "lock-command");
249 cmdlen = strlcpy(lockdata.cmd, cmd, sizeof lockdata.cmd);
250 if (cmdlen >= sizeof lockdata.cmd)
251 return;
253 tty_stop_tty(&c->tty);
254 tty_raw(&c->tty, tty_term_string(c->tty.term, TTYC_SMCUP));
255 tty_raw(&c->tty, tty_term_string(c->tty.term, TTYC_CLEAR));
256 tty_raw(&c->tty, tty_term_string(c->tty.term, TTYC_E3));
258 c->flags |= CLIENT_SUSPENDED;
259 server_write_client(c, MSG_LOCK, &lockdata, sizeof lockdata);
262 void
263 server_kill_window(struct window *w)
265 struct session *s, *next_s, *target_s;
266 struct session_group *sg;
267 struct winlink *wl;
269 next_s = RB_MIN(sessions, &sessions);
270 while (next_s != NULL) {
271 s = next_s;
272 next_s = RB_NEXT(sessions, &sessions, s);
274 if (session_has(s, w) == NULL)
275 continue;
276 while ((wl = winlink_find_by_window(&s->windows, w)) != NULL) {
277 if (session_detach(s, wl)) {
278 server_destroy_session_group(s);
279 break;
280 } else
281 server_redraw_session_group(s);
284 if (options_get_number(&s->options, "renumber-windows")) {
285 if ((sg = session_group_find(s)) != NULL) {
286 TAILQ_FOREACH(target_s, &sg->sessions, gentry)
287 session_renumber_windows(target_s);
288 } else
289 session_renumber_windows(s);
292 recalculate_sizes();
296 server_link_window(struct session *src, struct winlink *srcwl,
297 struct session *dst, int dstidx, int killflag, int selectflag, char **cause)
299 struct winlink *dstwl;
300 struct session_group *srcsg, *dstsg;
302 srcsg = session_group_find(src);
303 dstsg = session_group_find(dst);
304 if (src != dst && srcsg != NULL && dstsg != NULL && srcsg == dstsg) {
305 xasprintf(cause, "sessions are grouped");
306 return (-1);
309 dstwl = NULL;
310 if (dstidx != -1)
311 dstwl = winlink_find_by_index(&dst->windows, dstidx);
312 if (dstwl != NULL) {
313 if (dstwl->window == srcwl->window) {
314 xasprintf(cause, "same index: %d", dstidx);
315 return (-1);
317 if (killflag) {
319 * Can't use session_detach as it will destroy session
320 * if this makes it empty.
322 notify_window_unlinked(dst, dstwl->window);
323 dstwl->flags &= ~WINLINK_ALERTFLAGS;
324 winlink_stack_remove(&dst->lastw, dstwl);
325 winlink_remove(&dst->windows, dstwl);
327 /* Force select/redraw if current. */
328 if (dstwl == dst->curw) {
329 selectflag = 1;
330 dst->curw = NULL;
335 if (dstidx == -1)
336 dstidx = -1 - options_get_number(&dst->options, "base-index");
337 dstwl = session_attach(dst, srcwl->window, dstidx, cause);
338 if (dstwl == NULL)
339 return (-1);
341 if (selectflag)
342 session_select(dst, dstwl->idx);
343 server_redraw_session_group(dst);
345 return (0);
348 void
349 server_unlink_window(struct session *s, struct winlink *wl)
351 if (session_detach(s, wl))
352 server_destroy_session_group(s);
353 else
354 server_redraw_session_group(s);
357 void
358 server_destroy_pane(struct window_pane *wp)
360 struct window *w = wp->window;
361 int old_fd;
362 struct screen_write_ctx ctx;
363 struct grid_cell gc;
365 old_fd = wp->fd;
366 if (wp->fd != -1) {
367 bufferevent_free(wp->event);
368 close(wp->fd);
369 wp->fd = -1;
372 if (options_get_number(&w->options, "remain-on-exit")) {
373 if (old_fd == -1)
374 return;
375 screen_write_start(&ctx, wp, &wp->base);
376 screen_write_scrollregion(&ctx, 0, screen_size_y(ctx.s) - 1);
377 screen_write_cursormove(&ctx, 0, screen_size_y(ctx.s) - 1);
378 screen_write_linefeed(&ctx, 1);
379 memcpy(&gc, &grid_default_cell, sizeof gc);
380 gc.attr |= GRID_ATTR_BRIGHT;
381 screen_write_puts(&ctx, &gc, "Pane is dead");
382 screen_write_stop(&ctx);
383 wp->flags |= PANE_REDRAW;
384 return;
387 server_unzoom_window(w);
388 layout_close_pane(wp);
389 window_remove_pane(w, wp);
391 if (TAILQ_EMPTY(&w->panes))
392 server_kill_window(w);
393 else
394 server_redraw_window(w);
397 void
398 server_destroy_session_group(struct session *s)
400 struct session_group *sg;
402 if ((sg = session_group_find(s)) == NULL)
403 server_destroy_session(s);
404 else {
405 TAILQ_FOREACH(s, &sg->sessions, gentry)
406 server_destroy_session(s);
407 TAILQ_REMOVE(&session_groups, sg, entry);
408 free(sg);
412 struct session *
413 server_next_session(struct session *s)
415 struct session *s_loop, *s_out;
417 s_out = NULL;
418 RB_FOREACH(s_loop, sessions, &sessions) {
419 if (s_loop == s)
420 continue;
421 if (s_out == NULL ||
422 timercmp(&s_loop->activity_time, &s_out->activity_time, <))
423 s_out = s_loop;
425 return (s_out);
428 void
429 server_destroy_session(struct session *s)
431 struct client *c;
432 struct session *s_new;
433 u_int i;
435 if (!options_get_number(&s->options, "detach-on-destroy"))
436 s_new = server_next_session(s);
437 else
438 s_new = NULL;
440 for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
441 c = ARRAY_ITEM(&clients, i);
442 if (c == NULL || c->session != s)
443 continue;
444 if (s_new == NULL) {
445 c->session = NULL;
446 c->flags |= CLIENT_EXIT;
447 } else {
448 c->last_session = NULL;
449 c->session = s_new;
450 notify_attached_session_changed(c);
451 session_update_activity(s_new);
452 server_redraw_client(c);
455 recalculate_sizes();
458 void
459 server_check_unattached (void)
461 struct session *s;
464 * If any sessions are no longer attached and have destroy-unattached
465 * set, collect them.
467 RB_FOREACH(s, sessions, &sessions) {
468 if (!(s->flags & SESSION_UNATTACHED))
469 continue;
470 if (options_get_number (&s->options, "destroy-unattached"))
471 session_destroy(s);
475 void
476 server_set_identify(struct client *c)
478 struct timeval tv;
479 int delay;
481 delay = options_get_number(&c->session->options, "display-panes-time");
482 tv.tv_sec = delay / 1000;
483 tv.tv_usec = (delay % 1000) * 1000L;
485 if (event_initialized (&c->identify_timer))
486 evtimer_del(&c->identify_timer);
487 evtimer_set(&c->identify_timer, server_callback_identify, c);
488 evtimer_add(&c->identify_timer, &tv);
490 c->flags |= CLIENT_IDENTIFY;
491 c->tty.flags |= (TTY_FREEZE|TTY_NOCURSOR);
492 server_redraw_client(c);
495 void
496 server_clear_identify(struct client *c)
498 if (c->flags & CLIENT_IDENTIFY) {
499 c->flags &= ~CLIENT_IDENTIFY;
500 c->tty.flags &= ~(TTY_FREEZE|TTY_NOCURSOR);
501 server_redraw_client(c);
505 void
506 server_callback_identify(unused int fd, unused short events, void *data)
508 struct client *c = data;
510 server_clear_identify(c);
513 void
514 server_update_event(struct client *c)
516 short events;
518 events = 0;
519 if (!(c->flags & CLIENT_BAD))
520 events |= EV_READ;
521 if (c->ibuf.w.queued > 0)
522 events |= EV_WRITE;
523 if (event_initialized(&c->event))
524 event_del(&c->event);
525 event_set(&c->event, c->ibuf.fd, events, server_client_callback, c);
526 event_add(&c->event, NULL);
529 /* Push stdout to client if possible. */
530 void
531 server_push_stdout(struct client *c)
533 struct msg_stdout_data data;
534 size_t size;
536 size = EVBUFFER_LENGTH(c->stdout_data);
537 if (size == 0)
538 return;
539 if (size > sizeof data.data)
540 size = sizeof data.data;
542 memcpy(data.data, EVBUFFER_DATA(c->stdout_data), size);
543 data.size = size;
545 if (server_write_client(c, MSG_STDOUT, &data, sizeof data) == 0)
546 evbuffer_drain(c->stdout_data, size);
549 /* Push stderr to client if possible. */
550 void
551 server_push_stderr(struct client *c)
553 struct msg_stderr_data data;
554 size_t size;
556 if (c->stderr_data == c->stdout_data) {
557 server_push_stdout(c);
558 return;
560 size = EVBUFFER_LENGTH(c->stderr_data);
561 if (size == 0)
562 return;
563 if (size > sizeof data.data)
564 size = sizeof data.data;
566 memcpy(data.data, EVBUFFER_DATA(c->stderr_data), size);
567 data.size = size;
569 if (server_write_client(c, MSG_STDERR, &data, sizeof data) == 0)
570 evbuffer_drain(c->stderr_data, size);
573 /* Set stdin callback. */
575 server_set_stdin_callback(struct client *c, void (*cb)(struct client *, int,
576 void *), void *cb_data, char **cause)
578 if (c == NULL || c->session != NULL) {
579 *cause = xstrdup("no client with stdin");
580 return (-1);
582 if (c->flags & CLIENT_TERMINAL) {
583 *cause = xstrdup("stdin is a tty");
584 return (-1);
586 if (c->stdin_callback != NULL) {
587 *cause = xstrdup("stdin in use");
588 return (-1);
591 c->stdin_callback_data = cb_data;
592 c->stdin_callback = cb;
594 c->references++;
596 if (c->stdin_closed)
597 c->stdin_callback (c, 1, c->stdin_callback_data);
599 server_write_client(c, MSG_STDIN, NULL, 0);
601 return (0);
604 void
605 server_unzoom_window(struct window *w)
607 window_unzoom(w);
608 server_redraw_window(w);
609 server_status_window(w);