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>