Annotation of embedaddon/tmux/server-fn.c, revision 1.1
1.1 ! misho 1: /* $OpenBSD$ */
! 2:
! 3: /*
! 4: * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
! 5: *
! 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.
! 9: *
! 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.
! 17: */
! 18:
! 19: #include <sys/types.h>
! 20: #include <sys/uio.h>
! 21:
! 22: #include <stdlib.h>
! 23: #include <string.h>
! 24: #include <time.h>
! 25: #include <unistd.h>
! 26:
! 27: #include "tmux.h"
! 28:
! 29: static struct session *server_next_session(struct session *);
! 30: static void server_destroy_session_group(struct session *);
! 31:
! 32: void
! 33: server_redraw_client(struct client *c)
! 34: {
! 35: c->flags |= CLIENT_REDRAW;
! 36: }
! 37:
! 38: void
! 39: server_status_client(struct client *c)
! 40: {
! 41: c->flags |= CLIENT_STATUS;
! 42: }
! 43:
! 44: void
! 45: server_redraw_session(struct session *s)
! 46: {
! 47: struct client *c;
! 48:
! 49: TAILQ_FOREACH(c, &clients, entry) {
! 50: if (c->session == s)
! 51: server_redraw_client(c);
! 52: }
! 53: }
! 54:
! 55: void
! 56: server_redraw_session_group(struct session *s)
! 57: {
! 58: struct session_group *sg;
! 59:
! 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);
! 65: }
! 66: }
! 67:
! 68: void
! 69: server_status_session(struct session *s)
! 70: {
! 71: struct client *c;
! 72:
! 73: TAILQ_FOREACH(c, &clients, entry) {
! 74: if (c->session == s)
! 75: server_status_client(c);
! 76: }
! 77: }
! 78:
! 79: void
! 80: server_status_session_group(struct session *s)
! 81: {
! 82: struct session_group *sg;
! 83:
! 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);
! 89: }
! 90: }
! 91:
! 92: void
! 93: server_redraw_window(struct window *w)
! 94: {
! 95: struct client *c;
! 96:
! 97: TAILQ_FOREACH(c, &clients, entry) {
! 98: if (c->session != NULL && c->session->curw->window == w)
! 99: server_redraw_client(c);
! 100: }
! 101: }
! 102:
! 103: void
! 104: server_redraw_window_borders(struct window *w)
! 105: {
! 106: struct client *c;
! 107:
! 108: TAILQ_FOREACH(c, &clients, entry) {
! 109: if (c->session != NULL && c->session->curw->window == w)
! 110: c->flags |= CLIENT_BORDERS;
! 111: }
! 112: }
! 113:
! 114: void
! 115: server_status_window(struct window *w)
! 116: {
! 117: struct session *s;
! 118:
! 119: /*
! 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.
! 123: */
! 124:
! 125: RB_FOREACH(s, sessions, &sessions) {
! 126: if (session_has(s, w))
! 127: server_status_session(s);
! 128: }
! 129: }
! 130:
! 131: void
! 132: server_lock(void)
! 133: {
! 134: struct client *c;
! 135:
! 136: TAILQ_FOREACH(c, &clients, entry) {
! 137: if (c->session != NULL)
! 138: server_lock_client(c);
! 139: }
! 140: }
! 141:
! 142: void
! 143: server_lock_session(struct session *s)
! 144: {
! 145: struct client *c;
! 146:
! 147: TAILQ_FOREACH(c, &clients, entry) {
! 148: if (c->session == s)
! 149: server_lock_client(c);
! 150: }
! 151: }
! 152:
! 153: void
! 154: server_lock_client(struct client *c)
! 155: {
! 156: const char *cmd;
! 157:
! 158: if (c->flags & CLIENT_CONTROL)
! 159: return;
! 160:
! 161: if (c->flags & CLIENT_SUSPENDED)
! 162: return;
! 163:
! 164: cmd = options_get_string(c->session->options, "lock-command");
! 165: if (strlen(cmd) + 1 > MAX_IMSGSIZE - IMSG_HEADER_SIZE)
! 166: return;
! 167:
! 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));
! 172:
! 173: c->flags |= CLIENT_SUSPENDED;
! 174: proc_send_s(c->peer, MSG_LOCK, cmd);
! 175: }
! 176:
! 177: void
! 178: server_kill_window(struct window *w)
! 179: {
! 180: struct session *s, *next_s, *target_s;
! 181: struct session_group *sg;
! 182: struct winlink *wl;
! 183:
! 184: next_s = RB_MIN(sessions, &sessions);
! 185: while (next_s != NULL) {
! 186: s = next_s;
! 187: next_s = RB_NEXT(sessions, &sessions, s);
! 188:
! 189: if (!session_has(s, w))
! 190: continue;
! 191: server_unzoom_window(w);
! 192: while ((wl = winlink_find_by_window(&s->windows, w)) != NULL) {
! 193: if (session_detach(s, wl)) {
! 194: server_destroy_session_group(s);
! 195: break;
! 196: } else
! 197: server_redraw_session_group(s);
! 198: }
! 199:
! 200: if (options_get_number(s->options, "renumber-windows")) {
! 201: if ((sg = session_group_contains(s)) != NULL) {
! 202: TAILQ_FOREACH(target_s, &sg->sessions, gentry)
! 203: session_renumber_windows(target_s);
! 204: } else
! 205: session_renumber_windows(s);
! 206: }
! 207: }
! 208: recalculate_sizes();
! 209: }
! 210:
! 211: int
! 212: server_link_window(struct session *src, struct winlink *srcwl,
! 213: struct session *dst, int dstidx, int killflag, int selectflag,
! 214: char **cause)
! 215: {
! 216: struct winlink *dstwl;
! 217: struct session_group *srcsg, *dstsg;
! 218:
! 219: srcsg = session_group_contains(src);
! 220: dstsg = session_group_contains(dst);
! 221: if (src != dst && srcsg != NULL && dstsg != NULL && srcsg == dstsg) {
! 222: xasprintf(cause, "sessions are grouped");
! 223: return (-1);
! 224: }
! 225:
! 226: dstwl = NULL;
! 227: if (dstidx != -1)
! 228: dstwl = winlink_find_by_index(&dst->windows, dstidx);
! 229: if (dstwl != NULL) {
! 230: if (dstwl->window == srcwl->window) {
! 231: xasprintf(cause, "same index: %d", dstidx);
! 232: return (-1);
! 233: }
! 234: if (killflag) {
! 235: /*
! 236: * Can't use session_detach as it will destroy session
! 237: * if this makes it empty.
! 238: */
! 239: notify_session_window("window-unlinked", dst,
! 240: dstwl->window);
! 241: dstwl->flags &= ~WINLINK_ALERTFLAGS;
! 242: winlink_stack_remove(&dst->lastw, dstwl);
! 243: winlink_remove(&dst->windows, dstwl);
! 244:
! 245: /* Force select/redraw if current. */
! 246: if (dstwl == dst->curw) {
! 247: selectflag = 1;
! 248: dst->curw = NULL;
! 249: }
! 250: }
! 251: }
! 252:
! 253: if (dstidx == -1)
! 254: dstidx = -1 - options_get_number(dst->options, "base-index");
! 255: dstwl = session_attach(dst, srcwl->window, dstidx, cause);
! 256: if (dstwl == NULL)
! 257: return (-1);
! 258:
! 259: if (selectflag)
! 260: session_select(dst, dstwl->idx);
! 261: server_redraw_session_group(dst);
! 262:
! 263: return (0);
! 264: }
! 265:
! 266: void
! 267: server_unlink_window(struct session *s, struct winlink *wl)
! 268: {
! 269: if (session_detach(s, wl))
! 270: server_destroy_session_group(s);
! 271: else
! 272: server_redraw_session_group(s);
! 273: }
! 274:
! 275: void
! 276: server_destroy_pane(struct window_pane *wp, int notify)
! 277: {
! 278: struct window *w = wp->window;
! 279: int old_fd;
! 280: struct screen_write_ctx ctx;
! 281: struct grid_cell gc;
! 282:
! 283: old_fd = wp->fd;
! 284: if (wp->fd != -1) {
! 285: #ifdef HAVE_UTEMPTER
! 286: utempter_remove_record(wp->fd);
! 287: #endif
! 288: bufferevent_free(wp->event);
! 289: close(wp->fd);
! 290: wp->fd = -1;
! 291: }
! 292:
! 293: if (options_get_number(w->options, "remain-on-exit")) {
! 294: if (old_fd == -1)
! 295: return;
! 296:
! 297: if (notify)
! 298: notify_pane("pane-died", wp);
! 299:
! 300: screen_write_start(&ctx, wp, &wp->base);
! 301: screen_write_scrollregion(&ctx, 0, screen_size_y(ctx.s) - 1);
! 302: screen_write_cursormove(&ctx, 0, screen_size_y(ctx.s) - 1);
! 303: screen_write_linefeed(&ctx, 1);
! 304: memcpy(&gc, &grid_default_cell, sizeof gc);
! 305: gc.attr |= GRID_ATTR_BRIGHT;
! 306: screen_write_puts(&ctx, &gc, "Pane is dead");
! 307: screen_write_stop(&ctx);
! 308: wp->flags |= PANE_REDRAW;
! 309:
! 310: return;
! 311: }
! 312:
! 313: if (notify)
! 314: notify_pane("pane-exited", wp);
! 315:
! 316: server_unzoom_window(w);
! 317: layout_close_pane(wp);
! 318: window_remove_pane(w, wp);
! 319:
! 320: if (TAILQ_EMPTY(&w->panes))
! 321: server_kill_window(w);
! 322: else
! 323: server_redraw_window(w);
! 324: }
! 325:
! 326: static void
! 327: server_destroy_session_group(struct session *s)
! 328: {
! 329: struct session_group *sg;
! 330: struct session *s1;
! 331:
! 332: if ((sg = session_group_contains(s)) == NULL)
! 333: server_destroy_session(s);
! 334: else {
! 335: TAILQ_FOREACH_SAFE(s, &sg->sessions, gentry, s1) {
! 336: server_destroy_session(s);
! 337: session_destroy(s);
! 338: }
! 339: }
! 340: }
! 341:
! 342: static struct session *
! 343: server_next_session(struct session *s)
! 344: {
! 345: struct session *s_loop, *s_out;
! 346:
! 347: s_out = NULL;
! 348: RB_FOREACH(s_loop, sessions, &sessions) {
! 349: if (s_loop == s)
! 350: continue;
! 351: if (s_out == NULL ||
! 352: timercmp(&s_loop->activity_time, &s_out->activity_time, <))
! 353: s_out = s_loop;
! 354: }
! 355: return (s_out);
! 356: }
! 357:
! 358: void
! 359: server_destroy_session(struct session *s)
! 360: {
! 361: struct client *c;
! 362: struct session *s_new;
! 363:
! 364: if (!options_get_number(s->options, "detach-on-destroy"))
! 365: s_new = server_next_session(s);
! 366: else
! 367: s_new = NULL;
! 368:
! 369: TAILQ_FOREACH(c, &clients, entry) {
! 370: if (c->session != s)
! 371: continue;
! 372: if (s_new == NULL) {
! 373: c->session = NULL;
! 374: c->flags |= CLIENT_EXIT;
! 375: } else {
! 376: c->last_session = NULL;
! 377: c->session = s_new;
! 378: server_client_set_key_table(c, NULL);
! 379: status_timer_start(c);
! 380: notify_client("client-session-changed", c);
! 381: session_update_activity(s_new, NULL);
! 382: gettimeofday(&s_new->last_attached_time, NULL);
! 383: server_redraw_client(c);
! 384: alerts_check_session(s_new);
! 385: }
! 386: }
! 387: recalculate_sizes();
! 388: }
! 389:
! 390: void
! 391: server_check_unattached(void)
! 392: {
! 393: struct session *s;
! 394:
! 395: /*
! 396: * If any sessions are no longer attached and have destroy-unattached
! 397: * set, collect them.
! 398: */
! 399: RB_FOREACH(s, sessions, &sessions) {
! 400: if (!(s->flags & SESSION_UNATTACHED))
! 401: continue;
! 402: if (options_get_number (s->options, "destroy-unattached"))
! 403: session_destroy(s);
! 404: }
! 405: }
! 406:
! 407: /* Set stdin callback. */
! 408: int
! 409: server_set_stdin_callback(struct client *c, void (*cb)(struct client *, int,
! 410: void *), void *cb_data, char **cause)
! 411: {
! 412: if (c == NULL || c->session != NULL) {
! 413: *cause = xstrdup("no client with stdin");
! 414: return (-1);
! 415: }
! 416: if (c->flags & CLIENT_TERMINAL) {
! 417: *cause = xstrdup("stdin is a tty");
! 418: return (-1);
! 419: }
! 420: if (c->stdin_callback != NULL) {
! 421: *cause = xstrdup("stdin in use");
! 422: return (-1);
! 423: }
! 424:
! 425: c->stdin_callback_data = cb_data;
! 426: c->stdin_callback = cb;
! 427:
! 428: c->references++;
! 429:
! 430: if (c->stdin_closed)
! 431: c->stdin_callback(c, 1, c->stdin_callback_data);
! 432:
! 433: proc_send(c->peer, MSG_STDIN, -1, NULL, 0);
! 434:
! 435: return (0);
! 436: }
! 437:
! 438: void
! 439: server_unzoom_window(struct window *w)
! 440: {
! 441: if (window_unzoom(w) == 0) {
! 442: server_redraw_window(w);
! 443: server_status_window(w);
! 444: }
! 445: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>