Annotation of src/usr.bin/tmux/server-client.c, Revision 1.96
1.96 ! nicm 1: /* $OpenBSD: server-client.c,v 1.95 2013/03/24 09:57:59 nicm Exp $ */
1.1 nicm 2:
3: /*
4: * Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net>
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>
1.92 nicm 20: #include <sys/ioctl.h>
1.1 nicm 21:
1.12 nicm 22: #include <event.h>
1.1 nicm 23: #include <fcntl.h>
24: #include <string.h>
1.4 nicm 25: #include <time.h>
1.1 nicm 26: #include <paths.h>
1.77 nicm 27: #include <stdlib.h>
1.1 nicm 28: #include <unistd.h>
29:
30: #include "tmux.h"
31:
1.91 nicm 32: void server_client_check_focus(struct window_pane *);
1.92 nicm 33: void server_client_check_resize(struct window_pane *);
1.81 nicm 34: void server_client_check_mouse(struct client *, struct window_pane *);
1.17 nicm 35: void server_client_repeat_timer(int, short, void *);
1.36 nicm 36: void server_client_check_exit(struct client *);
1.1 nicm 37: void server_client_check_redraw(struct client *);
38: void server_client_set_title(struct client *);
1.18 nicm 39: void server_client_reset_state(struct client *);
1.82 nicm 40: int server_client_assume_paste(struct session *);
1.1 nicm 41:
42: int server_client_msg_dispatch(struct client *);
43: void server_client_msg_command(struct client *, struct msg_command_data *);
44: void server_client_msg_identify(
1.25 nicm 45: struct client *, struct msg_identify_data *, int);
1.1 nicm 46: void server_client_msg_shell(struct client *);
47:
48: /* Create a new client. */
49: void
50: server_client_create(int fd)
51: {
52: struct client *c;
53: u_int i;
54:
1.49 nicm 55: setblocking(fd, 0);
1.1 nicm 56:
57: c = xcalloc(1, sizeof *c);
58: c->references = 0;
59: imsg_init(&c->ibuf, fd);
1.14 nicm 60: server_update_event(c);
1.25 nicm 61:
1.10 nicm 62: if (gettimeofday(&c->creation_time, NULL) != 0)
1.1 nicm 63: fatal("gettimeofday failed");
1.11 nicm 64: memcpy(&c->activity_time, &c->creation_time, sizeof c->activity_time);
1.1 nicm 65:
1.94 nicm 66: c->cmdq = cmdq_new(c);
67: c->cmdq->client_exit = 1;
68:
1.73 nicm 69: c->stdin_data = evbuffer_new ();
70: c->stdout_data = evbuffer_new ();
71: c->stderr_data = evbuffer_new ();
1.33 nicm 72:
1.1 nicm 73: c->tty.fd = -1;
74: c->title = NULL;
75:
76: c->session = NULL;
1.45 nicm 77: c->last_session = NULL;
1.1 nicm 78: c->tty.sx = 80;
79: c->tty.sy = 24;
80:
81: screen_init(&c->status, c->tty.sx, 1, 0);
1.51 nicm 82: RB_INIT(&c->status_new);
83: RB_INIT(&c->status_old);
1.1 nicm 84:
85: c->message_string = NULL;
1.21 nicm 86: ARRAY_INIT(&c->message_log);
1.1 nicm 87:
88: c->prompt_string = NULL;
89: c->prompt_buffer = NULL;
90: c->prompt_index = 0;
91:
1.81 nicm 92: c->tty.mouse.xb = c->tty.mouse.button = 3;
93: c->tty.mouse.x = c->tty.mouse.y = -1;
94: c->tty.mouse.lx = c->tty.mouse.ly = -1;
95: c->tty.mouse.sx = c->tty.mouse.sy = -1;
96: c->tty.mouse.event = MOUSE_EVENT_UP;
97: c->tty.mouse.flags = 0;
1.57 nicm 98:
1.93 nicm 99: c->flags |= CLIENT_FOCUSED;
100:
1.17 nicm 101: evtimer_set(&c->repeat_timer, server_client_repeat_timer, c);
102:
1.1 nicm 103: for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
104: if (ARRAY_ITEM(&clients, i) == NULL) {
105: ARRAY_SET(&clients, i, c);
106: return;
107: }
108: }
109: ARRAY_ADD(&clients, c);
110: log_debug("new client %d", fd);
1.72 nicm 111: }
112:
113: /* Open client terminal if needed. */
114: int
115: server_client_open(struct client *c, struct session *s, char **cause)
116: {
117: struct options *oo = s != NULL ? &s->options : &global_s_options;
118: char *overrides;
119:
1.75 nicm 120: if (c->flags & CLIENT_CONTROL)
121: return (0);
122:
1.72 nicm 123: if (!(c->flags & CLIENT_TERMINAL)) {
124: *cause = xstrdup ("not a terminal");
125: return (-1);
126: }
127:
128: overrides = options_get_string(oo, "terminal-overrides");
129: if (tty_open(&c->tty, overrides, cause) != 0)
130: return (-1);
131:
132: return (0);
1.1 nicm 133: }
134:
135: /* Lost a client. */
136: void
137: server_client_lost(struct client *c)
138: {
1.21 nicm 139: struct message_entry *msg;
140: u_int i;
1.1 nicm 141:
142: for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
143: if (ARRAY_ITEM(&clients, i) == c)
144: ARRAY_SET(&clients, i, NULL);
145: }
146: log_debug("lost client %d", c->ibuf.fd);
147:
148: /*
149: * If CLIENT_TERMINAL hasn't been set, then tty_init hasn't been called
150: * and tty_free might close an unrelated fd.
151: */
152: if (c->flags & CLIENT_TERMINAL)
153: tty_free(&c->tty);
154:
1.73 nicm 155: evbuffer_free (c->stdin_data);
156: evbuffer_free (c->stdout_data);
157: evbuffer_free (c->stderr_data);
1.33 nicm 158:
1.51 nicm 159: status_free_jobs(&c->status_new);
160: status_free_jobs(&c->status_old);
1.1 nicm 161: screen_free(&c->status);
162:
1.77 nicm 163: free(c->title);
1.1 nicm 164:
1.17 nicm 165: evtimer_del(&c->repeat_timer);
166:
1.69 nicm 167: if (event_initialized(&c->identify_timer))
168: evtimer_del(&c->identify_timer);
1.15 nicm 169:
1.77 nicm 170: free(c->message_string);
1.69 nicm 171: if (event_initialized (&c->message_timer))
172: evtimer_del(&c->message_timer);
1.21 nicm 173: for (i = 0; i < ARRAY_LENGTH(&c->message_log); i++) {
174: msg = &ARRAY_ITEM(&c->message_log, i);
1.77 nicm 175: free(msg->msg);
1.21 nicm 176: }
177: ARRAY_FREE(&c->message_log);
1.1 nicm 178:
1.77 nicm 179: free(c->prompt_string);
180: free(c->prompt_buffer);
181: free(c->cwd);
1.61 nicm 182:
1.94 nicm 183: c->cmdq->dead = 1;
184: cmdq_free(c->cmdq);
185: c->cmdq = NULL;
186:
1.61 nicm 187: environ_free(&c->environ);
1.1 nicm 188:
189: close(c->ibuf.fd);
190: imsg_clear(&c->ibuf);
1.69 nicm 191: if (event_initialized(&c->event))
192: event_del(&c->event);
1.13 nicm 193:
1.1 nicm 194: for (i = 0; i < ARRAY_LENGTH(&dead_clients); i++) {
195: if (ARRAY_ITEM(&dead_clients, i) == NULL) {
196: ARRAY_SET(&dead_clients, i, c);
197: break;
198: }
199: }
200: if (i == ARRAY_LENGTH(&dead_clients))
201: ARRAY_ADD(&dead_clients, c);
202: c->flags |= CLIENT_DEAD;
1.71 nicm 203:
204: server_add_accept(0); /* may be more file descriptors now */
1.1 nicm 205:
206: recalculate_sizes();
1.41 nicm 207: server_check_unattached();
1.19 nicm 208: server_update_socket();
1.9 nicm 209: }
210:
1.1 nicm 211: /* Process a single client event. */
212: void
1.12 nicm 213: server_client_callback(int fd, short events, void *data)
1.1 nicm 214: {
215: struct client *c = data;
1.7 nicm 216:
217: if (c->flags & CLIENT_DEAD)
218: return;
1.1 nicm 219:
220: if (fd == c->ibuf.fd) {
1.12 nicm 221: if (events & EV_WRITE && msgbuf_write(&c->ibuf.w) < 0)
1.1 nicm 222: goto client_lost;
223:
224: if (c->flags & CLIENT_BAD) {
225: if (c->ibuf.w.queued == 0)
226: goto client_lost;
227: return;
228: }
229:
1.12 nicm 230: if (events & EV_READ && server_client_msg_dispatch(c) != 0)
1.1 nicm 231: goto client_lost;
232: }
1.14 nicm 233:
1.73 nicm 234: server_push_stdout(c);
235: server_push_stderr(c);
236:
1.25 nicm 237: server_update_event(c);
1.1 nicm 238: return;
239:
240: client_lost:
241: server_client_lost(c);
242: }
243:
1.16 nicm 244: /* Handle client status timer. */
245: void
246: server_client_status_timer(void)
247: {
248: struct client *c;
249: struct session *s;
250: struct timeval tv;
1.20 nicm 251: u_int i;
252: int interval;
253: time_t difference;
1.16 nicm 254:
255: if (gettimeofday(&tv, NULL) != 0)
256: fatal("gettimeofday failed");
257:
258: for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
259: c = ARRAY_ITEM(&clients, i);
260: if (c == NULL || c->session == NULL)
261: continue;
262: if (c->message_string != NULL || c->prompt_string != NULL) {
263: /*
264: * Don't need timed redraw for messages/prompts so bail
265: * now. The status timer isn't reset when they are
266: * redrawn anyway.
267: */
268: continue;
269: }
270: s = c->session;
271:
272: if (!options_get_number(&s->options, "status"))
273: continue;
274: interval = options_get_number(&s->options, "status-interval");
275:
1.20 nicm 276: difference = tv.tv_sec - c->status_timer.tv_sec;
277: if (difference >= interval) {
1.51 nicm 278: status_update_jobs(c);
1.16 nicm 279: c->flags |= CLIENT_STATUS;
280: }
281: }
282: }
283:
1.66 nicm 284: /* Check for mouse keys. */
285: void
1.81 nicm 286: server_client_check_mouse(struct client *c, struct window_pane *wp)
1.66 nicm 287: {
1.81 nicm 288: struct session *s = c->session;
289: struct options *oo = &s->options;
290: struct mouse_event *m = &c->tty.mouse;
291: int statusat;
1.66 nicm 292:
293: statusat = status_at_line(c);
294:
295: /* Is this a window selection click on the status line? */
1.81 nicm 296: if (statusat != -1 && m->y == (u_int)statusat &&
1.66 nicm 297: options_get_number(oo, "mouse-select-window")) {
1.81 nicm 298: if (m->event & MOUSE_EVENT_CLICK) {
299: status_set_window_at(c, m->x);
300: } else if (m->event == MOUSE_EVENT_WHEEL) {
301: if (m->wheel == MOUSE_WHEEL_UP)
1.66 nicm 302: session_previous(c->session, 0);
1.81 nicm 303: else if (m->wheel == MOUSE_WHEEL_DOWN)
1.66 nicm 304: session_next(c->session, 0);
1.81 nicm 305: server_redraw_session(s);
1.66 nicm 306: }
1.81 nicm 307: recalculate_sizes();
1.67 nicm 308: return;
1.66 nicm 309: }
310:
311: /*
312: * Not on status line - adjust mouse position if status line is at the
313: * top and limit if at the bottom. From here on a struct mouse
314: * represents the offset onto the window itself.
315: */
1.81 nicm 316: if (statusat == 0 && m->y > 0)
317: m->y--;
318: else if (statusat > 0 && m->y >= (u_int)statusat)
319: m->y = statusat - 1;
1.66 nicm 320:
321: /* Is this a pane selection? Allow down only in copy mode. */
322: if (options_get_number(oo, "mouse-select-pane") &&
1.81 nicm 323: (m->event == MOUSE_EVENT_DOWN || wp->mode != &window_copy_mode)) {
324: window_set_active_at(wp->window, m->x, m->y);
1.66 nicm 325: server_redraw_window_borders(wp->window);
326: wp = wp->window->active; /* may have changed */
327: }
328:
329: /* Check if trying to resize pane. */
330: if (options_get_number(oo, "mouse-resize-pane"))
1.81 nicm 331: layout_resize_pane_mouse(c);
1.66 nicm 332:
333: /* Update last and pass through to client. */
1.81 nicm 334: window_pane_mouse(wp, c->session, m);
1.66 nicm 335: }
336:
1.82 nicm 337: /* Is this fast enough to probably be a paste? */
338: int
339: server_client_assume_paste(struct session *s)
340: {
341: struct timeval tv;
1.84 nicm 342: int t;
1.82 nicm 343:
344: if ((t = options_get_number(&s->options, "assume-paste-time")) == 0)
1.83 nicm 345: return (0);
1.82 nicm 346:
347: timersub(&s->activity_time, &s->last_activity_time, &tv);
348: if (tv.tv_sec == 0 && tv.tv_usec < t * 1000)
1.83 nicm 349: return (1);
350: return (0);
1.82 nicm 351: }
352:
1.18 nicm 353: /* Handle data key input from client. */
354: void
1.74 nicm 355: server_client_handle_key(struct client *c, int key)
1.18 nicm 356: {
357: struct session *s;
358: struct window *w;
359: struct window_pane *wp;
360: struct timeval tv;
361: struct key_binding *bd;
1.82 nicm 362: int xtimeout, isprefix, ispaste;
1.18 nicm 363:
364: /* Check the client is good to accept input. */
365: if ((c->flags & (CLIENT_DEAD|CLIENT_SUSPENDED)) != 0)
366: return;
1.86 nicm 367:
1.18 nicm 368: if (c->session == NULL)
369: return;
370: s = c->session;
371:
372: /* Update the activity timer. */
373: if (gettimeofday(&c->activity_time, NULL) != 0)
374: fatal("gettimeofday failed");
1.82 nicm 375:
376: memcpy(&s->last_activity_time, &s->activity_time,
377: sizeof s->last_activity_time);
1.18 nicm 378: memcpy(&s->activity_time, &c->activity_time, sizeof s->activity_time);
379:
380: w = c->session->curw->window;
381: wp = w->active;
382:
383: /* Special case: number keys jump to pane in identify mode. */
1.25 nicm 384: if (c->flags & CLIENT_IDENTIFY && key >= '0' && key <= '9') {
1.30 nicm 385: if (c->flags & CLIENT_READONLY)
386: return;
1.95 nicm 387: window_unzoom(w);
1.18 nicm 388: wp = window_pane_at_index(w, key - '0');
389: if (wp != NULL && window_pane_visible(wp))
390: window_set_active_pane(w, wp);
391: server_clear_identify(c);
392: return;
393: }
394:
395: /* Handle status line. */
1.30 nicm 396: if (!(c->flags & CLIENT_READONLY)) {
397: status_message_clear(c);
398: server_clear_identify(c);
399: }
1.18 nicm 400: if (c->prompt_string != NULL) {
1.30 nicm 401: if (!(c->flags & CLIENT_READONLY))
402: status_prompt_key(c, key);
1.18 nicm 403: return;
404: }
405:
406: /* Check for mouse keys. */
407: if (key == KEYC_MOUSE) {
1.30 nicm 408: if (c->flags & CLIENT_READONLY)
409: return;
1.81 nicm 410: server_client_check_mouse(c, wp);
1.18 nicm 411: return;
412: }
413:
414: /* Is this a prefix key? */
1.82 nicm 415: if (key == options_get_number(&s->options, "prefix"))
1.63 nicm 416: isprefix = 1;
1.82 nicm 417: else if (key == options_get_number(&s->options, "prefix2"))
1.63 nicm 418: isprefix = 1;
419: else
420: isprefix = 0;
1.18 nicm 421:
1.82 nicm 422: /* Treat prefix as a regular key when pasting is detected. */
423: ispaste = server_client_assume_paste(s);
424: if (ispaste)
425: isprefix = 0;
426:
1.18 nicm 427: /* No previous prefix key. */
428: if (!(c->flags & CLIENT_PREFIX)) {
1.82 nicm 429: if (isprefix) {
1.18 nicm 430: c->flags |= CLIENT_PREFIX;
1.85 nicm 431: server_status_client(c);
1.82 nicm 432: return;
1.18 nicm 433: }
1.82 nicm 434:
435: /* Try as a non-prefix key binding. */
436: if (ispaste || (bd = key_bindings_lookup(key)) == NULL) {
437: if (!(c->flags & CLIENT_READONLY))
438: window_pane_key(wp, s, key);
439: } else
440: key_bindings_dispatch(bd, c);
1.18 nicm 441: return;
442: }
443:
444: /* Prefix key already pressed. Reset prefix and lookup key. */
445: c->flags &= ~CLIENT_PREFIX;
1.85 nicm 446: server_status_client(c);
1.18 nicm 447: if ((bd = key_bindings_lookup(key | KEYC_PREFIX)) == NULL) {
448: /* If repeating, treat this as a key, else ignore. */
449: if (c->flags & CLIENT_REPEAT) {
450: c->flags &= ~CLIENT_REPEAT;
451: if (isprefix)
452: c->flags |= CLIENT_PREFIX;
1.30 nicm 453: else if (!(c->flags & CLIENT_READONLY))
1.82 nicm 454: window_pane_key(wp, s, key);
1.18 nicm 455: }
456: return;
457: }
458:
459: /* If already repeating, but this key can't repeat, skip it. */
460: if (c->flags & CLIENT_REPEAT && !bd->can_repeat) {
461: c->flags &= ~CLIENT_REPEAT;
462: if (isprefix)
463: c->flags |= CLIENT_PREFIX;
1.30 nicm 464: else if (!(c->flags & CLIENT_READONLY))
1.82 nicm 465: window_pane_key(wp, s, key);
1.18 nicm 466: return;
467: }
468:
469: /* If this key can repeat, reset the repeat flags and timer. */
1.82 nicm 470: xtimeout = options_get_number(&s->options, "repeat-time");
1.18 nicm 471: if (xtimeout != 0 && bd->can_repeat) {
472: c->flags |= CLIENT_PREFIX|CLIENT_REPEAT;
1.25 nicm 473:
1.18 nicm 474: tv.tv_sec = xtimeout / 1000;
475: tv.tv_usec = (xtimeout % 1000) * 1000L;
476: evtimer_del(&c->repeat_timer);
477: evtimer_add(&c->repeat_timer, &tv);
478: }
479:
480: /* Dispatch the command. */
481: key_bindings_dispatch(bd, c);
482: }
483:
1.2 nicm 484: /* Client functions that need to happen every loop. */
485: void
486: server_client_loop(void)
487: {
488: struct client *c;
489: struct window *w;
490: struct window_pane *wp;
491: u_int i;
492:
493: for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
494: c = ARRAY_ITEM(&clients, i);
1.36 nicm 495: if (c == NULL)
1.2 nicm 496: continue;
497:
1.36 nicm 498: server_client_check_exit(c);
499: if (c->session != NULL) {
500: server_client_check_redraw(c);
501: server_client_reset_state(c);
502: }
1.2 nicm 503: }
504:
505: /*
506: * Any windows will have been redrawn as part of clients, so clear
1.92 nicm 507: * their flags now. Also check pane focus and resize.
1.2 nicm 508: */
509: for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
510: w = ARRAY_ITEM(&windows, i);
511: if (w == NULL)
512: continue;
513:
514: w->flags &= ~WINDOW_REDRAW;
1.91 nicm 515: TAILQ_FOREACH(wp, &w->panes, entry) {
516: server_client_check_focus(wp);
1.92 nicm 517: server_client_check_resize(wp);
1.2 nicm 518: wp->flags &= ~PANE_REDRAW;
1.91 nicm 519: }
1.2 nicm 520: }
1.92 nicm 521: }
522:
523: /* Check if pane should be resized. */
524: void
525: server_client_check_resize(struct window_pane *wp)
526: {
527: struct winsize ws;
528:
529: if (wp->fd == -1 || !(wp->flags & PANE_RESIZE))
530: return;
531:
532: memset(&ws, 0, sizeof ws);
533: ws.ws_col = wp->sx;
534: ws.ws_row = wp->sy;
535:
536: if (ioctl(wp->fd, TIOCSWINSZ, &ws) == -1) {
537: #ifdef __sun
538: /*
539: * Some versions of Solaris apparently can return an error when
540: * resizing; don't know why this happens, can't reproduce on
541: * other platforms and ignoring it doesn't seem to cause any
542: * issues.
543: */
544: if (errno != EINVAL)
545: #endif
546: fatal("ioctl failed");
547: }
548:
549: wp->flags &= ~PANE_RESIZE;
1.91 nicm 550: }
551:
552: /* Check whether pane should be focused. */
553: void
554: server_client_check_focus(struct window_pane *wp)
555: {
1.93 nicm 556: u_int i;
557: struct client *c;
1.91 nicm 558:
559: /* If we don't care about focus, forget it. */
560: if (!(wp->base.mode & MODE_FOCUSON))
561: return;
562:
563: /* If we're not the active pane in our window, we're not focused. */
564: if (wp->window->active != wp)
565: goto not_focused;
566:
567: /* If we're in a mode, we're not focused. */
568: if (wp->screen != &wp->base)
569: goto not_focused;
570:
571: /*
1.93 nicm 572: * If our window is the current window in any focused clients with an
573: * attached session, we're focused.
1.91 nicm 574: */
1.93 nicm 575: for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
576: c = ARRAY_ITEM(&clients, i);
577: if (c == NULL || c->session == NULL)
578: continue;
579:
580: if (!(c->flags & CLIENT_FOCUSED))
1.91 nicm 581: continue;
1.93 nicm 582: if (c->session->flags & SESSION_UNATTACHED)
583: continue;
584:
585: if (c->session->curw->window == wp->window)
1.91 nicm 586: goto focused;
587: }
588:
589: not_focused:
590: if (wp->flags & PANE_FOCUSED)
591: bufferevent_write(wp->event, "\033[O", 3);
592: wp->flags &= ~PANE_FOCUSED;
593: return;
594:
595: focused:
596: if (!(wp->flags & PANE_FOCUSED))
597: bufferevent_write(wp->event, "\033[I", 3);
598: wp->flags |= PANE_FOCUSED;
1.2 nicm 599: }
600:
1.18 nicm 601: /*
602: * Update cursor position and mode settings. The scroll region and attributes
603: * are cleared when idle (waiting for an event) as this is the most likely time
604: * a user may interrupt tmux, for example with ~^Z in ssh(1). This is a
605: * compromise between excessive resets and likelihood of an interrupt.
606: *
607: * tty_region/tty_reset/tty_update_mode already take care of not resetting
608: * things that are already in their default state.
609: */
1.1 nicm 610: void
1.18 nicm 611: server_client_reset_state(struct client *c)
1.1 nicm 612: {
1.18 nicm 613: struct window *w = c->session->curw->window;
614: struct window_pane *wp = w->active;
615: struct screen *s = wp->screen;
616: struct options *oo = &c->session->options;
1.54 nicm 617: struct options *wo = &w->options;
1.66 nicm 618: int status, mode, o;
1.60 nicm 619:
620: if (c->flags & CLIENT_SUSPENDED)
621: return;
1.1 nicm 622:
1.86 nicm 623: if (c->flags & CLIENT_CONTROL)
624: return;
625:
1.1 nicm 626: tty_region(&c->tty, 0, c->tty.sy - 1);
627:
628: status = options_get_number(oo, "status");
629: if (!window_pane_visible(wp) || wp->yoff + s->cy >= c->tty.sy - status)
630: tty_cursor(&c->tty, 0, 0);
1.66 nicm 631: else {
632: o = status && options_get_number (oo, "status-position") == 0;
633: tty_cursor(&c->tty, wp->xoff + s->cx, o + wp->yoff + s->cy);
634: }
1.1 nicm 635:
1.50 nicm 636: /*
1.57 nicm 637: * Resizing panes with the mouse requires at least button mode to give
638: * a smooth appearance.
639: */
640: mode = s->mode;
1.81 nicm 641: if ((c->tty.mouse.flags & MOUSE_RESIZE_PANE) &&
1.57 nicm 642: !(mode & (MODE_MOUSE_BUTTON|MODE_MOUSE_ANY)))
643: mode |= MODE_MOUSE_BUTTON;
644:
645: /*
1.50 nicm 646: * Any mode will do for mouse-select-pane, but set standard mode if
647: * none.
648: */
1.54 nicm 649: if ((mode & ALL_MOUSE_MODES) == 0) {
650: if (TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry) != NULL &&
1.55 nicm 651: options_get_number(oo, "mouse-select-pane"))
1.57 nicm 652: mode |= MODE_MOUSE_STANDARD;
653: else if (options_get_number(oo, "mouse-resize-pane"))
1.54 nicm 654: mode |= MODE_MOUSE_STANDARD;
655: else if (options_get_number(oo, "mouse-select-window"))
656: mode |= MODE_MOUSE_STANDARD;
657: else if (options_get_number(wo, "mode-mouse"))
658: mode |= MODE_MOUSE_STANDARD;
659: }
1.48 nicm 660:
661: /*
662: * Set UTF-8 mouse input if required. If the terminal is UTF-8, the
663: * user has set mouse-utf8 and any mouse mode is in effect, turn on
664: * UTF-8 mouse input. If the receiving terminal hasn't requested it
665: * (that is, it isn't in s->mode), then it'll be converted in
666: * input_mouse.
667: */
668: if ((c->tty.flags & TTY_UTF8) &&
669: (mode & ALL_MOUSE_MODES) && options_get_number(oo, "mouse-utf8"))
670: mode |= MODE_MOUSE_UTF8;
671: else
672: mode &= ~MODE_MOUSE_UTF8;
673:
674: /* Set the terminal mode and reset attributes. */
1.59 nicm 675: tty_update_mode(&c->tty, mode, s);
1.1 nicm 676: tty_reset(&c->tty);
1.17 nicm 677: }
678:
679: /* Repeat time callback. */
680: void
681: server_client_repeat_timer(unused int fd, unused short events, void *data)
682: {
683: struct client *c = data;
684:
1.85 nicm 685: if (c->flags & CLIENT_REPEAT) {
686: if (c->flags & CLIENT_PREFIX)
687: server_status_client(c);
1.17 nicm 688: c->flags &= ~(CLIENT_PREFIX|CLIENT_REPEAT);
1.85 nicm 689: }
1.1 nicm 690: }
691:
1.36 nicm 692: /* Check if client should be exited. */
693: void
694: server_client_check_exit(struct client *c)
695: {
696: struct msg_exit_data exitdata;
697:
698: if (!(c->flags & CLIENT_EXIT))
699: return;
700:
1.73 nicm 701: if (EVBUFFER_LENGTH(c->stdin_data) != 0)
702: return;
703: if (EVBUFFER_LENGTH(c->stdout_data) != 0)
1.36 nicm 704: return;
1.73 nicm 705: if (EVBUFFER_LENGTH(c->stderr_data) != 0)
1.36 nicm 706: return;
707:
708: exitdata.retcode = c->retcode;
709: server_write_client(c, MSG_EXIT, &exitdata, sizeof exitdata);
710:
711: c->flags &= ~CLIENT_EXIT;
1.38 nicm 712: }
713:
1.1 nicm 714: /* Check for client redraws. */
715: void
716: server_client_check_redraw(struct client *c)
717: {
718: struct session *s = c->session;
719: struct window_pane *wp;
720: int flags, redraw;
721:
1.86 nicm 722: if (c->flags & (CLIENT_CONTROL|CLIENT_SUSPENDED))
1.79 nicm 723: return;
724:
1.1 nicm 725: flags = c->tty.flags & TTY_FREEZE;
726: c->tty.flags &= ~TTY_FREEZE;
727:
728: if (c->flags & (CLIENT_REDRAW|CLIENT_STATUS)) {
729: if (options_get_number(&s->options, "set-titles"))
730: server_client_set_title(c);
1.12 nicm 731:
1.1 nicm 732: if (c->message_string != NULL)
733: redraw = status_message_redraw(c);
734: else if (c->prompt_string != NULL)
735: redraw = status_prompt_redraw(c);
736: else
737: redraw = status_redraw(c);
738: if (!redraw)
739: c->flags &= ~CLIENT_STATUS;
740: }
741:
742: if (c->flags & CLIENT_REDRAW) {
1.27 nicm 743: screen_redraw_screen(c, 0, 0);
744: c->flags &= ~(CLIENT_STATUS|CLIENT_BORDERS);
1.38 nicm 745: } else if (c->flags & CLIENT_REDRAWWINDOW) {
746: TAILQ_FOREACH(wp, &c->session->curw->window->panes, entry)
747: screen_redraw_pane(c, wp);
748: c->flags &= ~CLIENT_REDRAWWINDOW;
1.1 nicm 749: } else {
750: TAILQ_FOREACH(wp, &c->session->curw->window->panes, entry) {
751: if (wp->flags & PANE_REDRAW)
752: screen_redraw_pane(c, wp);
753: }
754: }
755:
1.27 nicm 756: if (c->flags & CLIENT_BORDERS)
757: screen_redraw_screen(c, 0, 1);
758:
1.1 nicm 759: if (c->flags & CLIENT_STATUS)
1.27 nicm 760: screen_redraw_screen(c, 1, 0);
1.1 nicm 761:
762: c->tty.flags |= flags;
763:
1.27 nicm 764: c->flags &= ~(CLIENT_REDRAW|CLIENT_STATUS|CLIENT_BORDERS);
1.1 nicm 765: }
766:
767: /* Set client title. */
768: void
769: server_client_set_title(struct client *c)
770: {
771: struct session *s = c->session;
772: const char *template;
773: char *title;
774:
775: template = options_get_string(&s->options, "set-titles-string");
1.25 nicm 776:
1.52 nicm 777: title = status_replace(c, NULL, NULL, NULL, template, time(NULL), 1);
1.1 nicm 778: if (c->title == NULL || strcmp(title, c->title) != 0) {
1.77 nicm 779: free(c->title);
1.1 nicm 780: c->title = xstrdup(title);
781: tty_set_title(&c->tty, c->title);
782: }
1.77 nicm 783: free(title);
1.1 nicm 784: }
785:
786: /* Dispatch message from client. */
787: int
788: server_client_msg_dispatch(struct client *c)
789: {
790: struct imsg imsg;
791: struct msg_command_data commanddata;
792: struct msg_identify_data identifydata;
793: struct msg_environ_data environdata;
1.73 nicm 794: struct msg_stdin_data stdindata;
1.1 nicm 795: ssize_t n, datalen;
796:
1.8 deraadt 797: if ((n = imsg_read(&c->ibuf)) == -1 || n == 0)
798: return (-1);
1.1 nicm 799:
800: for (;;) {
801: if ((n = imsg_get(&c->ibuf, &imsg)) == -1)
802: return (-1);
803: if (n == 0)
804: return (0);
805: datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
806:
807: if (imsg.hdr.peerid != PROTOCOL_VERSION) {
808: server_write_client(c, MSG_VERSION, NULL, 0);
809: c->flags |= CLIENT_BAD;
810: imsg_free(&imsg);
811: continue;
812: }
813:
814: log_debug("got %d from client %d", imsg.hdr.type, c->ibuf.fd);
815: switch (imsg.hdr.type) {
816: case MSG_COMMAND:
817: if (datalen != sizeof commanddata)
818: fatalx("bad MSG_COMMAND size");
819: memcpy(&commanddata, imsg.data, sizeof commanddata);
820:
821: server_client_msg_command(c, &commanddata);
822: break;
823: case MSG_IDENTIFY:
824: if (datalen != sizeof identifydata)
825: fatalx("bad MSG_IDENTIFY size");
826: if (imsg.fd == -1)
827: fatalx("MSG_IDENTIFY missing fd");
828: memcpy(&identifydata, imsg.data, sizeof identifydata);
829:
830: server_client_msg_identify(c, &identifydata, imsg.fd);
831: break;
1.73 nicm 832: case MSG_STDIN:
833: if (datalen != sizeof stdindata)
834: fatalx("bad MSG_STDIN size");
835: memcpy(&stdindata, imsg.data, sizeof stdindata);
1.36 nicm 836:
1.73 nicm 837: if (c->stdin_callback == NULL)
838: break;
839: if (stdindata.size <= 0)
840: c->stdin_closed = 1;
841: else {
842: evbuffer_add(c->stdin_data, stdindata.data,
843: stdindata.size);
844: }
845: c->stdin_callback(c, c->stdin_closed,
846: c->stdin_callback_data);
1.33 nicm 847: break;
1.1 nicm 848: case MSG_RESIZE:
849: if (datalen != 0)
850: fatalx("bad MSG_RESIZE size");
851:
1.86 nicm 852: if (c->flags & CLIENT_CONTROL)
853: break;
1.32 nicm 854: if (tty_resize(&c->tty)) {
855: recalculate_sizes();
856: server_redraw_client(c);
857: }
1.1 nicm 858: break;
859: case MSG_EXITING:
860: if (datalen != 0)
861: fatalx("bad MSG_EXITING size");
862:
863: c->session = NULL;
864: tty_close(&c->tty);
865: server_write_client(c, MSG_EXITED, NULL, 0);
866: break;
867: case MSG_WAKEUP:
868: case MSG_UNLOCK:
869: if (datalen != 0)
870: fatalx("bad MSG_WAKEUP size");
871:
872: if (!(c->flags & CLIENT_SUSPENDED))
873: break;
874: c->flags &= ~CLIENT_SUSPENDED;
1.10 nicm 875:
1.11 nicm 876: if (gettimeofday(&c->activity_time, NULL) != 0)
877: fatal("gettimeofday");
1.47 nicm 878: if (c->session != NULL)
879: session_update_activity(c->session);
1.10 nicm 880:
1.1 nicm 881: tty_start_tty(&c->tty);
882: server_redraw_client(c);
883: recalculate_sizes();
884: break;
885: case MSG_ENVIRON:
886: if (datalen != sizeof environdata)
887: fatalx("bad MSG_ENVIRON size");
888: memcpy(&environdata, imsg.data, sizeof environdata);
889:
890: environdata.var[(sizeof environdata.var) - 1] = '\0';
891: if (strchr(environdata.var, '=') != NULL)
892: environ_put(&c->environ, environdata.var);
893: break;
894: case MSG_SHELL:
895: if (datalen != 0)
896: fatalx("bad MSG_SHELL size");
897:
898: server_client_msg_shell(c);
899: break;
900: default:
901: fatalx("unexpected message");
902: }
903:
904: imsg_free(&imsg);
905: }
906: }
907:
908: /* Handle command message. */
909: void
910: server_client_msg_command(struct client *c, struct msg_command_data *data)
911: {
1.36 nicm 912: struct cmd_list *cmdlist = NULL;
913: int argc;
914: char **argv, *cause;
1.1 nicm 915:
916: argc = data->argc;
917: data->argv[(sizeof data->argv) - 1] = '\0';
918: if (cmd_unpack_argv(data->argv, sizeof data->argv, argc, &argv) != 0) {
1.94 nicm 919: cmdq_error(c->cmdq, "command too long");
1.1 nicm 920: goto error;
921: }
922:
923: if (argc == 0) {
924: argc = 1;
925: argv = xcalloc(1, sizeof *argv);
926: *argv = xstrdup("new-session");
927: }
928:
1.94 nicm 929: if ((cmdlist = cmd_list_parse(argc, argv, NULL, 0, &cause)) == NULL) {
930: cmdq_error(c->cmdq, "%s", cause);
1.1 nicm 931: cmd_free_argv(argc, argv);
932: goto error;
933: }
934: cmd_free_argv(argc, argv);
935:
1.94 nicm 936: cmdq_run(c->cmdq, cmdlist);
1.1 nicm 937: cmd_list_free(cmdlist);
938: return;
939:
940: error:
941: if (cmdlist != NULL)
942: cmd_list_free(cmdlist);
1.88 nicm 943:
1.36 nicm 944: c->flags |= CLIENT_EXIT;
1.1 nicm 945: }
946:
947: /* Handle identify message. */
948: void
949: server_client_msg_identify(
950: struct client *c, struct msg_identify_data *data, int fd)
951: {
952: c->cwd = NULL;
953: data->cwd[(sizeof data->cwd) - 1] = '\0';
954: if (*data->cwd != '\0')
955: c->cwd = xstrdup(data->cwd);
1.75 nicm 956:
957: if (data->flags & IDENTIFY_CONTROL) {
958: c->stdin_callback = control_callback;
1.86 nicm 959: c->flags |= CLIENT_CONTROL;
1.96 ! nicm 960: if (data->flags & IDENTIFY_TERMIOS)
! 961: evbuffer_add_printf(c->stdout_data, "\033P1000p");
1.79 nicm 962: server_write_client(c, MSG_STDIN, NULL, 0);
1.75 nicm 963:
964: c->tty.fd = -1;
965: c->tty.log_fd = -1;
966:
967: close(fd);
968: return;
969: }
1.1 nicm 970:
1.80 nicm 971: if (!isatty(fd)) {
972: close(fd);
973: return;
974: }
1.1 nicm 975: data->term[(sizeof data->term) - 1] = '\0';
1.74 nicm 976: tty_init(&c->tty, c, fd, data->term);
1.1 nicm 977: if (data->flags & IDENTIFY_UTF8)
978: c->tty.flags |= TTY_UTF8;
979: if (data->flags & IDENTIFY_256COLOURS)
980: c->tty.term_flags |= TERM_256COLOURS;
981: else if (data->flags & IDENTIFY_88COLOURS)
982: c->tty.term_flags |= TERM_88COLOURS;
983:
984: tty_resize(&c->tty);
985:
1.86 nicm 986: if (!(data->flags & IDENTIFY_CONTROL))
987: c->flags |= CLIENT_TERMINAL;
1.1 nicm 988: }
989:
990: /* Handle shell message. */
991: void
992: server_client_msg_shell(struct client *c)
993: {
994: struct msg_shell_data data;
995: const char *shell;
1.25 nicm 996:
1.1 nicm 997: shell = options_get_string(&global_s_options, "default-shell");
998:
999: if (*shell == '\0' || areshell(shell))
1000: shell = _PATH_BSHELL;
1001: if (strlcpy(data.shell, shell, sizeof data.shell) >= sizeof data.shell)
1002: strlcpy(data.shell, _PATH_BSHELL, sizeof data.shell);
1.25 nicm 1003:
1.1 nicm 1004: server_write_client(c, MSG_SHELL, &data, sizeof data);
1005: c->flags |= CLIENT_BAD; /* it will die after exec */
1006: }