Annotation of src/usr.bin/tmux/server-client.c, Revision 1.207
1.207 ! nicm 1: /* $OpenBSD: server-client.c,v 1.206 2017/01/11 22:36:07 nicm Exp $ */
1.1 nicm 2:
3: /*
1.181 nicm 4: * Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
1.1 nicm 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.162 nicm 21: #include <sys/uio.h>
1.1 nicm 22:
1.114 benno 23: #include <errno.h>
1.12 nicm 24: #include <event.h>
1.1 nicm 25: #include <fcntl.h>
1.162 nicm 26: #include <imsg.h>
1.98 nicm 27: #include <paths.h>
28: #include <stdlib.h>
1.1 nicm 29: #include <string.h>
1.4 nicm 30: #include <time.h>
1.1 nicm 31: #include <unistd.h>
32:
33: #include "tmux.h"
34:
1.190 nicm 35: static void server_client_free(int, short, void *);
36: static void server_client_check_focus(struct window_pane *);
37: static void server_client_check_resize(struct window_pane *);
38: static key_code server_client_check_mouse(struct client *);
39: static void server_client_repeat_timer(int, short, void *);
1.192 nicm 40: static void server_client_click_timer(int, short, void *);
1.190 nicm 41: static void server_client_check_exit(struct client *);
42: static void server_client_check_redraw(struct client *);
43: static void server_client_set_title(struct client *);
44: static void server_client_reset_state(struct client *);
45: static int server_client_assume_paste(struct session *);
46:
47: static void server_client_dispatch(struct imsg *, void *);
48: static void server_client_dispatch_command(struct client *, struct imsg *);
49: static void server_client_dispatch_identify(struct client *, struct imsg *);
50: static void server_client_dispatch_shell(struct client *);
1.140 nicm 51:
52: /* Check if this client is inside this server. */
53: int
54: server_client_check_nested(struct client *c)
55: {
56: struct environ_entry *envent;
57: struct window_pane *wp;
58:
59: if (c->tty.path == NULL)
60: return (0);
61:
1.164 nicm 62: envent = environ_find(c->environ, "TMUX");
1.140 nicm 63: if (envent == NULL || *envent->value == '\0')
64: return (0);
65:
66: RB_FOREACH(wp, window_pane_tree, &all_window_panes) {
67: if (strcmp(wp->tty, c->tty.path) == 0)
68: return (1);
69: }
70: return (0);
71: }
1.1 nicm 72:
1.132 nicm 73: /* Set client key table. */
74: void
1.178 nicm 75: server_client_set_key_table(struct client *c, const char *name)
1.132 nicm 76: {
1.178 nicm 77: if (name == NULL)
78: name = server_client_get_key_table(c);
79:
1.132 nicm 80: key_bindings_unref_table(c->keytable);
81: c->keytable = key_bindings_get_table(name, 1);
82: c->keytable->references++;
83: }
84:
1.178 nicm 85: /* Get default key table. */
86: const char *
87: server_client_get_key_table(struct client *c)
88: {
89: struct session *s = c->session;
90: const char *name;
91:
92: if (s == NULL)
93: return ("root");
94:
95: name = options_get_string(s->options, "key-table");
96: if (*name == '\0')
97: return ("root");
98: return (name);
99: }
100:
1.191 nicm 101: /* Is this client using the default key table? */
102: int
103: server_client_is_default_key_table(struct client *c)
104: {
105: return (strcmp(c->keytable->name, server_client_get_key_table(c)) == 0);
106: }
107:
1.1 nicm 108: /* Create a new client. */
109: void
110: server_client_create(int fd)
111: {
112: struct client *c;
113:
1.49 nicm 114: setblocking(fd, 0);
1.1 nicm 115:
116: c = xcalloc(1, sizeof *c);
1.141 nicm 117: c->references = 1;
1.162 nicm 118: c->peer = proc_add_peer(server_proc, fd, server_client_dispatch, c);
1.25 nicm 119:
1.10 nicm 120: if (gettimeofday(&c->creation_time, NULL) != 0)
1.1 nicm 121: fatal("gettimeofday failed");
1.11 nicm 122: memcpy(&c->activity_time, &c->creation_time, sizeof c->activity_time);
1.111 nicm 123:
1.164 nicm 124: c->environ = environ_create();
1.1 nicm 125:
1.146 nicm 126: c->fd = -1;
1.165 nicm 127: c->cwd = NULL;
1.145 nicm 128:
1.194 nicm 129: TAILQ_INIT(&c->queue);
1.94 nicm 130:
1.116 nicm 131: c->stdin_data = evbuffer_new();
132: c->stdout_data = evbuffer_new();
133: c->stderr_data = evbuffer_new();
1.33 nicm 134:
1.1 nicm 135: c->tty.fd = -1;
136: c->title = NULL;
137:
138: c->session = NULL;
1.45 nicm 139: c->last_session = NULL;
1.1 nicm 140: c->tty.sx = 80;
141: c->tty.sy = 24;
142:
143: screen_init(&c->status, c->tty.sx, 1, 0);
144:
145: c->message_string = NULL;
1.136 nicm 146: TAILQ_INIT(&c->message_log);
1.1 nicm 147:
148: c->prompt_string = NULL;
149: c->prompt_buffer = NULL;
150: c->prompt_index = 0;
151:
1.93 nicm 152: c->flags |= CLIENT_FOCUSED;
153:
1.132 nicm 154: c->keytable = key_bindings_get_table("root", 1);
155: c->keytable->references++;
156:
1.17 nicm 157: evtimer_set(&c->repeat_timer, server_client_repeat_timer, c);
1.192 nicm 158: evtimer_set(&c->click_timer, server_client_click_timer, c);
1.17 nicm 159:
1.135 nicm 160: TAILQ_INSERT_TAIL(&clients, c, entry);
1.157 nicm 161: log_debug("new client %p", c);
1.72 nicm 162: }
163:
164: /* Open client terminal if needed. */
165: int
1.119 nicm 166: server_client_open(struct client *c, char **cause)
1.72 nicm 167: {
1.75 nicm 168: if (c->flags & CLIENT_CONTROL)
169: return (0);
1.120 nicm 170:
171: if (strcmp(c->ttyname, "/dev/tty") == 0) {
172: *cause = xstrdup("can't use /dev/tty");
173: return (-1);
174: }
1.75 nicm 175:
1.72 nicm 176: if (!(c->flags & CLIENT_TERMINAL)) {
1.116 nicm 177: *cause = xstrdup("not a terminal");
1.72 nicm 178: return (-1);
179: }
180:
1.119 nicm 181: if (tty_open(&c->tty, cause) != 0)
1.72 nicm 182: return (-1);
183:
184: return (0);
1.1 nicm 185: }
186:
187: /* Lost a client. */
188: void
189: server_client_lost(struct client *c)
190: {
1.136 nicm 191: struct message_entry *msg, *msg1;
1.1 nicm 192:
1.141 nicm 193: c->flags |= CLIENT_DEAD;
194:
1.187 nicm 195: server_clear_identify(c, NULL);
1.141 nicm 196: status_prompt_clear(c);
197: status_message_clear(c);
198:
199: if (c->stdin_callback != NULL)
200: c->stdin_callback(c, 1, c->stdin_callback_data);
201:
1.135 nicm 202: TAILQ_REMOVE(&clients, c, entry);
1.157 nicm 203: log_debug("lost client %p", c);
1.1 nicm 204:
205: /*
206: * If CLIENT_TERMINAL hasn't been set, then tty_init hasn't been called
207: * and tty_free might close an unrelated fd.
208: */
209: if (c->flags & CLIENT_TERMINAL)
210: tty_free(&c->tty);
1.109 nicm 211: free(c->ttyname);
212: free(c->term);
1.1 nicm 213:
1.113 nicm 214: evbuffer_free(c->stdin_data);
215: evbuffer_free(c->stdout_data);
1.97 nicm 216: if (c->stderr_data != c->stdout_data)
1.126 nicm 217: evbuffer_free(c->stderr_data);
1.33 nicm 218:
1.148 nicm 219: if (event_initialized(&c->status_timer))
220: evtimer_del(&c->status_timer);
1.1 nicm 221: screen_free(&c->status);
222:
1.77 nicm 223: free(c->title);
1.165 nicm 224: free((void *)c->cwd);
1.1 nicm 225:
1.17 nicm 226: evtimer_del(&c->repeat_timer);
1.192 nicm 227: evtimer_del(&c->click_timer);
1.17 nicm 228:
1.132 nicm 229: key_bindings_unref_table(c->keytable);
230:
1.69 nicm 231: if (event_initialized(&c->identify_timer))
232: evtimer_del(&c->identify_timer);
1.15 nicm 233:
1.77 nicm 234: free(c->message_string);
1.116 nicm 235: if (event_initialized(&c->message_timer))
1.69 nicm 236: evtimer_del(&c->message_timer);
1.136 nicm 237: TAILQ_FOREACH_SAFE(msg, &c->message_log, entry, msg1) {
1.77 nicm 238: free(msg->msg);
1.136 nicm 239: TAILQ_REMOVE(&c->message_log, msg, entry);
240: free(msg);
1.21 nicm 241: }
1.1 nicm 242:
1.77 nicm 243: free(c->prompt_string);
244: free(c->prompt_buffer);
1.61 nicm 245:
1.164 nicm 246: environ_free(c->environ);
1.1 nicm 247:
1.162 nicm 248: proc_remove_peer(c->peer);
249: c->peer = NULL;
1.13 nicm 250:
1.142 nicm 251: server_client_unref(c);
1.71 nicm 252:
253: server_add_accept(0); /* may be more file descriptors now */
1.1 nicm 254:
255: recalculate_sizes();
1.41 nicm 256: server_check_unattached();
1.19 nicm 257: server_update_socket();
1.141 nicm 258: }
259:
260: /* Remove reference from a client. */
261: void
1.142 nicm 262: server_client_unref(struct client *c)
1.141 nicm 263: {
1.157 nicm 264: log_debug("unref client %p (%d references)", c, c->references);
1.141 nicm 265:
266: c->references--;
267: if (c->references == 0)
268: event_once(-1, EV_TIMEOUT, server_client_free, c, NULL);
269: }
270:
271: /* Free dead client. */
1.190 nicm 272: static void
1.170 nicm 273: server_client_free(__unused int fd, __unused short events, void *arg)
1.141 nicm 274: {
275: struct client *c = arg;
276:
1.157 nicm 277: log_debug("free client %p (%d references)", c, c->references);
1.141 nicm 278:
1.194 nicm 279: if (!TAILQ_EMPTY(&c->queue))
280: fatalx("queue not empty");
281:
1.141 nicm 282: if (c->references == 0)
283: free(c);
1.9 nicm 284: }
285:
1.174 nicm 286: /* Detach a client. */
287: void
288: server_client_detach(struct client *c, enum msgtype msgtype)
289: {
290: struct session *s = c->session;
291:
292: if (s == NULL)
293: return;
294:
1.196 nicm 295: notify_client("client-detached", c);
1.174 nicm 296: proc_send_s(c->peer, msgtype, s->name);
1.207 ! nicm 297: }
! 298:
! 299: /* Execute command to replace a client, */
! 300: void
! 301: server_client_exec(struct client *c, const char *cmd)
! 302: {
! 303: struct session *s = c->session;
! 304: char *msg, *shell;
! 305: size_t cmdsize, shellsize;
! 306:
! 307: if (*cmd == '\0')
! 308: return;
! 309: cmdsize = strlen(cmd) + 1;
! 310:
! 311: if (s != NULL)
! 312: shell = options_get_string(s->options, "default-shell");
! 313: else
! 314: shell = options_get_string(global_s_options, "default-shell");
! 315: shellsize = strlen(shell) + 1;
! 316:
! 317: msg = xmalloc(cmdsize + shellsize);
! 318: memcpy(msg, cmd, cmdsize);
! 319: memcpy(msg + cmdsize, shell, shellsize);
! 320:
! 321: proc_send(c->peer, MSG_EXEC, -1, msg, cmdsize + shellsize);
! 322: free(msg);
1.174 nicm 323: }
324:
1.66 nicm 325: /* Check for mouse keys. */
1.190 nicm 326: static key_code
1.131 nicm 327: server_client_check_mouse(struct client *c)
1.66 nicm 328: {
1.192 nicm 329: struct session *s = c->session;
330: struct mouse_event *m = &c->tty.mouse;
331: struct window *w;
332: struct window_pane *wp;
333: u_int x, y, b;
334: int flag;
335: key_code key;
336: struct timeval tv;
337: enum { NOTYPE, DOWN, UP, DRAG, WHEEL, DOUBLE, TRIPLE } type = NOTYPE;
338: enum { NOWHERE, PANE, STATUS, BORDER } where = NOWHERE;
1.131 nicm 339:
340: log_debug("mouse %02x at %u,%u (last %u,%u) (%d)", m->b, m->x, m->y,
341: m->lx, m->ly, c->tty.mouse_drag_flag);
342:
343: /* What type of event is this? */
344: if (MOUSE_DRAG(m->b)) {
345: type = DRAG;
346: if (c->tty.mouse_drag_flag) {
347: x = m->x, y = m->y, b = m->b;
348: log_debug("drag update at %u,%u", x, y);
349: } else {
350: x = m->lx, y = m->ly, b = m->lb;
351: log_debug("drag start at %u,%u", x, y);
352: }
353: } else if (MOUSE_WHEEL(m->b)) {
354: type = WHEEL;
355: x = m->x, y = m->y, b = m->b;
356: log_debug("wheel at %u,%u", x, y);
1.200 nicm 357: } else if (MOUSE_RELEASE(m->b)) {
1.131 nicm 358: type = UP;
359: x = m->x, y = m->y, b = m->lb;
360: log_debug("up at %u,%u", x, y);
361: } else {
1.192 nicm 362: if (c->flags & CLIENT_DOUBLECLICK) {
363: evtimer_del(&c->click_timer);
364: c->flags &= ~CLIENT_DOUBLECLICK;
365: if (m->b == c->click_button) {
366: type = DOUBLE;
367: x = m->x, y = m->y, b = m->b;
368: log_debug("double-click at %u,%u", x, y);
369: flag = CLIENT_TRIPLECLICK;
370: goto add_timer;
371: }
372: } else if (c->flags & CLIENT_TRIPLECLICK) {
373: evtimer_del(&c->click_timer);
374: c->flags &= ~CLIENT_TRIPLECLICK;
375: if (m->b == c->click_button) {
376: type = TRIPLE;
377: x = m->x, y = m->y, b = m->b;
378: log_debug("triple-click at %u,%u", x, y);
379: goto have_event;
380: }
381: }
382:
1.131 nicm 383: type = DOWN;
384: x = m->x, y = m->y, b = m->b;
385: log_debug("down at %u,%u", x, y);
1.192 nicm 386: flag = CLIENT_DOUBLECLICK;
387:
388: add_timer:
389: if (KEYC_CLICK_TIMEOUT != 0) {
390: c->flags |= flag;
391: c->click_button = m->b;
392:
393: tv.tv_sec = KEYC_CLICK_TIMEOUT / 1000;
394: tv.tv_usec = (KEYC_CLICK_TIMEOUT % 1000) * 1000L;
395: evtimer_del(&c->click_timer);
396: evtimer_add(&c->click_timer, &tv);
397: }
1.131 nicm 398: }
1.192 nicm 399:
400: have_event:
1.131 nicm 401: if (type == NOTYPE)
1.177 nicm 402: return (KEYC_UNKNOWN);
1.131 nicm 403:
404: /* Always save the session. */
405: m->s = s->id;
406:
407: /* Is this on the status line? */
408: m->statusat = status_at_line(c);
409: if (m->statusat != -1 && y == (u_int)m->statusat) {
410: w = status_get_window_at(c, x);
411: if (w == NULL)
1.177 nicm 412: return (KEYC_UNKNOWN);
1.131 nicm 413: m->w = w->id;
414: where = STATUS;
415: } else
416: m->w = -1;
417:
418: /* Not on status line. Adjust position and check for border or pane. */
419: if (where == NOWHERE) {
420: if (m->statusat == 0 && y > 0)
421: y--;
422: else if (m->statusat > 0 && y >= (u_int)m->statusat)
423: y = m->statusat - 1;
424:
425: TAILQ_FOREACH(wp, &s->curw->window->panes, entry) {
426: if ((wp->xoff + wp->sx == x &&
427: wp->yoff <= 1 + y &&
428: wp->yoff + wp->sy >= y) ||
429: (wp->yoff + wp->sy == y &&
430: wp->xoff <= 1 + x &&
431: wp->xoff + wp->sx >= x))
432: break;
433: }
434: if (wp != NULL)
435: where = BORDER;
436: else {
437: wp = window_get_active_at(s->curw->window, x, y);
1.173 nicm 438: if (wp != NULL) {
1.131 nicm 439: where = PANE;
1.173 nicm 440: log_debug("mouse at %u,%u is on pane %%%u",
441: x, y, wp->id);
442: }
1.131 nicm 443: }
444: if (where == NOWHERE)
1.177 nicm 445: return (KEYC_UNKNOWN);
1.131 nicm 446: m->wp = wp->id;
447: m->w = wp->window->id;
448: } else
449: m->wp = -1;
450:
451: /* Stop dragging if needed. */
1.200 nicm 452: if (type != DRAG && type != WHEEL && c->tty.mouse_drag_flag) {
1.131 nicm 453: if (c->tty.mouse_drag_release != NULL)
454: c->tty.mouse_drag_release(c, m);
455:
456: c->tty.mouse_drag_update = NULL;
457: c->tty.mouse_drag_release = NULL;
458:
1.182 nicm 459: /*
1.183 nicm 460: * End a mouse drag by passing a MouseDragEnd key corresponding
461: * to the button that started the drag.
1.182 nicm 462: */
463: switch (c->tty.mouse_drag_flag) {
464: case 1:
465: if (where == PANE)
1.183 nicm 466: key = KEYC_MOUSEDRAGEND1_PANE;
1.182 nicm 467: if (where == STATUS)
1.183 nicm 468: key = KEYC_MOUSEDRAGEND1_STATUS;
1.182 nicm 469: if (where == BORDER)
1.183 nicm 470: key = KEYC_MOUSEDRAGEND1_BORDER;
1.182 nicm 471: break;
472: case 2:
473: if (where == PANE)
1.183 nicm 474: key = KEYC_MOUSEDRAGEND2_PANE;
1.182 nicm 475: if (where == STATUS)
1.183 nicm 476: key = KEYC_MOUSEDRAGEND2_STATUS;
1.182 nicm 477: if (where == BORDER)
1.183 nicm 478: key = KEYC_MOUSEDRAGEND2_BORDER;
1.182 nicm 479: break;
480: case 3:
481: if (where == PANE)
1.183 nicm 482: key = KEYC_MOUSEDRAGEND3_PANE;
1.182 nicm 483: if (where == STATUS)
1.183 nicm 484: key = KEYC_MOUSEDRAGEND3_STATUS;
1.182 nicm 485: if (where == BORDER)
1.183 nicm 486: key = KEYC_MOUSEDRAGEND3_BORDER;
1.182 nicm 487: break;
488: default:
489: key = KEYC_MOUSE;
490: break;
491: }
1.131 nicm 492: c->tty.mouse_drag_flag = 0;
1.182 nicm 493:
494: return (key);
1.131 nicm 495: }
496:
497: /* Convert to a key binding. */
1.177 nicm 498: key = KEYC_UNKNOWN;
1.131 nicm 499: switch (type) {
500: case NOTYPE:
501: break;
502: case DRAG:
1.204 nicm 503: if (c->tty.mouse_drag_update != NULL)
504: key = KEYC_DRAGGING;
505: else {
1.131 nicm 506: switch (MOUSE_BUTTONS(b)) {
507: case 0:
508: if (where == PANE)
509: key = KEYC_MOUSEDRAG1_PANE;
510: if (where == STATUS)
511: key = KEYC_MOUSEDRAG1_STATUS;
512: if (where == BORDER)
513: key = KEYC_MOUSEDRAG1_BORDER;
514: break;
515: case 1:
516: if (where == PANE)
517: key = KEYC_MOUSEDRAG2_PANE;
518: if (where == STATUS)
519: key = KEYC_MOUSEDRAG2_STATUS;
520: if (where == BORDER)
521: key = KEYC_MOUSEDRAG2_BORDER;
522: break;
523: case 2:
524: if (where == PANE)
525: key = KEYC_MOUSEDRAG3_PANE;
526: if (where == STATUS)
527: key = KEYC_MOUSEDRAG3_STATUS;
528: if (where == BORDER)
529: key = KEYC_MOUSEDRAG3_BORDER;
530: break;
531: }
532: }
1.66 nicm 533:
1.182 nicm 534: /*
535: * Begin a drag by setting the flag to a non-zero value that
536: * corresponds to the mouse button in use.
537: */
538: c->tty.mouse_drag_flag = MOUSE_BUTTONS(b) + 1;
1.131 nicm 539: break;
540: case WHEEL:
541: if (MOUSE_BUTTONS(b) == MOUSE_WHEEL_UP) {
542: if (where == PANE)
543: key = KEYC_WHEELUP_PANE;
544: if (where == STATUS)
545: key = KEYC_WHEELUP_STATUS;
546: if (where == BORDER)
547: key = KEYC_WHEELUP_BORDER;
548: } else {
549: if (where == PANE)
550: key = KEYC_WHEELDOWN_PANE;
551: if (where == STATUS)
552: key = KEYC_WHEELDOWN_STATUS;
553: if (where == BORDER)
554: key = KEYC_WHEELDOWN_BORDER;
555: }
556: break;
557: case UP:
558: switch (MOUSE_BUTTONS(b)) {
559: case 0:
560: if (where == PANE)
561: key = KEYC_MOUSEUP1_PANE;
562: if (where == STATUS)
563: key = KEYC_MOUSEUP1_STATUS;
564: if (where == BORDER)
565: key = KEYC_MOUSEUP1_BORDER;
566: break;
567: case 1:
568: if (where == PANE)
569: key = KEYC_MOUSEUP2_PANE;
570: if (where == STATUS)
571: key = KEYC_MOUSEUP2_STATUS;
572: if (where == BORDER)
573: key = KEYC_MOUSEUP2_BORDER;
574: break;
575: case 2:
576: if (where == PANE)
577: key = KEYC_MOUSEUP3_PANE;
578: if (where == STATUS)
579: key = KEYC_MOUSEUP3_STATUS;
580: if (where == BORDER)
581: key = KEYC_MOUSEUP3_BORDER;
582: break;
583: }
584: break;
585: case DOWN:
586: switch (MOUSE_BUTTONS(b)) {
587: case 0:
588: if (where == PANE)
589: key = KEYC_MOUSEDOWN1_PANE;
590: if (where == STATUS)
591: key = KEYC_MOUSEDOWN1_STATUS;
592: if (where == BORDER)
593: key = KEYC_MOUSEDOWN1_BORDER;
594: break;
595: case 1:
596: if (where == PANE)
597: key = KEYC_MOUSEDOWN2_PANE;
598: if (where == STATUS)
599: key = KEYC_MOUSEDOWN2_STATUS;
600: if (where == BORDER)
601: key = KEYC_MOUSEDOWN2_BORDER;
602: break;
603: case 2:
604: if (where == PANE)
605: key = KEYC_MOUSEDOWN3_PANE;
606: if (where == STATUS)
607: key = KEYC_MOUSEDOWN3_STATUS;
608: if (where == BORDER)
609: key = KEYC_MOUSEDOWN3_BORDER;
610: break;
1.66 nicm 611: }
1.131 nicm 612: break;
1.192 nicm 613: case DOUBLE:
614: switch (MOUSE_BUTTONS(b)) {
615: case 0:
616: if (where == PANE)
617: key = KEYC_DOUBLECLICK1_PANE;
618: if (where == STATUS)
619: key = KEYC_DOUBLECLICK1_STATUS;
620: if (where == BORDER)
621: key = KEYC_DOUBLECLICK1_BORDER;
622: break;
623: case 1:
624: if (where == PANE)
625: key = KEYC_DOUBLECLICK2_PANE;
626: if (where == STATUS)
627: key = KEYC_DOUBLECLICK2_STATUS;
628: if (where == BORDER)
629: key = KEYC_DOUBLECLICK2_BORDER;
630: break;
631: case 2:
632: if (where == PANE)
633: key = KEYC_DOUBLECLICK3_PANE;
634: if (where == STATUS)
635: key = KEYC_DOUBLECLICK3_STATUS;
636: if (where == BORDER)
637: key = KEYC_DOUBLECLICK3_BORDER;
638: break;
639: }
640: break;
641: case TRIPLE:
642: switch (MOUSE_BUTTONS(b)) {
643: case 0:
644: if (where == PANE)
645: key = KEYC_TRIPLECLICK1_PANE;
646: if (where == STATUS)
647: key = KEYC_TRIPLECLICK1_STATUS;
648: if (where == BORDER)
649: key = KEYC_TRIPLECLICK1_BORDER;
650: break;
651: case 1:
652: if (where == PANE)
653: key = KEYC_TRIPLECLICK2_PANE;
654: if (where == STATUS)
655: key = KEYC_TRIPLECLICK2_STATUS;
656: if (where == BORDER)
657: key = KEYC_TRIPLECLICK2_BORDER;
658: break;
659: case 2:
660: if (where == PANE)
661: key = KEYC_TRIPLECLICK3_PANE;
662: if (where == STATUS)
663: key = KEYC_TRIPLECLICK3_STATUS;
664: if (where == BORDER)
665: key = KEYC_TRIPLECLICK3_BORDER;
666: break;
667: }
668: break;
1.66 nicm 669: }
1.177 nicm 670: if (key == KEYC_UNKNOWN)
671: return (KEYC_UNKNOWN);
1.66 nicm 672:
1.131 nicm 673: /* Apply modifiers if any. */
674: if (b & MOUSE_MASK_META)
675: key |= KEYC_ESCAPE;
676: if (b & MOUSE_MASK_CTRL)
677: key |= KEYC_CTRL;
678: if (b & MOUSE_MASK_SHIFT)
679: key |= KEYC_SHIFT;
1.66 nicm 680:
1.131 nicm 681: return (key);
1.66 nicm 682: }
683:
1.82 nicm 684: /* Is this fast enough to probably be a paste? */
1.190 nicm 685: static int
1.82 nicm 686: server_client_assume_paste(struct session *s)
687: {
688: struct timeval tv;
1.84 nicm 689: int t;
1.82 nicm 690:
1.163 nicm 691: if ((t = options_get_number(s->options, "assume-paste-time")) == 0)
1.83 nicm 692: return (0);
1.82 nicm 693:
694: timersub(&s->activity_time, &s->last_activity_time, &tv);
1.171 nicm 695: if (tv.tv_sec == 0 && tv.tv_usec < t * 1000) {
696: log_debug("session %s pasting (flag %d)", s->name,
697: !!(s->flags & SESSION_PASTING));
698: if (s->flags & SESSION_PASTING)
699: return (1);
700: s->flags |= SESSION_PASTING;
701: return (0);
702: }
703: log_debug("session %s not pasting", s->name);
704: s->flags &= ~SESSION_PASTING;
1.83 nicm 705: return (0);
1.82 nicm 706: }
707:
1.18 nicm 708: /* Handle data key input from client. */
709: void
1.168 nicm 710: server_client_handle_key(struct client *c, key_code key)
1.18 nicm 711: {
1.131 nicm 712: struct mouse_event *m = &c->tty.mouse;
1.132 nicm 713: struct session *s = c->session;
1.18 nicm 714: struct window *w;
715: struct window_pane *wp;
716: struct timeval tv;
1.191 nicm 717: const char *name;
1.156 nicm 718: struct key_table *table;
1.132 nicm 719: struct key_binding bd_find, *bd;
720: int xtimeout;
1.201 nicm 721: struct cmd_find_state fs;
1.18 nicm 722:
723: /* Check the client is good to accept input. */
1.132 nicm 724: if (s == NULL || (c->flags & (CLIENT_DEAD|CLIENT_SUSPENDED)) != 0)
1.18 nicm 725: return;
1.132 nicm 726: w = s->curw->window;
1.18 nicm 727:
728: /* Update the activity timer. */
729: if (gettimeofday(&c->activity_time, NULL) != 0)
730: fatal("gettimeofday failed");
1.149 nicm 731: session_update_activity(s, &c->activity_time);
1.18 nicm 732:
1.132 nicm 733: /* Number keys jump to pane in identify mode. */
1.25 nicm 734: if (c->flags & CLIENT_IDENTIFY && key >= '0' && key <= '9') {
1.30 nicm 735: if (c->flags & CLIENT_READONLY)
736: return;
1.95 nicm 737: window_unzoom(w);
1.18 nicm 738: wp = window_pane_at_index(w, key - '0');
1.187 nicm 739: if (wp != NULL && !window_pane_visible(wp))
740: wp = NULL;
741: server_clear_identify(c, wp);
1.18 nicm 742: return;
743: }
744:
745: /* Handle status line. */
1.30 nicm 746: if (!(c->flags & CLIENT_READONLY)) {
747: status_message_clear(c);
1.187 nicm 748: server_clear_identify(c, NULL);
1.30 nicm 749: }
1.18 nicm 750: if (c->prompt_string != NULL) {
1.193 nicm 751: if (c->flags & CLIENT_READONLY)
752: return;
753: if (status_prompt_key(c, key) == 0)
754: return;
1.18 nicm 755: }
756:
757: /* Check for mouse keys. */
1.204 nicm 758: m->valid = 0;
1.18 nicm 759: if (key == KEYC_MOUSE) {
1.30 nicm 760: if (c->flags & CLIENT_READONLY)
761: return;
1.131 nicm 762: key = server_client_check_mouse(c);
1.177 nicm 763: if (key == KEYC_UNKNOWN)
1.131 nicm 764: return;
765:
766: m->valid = 1;
767: m->key = key;
1.203 nicm 768:
769: /*
1.204 nicm 770: * Mouse drag is in progress, so fire the callback (now that
771: * the mouse event is valid).
1.203 nicm 772: */
1.204 nicm 773: if (key == KEYC_DRAGGING) {
774: c->tty.mouse_drag_update(c, m);
1.203 nicm 775: return;
1.204 nicm 776: }
1.131 nicm 777: } else
778: m->valid = 0;
1.18 nicm 779:
1.202 nicm 780: /* Find affected pane. */
781: if (KEYC_IS_MOUSE(key) && m->valid)
782: wp = cmd_mouse_pane(m, NULL, NULL);
783: else
784: wp = w->active;
785:
786: /* Forward mouse keys if disabled. */
1.206 nicm 787: if (KEYC_IS_MOUSE(key) && !options_get_number(s->options, "mouse"))
1.202 nicm 788: goto forward;
789:
1.132 nicm 790: /* Treat everything as a regular key when pasting is detected. */
1.161 nicm 791: if (!KEYC_IS_MOUSE(key) && server_client_assume_paste(s))
792: goto forward;
1.18 nicm 793:
1.132 nicm 794: retry:
1.191 nicm 795: /*
796: * Work out the current key table. If the pane is in a mode, use
797: * the mode table instead of the default key table.
798: */
799: name = NULL;
800: if (wp != NULL && wp->mode != NULL && wp->mode->key_table != NULL)
801: name = wp->mode->key_table(wp);
802: if (name == NULL || !server_client_is_default_key_table(c))
803: table = c->keytable;
804: else
805: table = key_bindings_get_table(name, 1);
1.202 nicm 806: if (wp == NULL)
807: log_debug("key table %s (no pane)", table->name);
808: else
809: log_debug("key table %s (pane %%%u)", table->name, wp->id);
1.191 nicm 810:
1.205 nicm 811: /*
812: * The prefix always takes precedence and forces a switch to the prefix
813: * table, unless we are already there.
814: */
815: if ((key == (key_code)options_get_number(s->options, "prefix") ||
816: key == (key_code)options_get_number(s->options, "prefix2")) &&
817: strcmp(table->name, "prefix") != 0) {
818: server_client_set_key_table(c, "prefix");
819: server_status_client(c);
820: return;
821: }
822:
1.132 nicm 823: /* Try to see if there is a key binding in the current table. */
824: bd_find.key = key;
1.191 nicm 825: bd = RB_FIND(key_bindings, &table->key_bindings, &bd_find);
1.132 nicm 826: if (bd != NULL) {
827: /*
828: * Key was matched in this table. If currently repeating but a
829: * non-repeating binding was found, stop repeating and try
830: * again in the root table.
831: */
832: if ((c->flags & CLIENT_REPEAT) && !bd->can_repeat) {
1.178 nicm 833: server_client_set_key_table(c, NULL);
1.132 nicm 834: c->flags &= ~CLIENT_REPEAT;
1.85 nicm 835: server_status_client(c);
1.132 nicm 836: goto retry;
1.18 nicm 837: }
1.82 nicm 838:
1.132 nicm 839: /*
840: * Take a reference to this table to make sure the key binding
841: * doesn't disappear.
842: */
843: table->references++;
844:
845: /*
846: * If this is a repeating key, start the timer. Otherwise reset
847: * the client back to the root table.
848: */
1.163 nicm 849: xtimeout = options_get_number(s->options, "repeat-time");
1.132 nicm 850: if (xtimeout != 0 && bd->can_repeat) {
851: c->flags |= CLIENT_REPEAT;
852:
853: tv.tv_sec = xtimeout / 1000;
854: tv.tv_usec = (xtimeout % 1000) * 1000L;
855: evtimer_del(&c->repeat_timer);
856: evtimer_add(&c->repeat_timer, &tv);
857: } else {
1.18 nicm 858: c->flags &= ~CLIENT_REPEAT;
1.178 nicm 859: server_client_set_key_table(c, NULL);
1.18 nicm 860: }
1.132 nicm 861: server_status_client(c);
862:
1.201 nicm 863: /* Find default state if the pane is known. */
864: cmd_find_clear_state(&fs, NULL, 0);
865: if (wp != NULL) {
866: fs.s = s;
867: fs.wl = fs.s->curw;
868: fs.w = fs.wl->window;
869: fs.wp = wp;
870: cmd_find_log_state(__func__, &fs);
871:
872: if (!cmd_find_valid_state(&fs))
873: fatalx("invalid key state");
874: }
875:
1.132 nicm 876: /* Dispatch the key binding. */
1.201 nicm 877: key_bindings_dispatch(bd, c, m, &fs);
1.132 nicm 878: key_bindings_unref_table(table);
1.18 nicm 879: return;
880: }
881:
1.132 nicm 882: /*
883: * No match in this table. If repeating, switch the client back to the
884: * root table and try again.
885: */
886: if (c->flags & CLIENT_REPEAT) {
1.178 nicm 887: server_client_set_key_table(c, NULL);
1.18 nicm 888: c->flags &= ~CLIENT_REPEAT;
1.132 nicm 889: server_status_client(c);
890: goto retry;
1.18 nicm 891: }
892:
1.132 nicm 893: /* If no match and we're not in the root table, that's it. */
1.191 nicm 894: if (name == NULL && !server_client_is_default_key_table(c)) {
1.205 nicm 895: log_debug("no key in key table %s", table->name);
1.178 nicm 896: server_client_set_key_table(c, NULL);
1.132 nicm 897: server_status_client(c);
1.161 nicm 898: return;
899: }
900:
901: forward:
902: if (c->flags & CLIENT_READONLY)
903: return;
904: if (wp != NULL)
1.132 nicm 905: window_pane_key(wp, c, s, key, m);
1.18 nicm 906: }
907:
1.2 nicm 908: /* Client functions that need to happen every loop. */
909: void
910: server_client_loop(void)
911: {
912: struct client *c;
913: struct window *w;
914: struct window_pane *wp;
1.197 nicm 915: int focus;
1.2 nicm 916:
1.135 nicm 917: TAILQ_FOREACH(c, &clients, entry) {
1.36 nicm 918: server_client_check_exit(c);
919: if (c->session != NULL) {
920: server_client_check_redraw(c);
921: server_client_reset_state(c);
922: }
1.2 nicm 923: }
924:
925: /*
926: * Any windows will have been redrawn as part of clients, so clear
1.92 nicm 927: * their flags now. Also check pane focus and resize.
1.2 nicm 928: */
1.197 nicm 929: focus = options_get_number(global_options, "focus-events");
1.134 nicm 930: RB_FOREACH(w, windows, &windows) {
1.2 nicm 931: w->flags &= ~WINDOW_REDRAW;
1.91 nicm 932: TAILQ_FOREACH(wp, &w->panes, entry) {
1.101 nicm 933: if (wp->fd != -1) {
1.197 nicm 934: if (focus)
935: server_client_check_focus(wp);
1.101 nicm 936: server_client_check_resize(wp);
937: }
1.2 nicm 938: wp->flags &= ~PANE_REDRAW;
1.91 nicm 939: }
1.150 nicm 940: check_window_name(w);
1.2 nicm 941: }
1.92 nicm 942: }
943:
1.188 nicm 944: static void
945: server_client_resize_event(__unused int fd, __unused short events, void *data)
1.92 nicm 946: {
1.188 nicm 947: struct window_pane *wp = data;
948: struct winsize ws;
949:
950: evtimer_del(&wp->resize_timer);
1.92 nicm 951:
1.101 nicm 952: if (!(wp->flags & PANE_RESIZE))
1.92 nicm 953: return;
954:
955: memset(&ws, 0, sizeof ws);
956: ws.ws_col = wp->sx;
957: ws.ws_row = wp->sy;
958:
1.100 nicm 959: if (ioctl(wp->fd, TIOCSWINSZ, &ws) == -1)
1.92 nicm 960: fatal("ioctl failed");
961:
962: wp->flags &= ~PANE_RESIZE;
1.188 nicm 963: }
964:
965: /* Check if pane should be resized. */
1.190 nicm 966: static void
1.188 nicm 967: server_client_check_resize(struct window_pane *wp)
968: {
969: struct timeval tv = { .tv_usec = 250000 };
970:
971: if (!(wp->flags & PANE_RESIZE))
972: return;
973:
974: if (!event_initialized(&wp->resize_timer))
975: evtimer_set(&wp->resize_timer, server_client_resize_event, wp);
976:
977: /*
978: * The first resize should happen immediately, so if the timer is not
979: * running, do it now.
980: */
981: if (!evtimer_pending(&wp->resize_timer, NULL))
982: server_client_resize_event(-1, 0, wp);
983:
984: /*
985: * If the pane is in the alternate screen, let the timer expire and
986: * resize to give the application a chance to redraw. If not, keep
987: * pushing the timer back.
988: */
989: if (wp->saved_grid != NULL && evtimer_pending(&wp->resize_timer, NULL))
990: return;
991: evtimer_del(&wp->resize_timer);
992: evtimer_add(&wp->resize_timer, &tv);
1.91 nicm 993: }
994:
995: /* Check whether pane should be focused. */
1.190 nicm 996: static void
1.91 nicm 997: server_client_check_focus(struct window_pane *wp)
998: {
1.93 nicm 999: struct client *c;
1.102 nicm 1000: int push;
1001:
1002: /* Do we need to push the focus state? */
1003: push = wp->flags & PANE_FOCUSPUSH;
1004: wp->flags &= ~PANE_FOCUSPUSH;
1.91 nicm 1005:
1006: /* If we don't care about focus, forget it. */
1007: if (!(wp->base.mode & MODE_FOCUSON))
1008: return;
1009:
1010: /* If we're not the active pane in our window, we're not focused. */
1011: if (wp->window->active != wp)
1012: goto not_focused;
1013:
1014: /* If we're in a mode, we're not focused. */
1015: if (wp->screen != &wp->base)
1016: goto not_focused;
1017:
1018: /*
1.93 nicm 1019: * If our window is the current window in any focused clients with an
1020: * attached session, we're focused.
1.91 nicm 1021: */
1.135 nicm 1022: TAILQ_FOREACH(c, &clients, entry) {
1023: if (c->session == NULL || !(c->flags & CLIENT_FOCUSED))
1.91 nicm 1024: continue;
1.93 nicm 1025: if (c->session->flags & SESSION_UNATTACHED)
1026: continue;
1027:
1028: if (c->session->curw->window == wp->window)
1.91 nicm 1029: goto focused;
1030: }
1031:
1032: not_focused:
1.102 nicm 1033: if (push || (wp->flags & PANE_FOCUSED))
1.91 nicm 1034: bufferevent_write(wp->event, "\033[O", 3);
1035: wp->flags &= ~PANE_FOCUSED;
1036: return;
1037:
1038: focused:
1.102 nicm 1039: if (push || !(wp->flags & PANE_FOCUSED))
1.91 nicm 1040: bufferevent_write(wp->event, "\033[I", 3);
1041: wp->flags |= PANE_FOCUSED;
1.2 nicm 1042: }
1043:
1.18 nicm 1044: /*
1045: * Update cursor position and mode settings. The scroll region and attributes
1046: * are cleared when idle (waiting for an event) as this is the most likely time
1047: * a user may interrupt tmux, for example with ~^Z in ssh(1). This is a
1048: * compromise between excessive resets and likelihood of an interrupt.
1049: *
1050: * tty_region/tty_reset/tty_update_mode already take care of not resetting
1051: * things that are already in their default state.
1052: */
1.190 nicm 1053: static void
1.18 nicm 1054: server_client_reset_state(struct client *c)
1.1 nicm 1055: {
1.18 nicm 1056: struct window *w = c->session->curw->window;
1057: struct window_pane *wp = w->active;
1058: struct screen *s = wp->screen;
1.163 nicm 1059: struct options *oo = c->session->options;
1.66 nicm 1060: int status, mode, o;
1.60 nicm 1061:
1.186 nicm 1062: if (c->flags & (CLIENT_CONTROL|CLIENT_SUSPENDED))
1.86 nicm 1063: return;
1064:
1.199 nicm 1065: tty_region_off(&c->tty);
1066: tty_margin_off(&c->tty);
1.1 nicm 1067:
1068: status = options_get_number(oo, "status");
1069: if (!window_pane_visible(wp) || wp->yoff + s->cy >= c->tty.sy - status)
1070: tty_cursor(&c->tty, 0, 0);
1.66 nicm 1071: else {
1.126 nicm 1072: o = status && options_get_number(oo, "status-position") == 0;
1.66 nicm 1073: tty_cursor(&c->tty, wp->xoff + s->cx, o + wp->yoff + s->cy);
1074: }
1.1 nicm 1075:
1.50 nicm 1076: /*
1.131 nicm 1077: * Set mouse mode if requested. To support dragging, always use button
1078: * mode.
1.57 nicm 1079: */
1080: mode = s->mode;
1.131 nicm 1081: if (options_get_number(oo, "mouse"))
1082: mode = (mode & ~ALL_MOUSE_MODES) | MODE_MOUSE_BUTTON;
1.48 nicm 1083:
1084: /* Set the terminal mode and reset attributes. */
1.59 nicm 1085: tty_update_mode(&c->tty, mode, s);
1.1 nicm 1086: tty_reset(&c->tty);
1.17 nicm 1087: }
1088:
1089: /* Repeat time callback. */
1.190 nicm 1090: static void
1.170 nicm 1091: server_client_repeat_timer(__unused int fd, __unused short events, void *data)
1.17 nicm 1092: {
1093: struct client *c = data;
1094:
1.85 nicm 1095: if (c->flags & CLIENT_REPEAT) {
1.178 nicm 1096: server_client_set_key_table(c, NULL);
1.132 nicm 1097: c->flags &= ~CLIENT_REPEAT;
1098: server_status_client(c);
1.85 nicm 1099: }
1.192 nicm 1100: }
1101:
1102: /* Double-click callback. */
1103: static void
1104: server_client_click_timer(__unused int fd, __unused short events, void *data)
1105: {
1106: struct client *c = data;
1107:
1108: c->flags &= ~(CLIENT_DOUBLECLICK|CLIENT_TRIPLECLICK);
1.1 nicm 1109: }
1110:
1.36 nicm 1111: /* Check if client should be exited. */
1.190 nicm 1112: static void
1.36 nicm 1113: server_client_check_exit(struct client *c)
1114: {
1115: if (!(c->flags & CLIENT_EXIT))
1116: return;
1117:
1.73 nicm 1118: if (EVBUFFER_LENGTH(c->stdin_data) != 0)
1119: return;
1120: if (EVBUFFER_LENGTH(c->stdout_data) != 0)
1.36 nicm 1121: return;
1.73 nicm 1122: if (EVBUFFER_LENGTH(c->stderr_data) != 0)
1.36 nicm 1123: return;
1124:
1.162 nicm 1125: proc_send(c->peer, MSG_EXIT, -1, &c->retval, sizeof c->retval);
1.36 nicm 1126: c->flags &= ~CLIENT_EXIT;
1.38 nicm 1127: }
1128:
1.1 nicm 1129: /* Check for client redraws. */
1.190 nicm 1130: static void
1.1 nicm 1131: server_client_check_redraw(struct client *c)
1132: {
1133: struct session *s = c->session;
1.137 nicm 1134: struct tty *tty = &c->tty;
1.1 nicm 1135: struct window_pane *wp;
1.189 nicm 1136: int flags, masked;
1.1 nicm 1137:
1.86 nicm 1138: if (c->flags & (CLIENT_CONTROL|CLIENT_SUSPENDED))
1.79 nicm 1139: return;
1140:
1.1 nicm 1141: if (c->flags & (CLIENT_REDRAW|CLIENT_STATUS)) {
1.163 nicm 1142: if (options_get_number(s->options, "set-titles"))
1.1 nicm 1143: server_client_set_title(c);
1.189 nicm 1144: screen_redraw_update(c); /* will adjust flags */
1.1 nicm 1145: }
1146:
1.137 nicm 1147: flags = tty->flags & (TTY_FREEZE|TTY_NOCURSOR);
1148: tty->flags = (tty->flags & ~TTY_FREEZE) | TTY_NOCURSOR;
1149:
1.1 nicm 1150: if (c->flags & CLIENT_REDRAW) {
1.137 nicm 1151: tty_update_mode(tty, tty->mode, NULL);
1.115 nicm 1152: screen_redraw_screen(c, 1, 1, 1);
1.27 nicm 1153: c->flags &= ~(CLIENT_STATUS|CLIENT_BORDERS);
1.38 nicm 1154: } else if (c->flags & CLIENT_REDRAWWINDOW) {
1.137 nicm 1155: tty_update_mode(tty, tty->mode, NULL);
1.38 nicm 1156: TAILQ_FOREACH(wp, &c->session->curw->window->panes, entry)
1157: screen_redraw_pane(c, wp);
1158: c->flags &= ~CLIENT_REDRAWWINDOW;
1.1 nicm 1159: } else {
1160: TAILQ_FOREACH(wp, &c->session->curw->window->panes, entry) {
1.137 nicm 1161: if (wp->flags & PANE_REDRAW) {
1162: tty_update_mode(tty, tty->mode, NULL);
1.1 nicm 1163: screen_redraw_pane(c, wp);
1.137 nicm 1164: }
1.1 nicm 1165: }
1166: }
1167:
1.185 nicm 1168: masked = c->flags & (CLIENT_BORDERS|CLIENT_STATUS);
1169: if (masked != 0)
1.137 nicm 1170: tty_update_mode(tty, tty->mode, NULL);
1.185 nicm 1171: if (masked == CLIENT_BORDERS)
1.115 nicm 1172: screen_redraw_screen(c, 0, 0, 1);
1.185 nicm 1173: else if (masked == CLIENT_STATUS)
1.115 nicm 1174: screen_redraw_screen(c, 0, 1, 0);
1.185 nicm 1175: else if (masked != 0)
1176: screen_redraw_screen(c, 0, 1, 1);
1.1 nicm 1177:
1.137 nicm 1178: tty->flags = (tty->flags & ~(TTY_FREEZE|TTY_NOCURSOR)) | flags;
1179: tty_update_mode(tty, tty->mode, NULL);
1.1 nicm 1180:
1.153 nicm 1181: c->flags &= ~(CLIENT_REDRAW|CLIENT_BORDERS|CLIENT_STATUS|
1182: CLIENT_STATUSFORCE);
1.1 nicm 1183: }
1184:
1185: /* Set client title. */
1.190 nicm 1186: static void
1.1 nicm 1187: server_client_set_title(struct client *c)
1188: {
1.128 nicm 1189: struct session *s = c->session;
1190: const char *template;
1191: char *title;
1192: struct format_tree *ft;
1.1 nicm 1193:
1.163 nicm 1194: template = options_get_string(s->options, "set-titles-string");
1.25 nicm 1195:
1.176 nicm 1196: ft = format_create(NULL, 0);
1.128 nicm 1197: format_defaults(ft, c, NULL, NULL, NULL);
1198:
1199: title = format_expand_time(ft, template, time(NULL));
1.1 nicm 1200: if (c->title == NULL || strcmp(title, c->title) != 0) {
1.77 nicm 1201: free(c->title);
1.1 nicm 1202: c->title = xstrdup(title);
1203: tty_set_title(&c->tty, c->title);
1204: }
1.77 nicm 1205: free(title);
1.128 nicm 1206:
1207: format_free(ft);
1.1 nicm 1208: }
1209:
1210: /* Dispatch message from client. */
1.190 nicm 1211: static void
1.162 nicm 1212: server_client_dispatch(struct imsg *imsg, void *arg)
1.1 nicm 1213: {
1.162 nicm 1214: struct client *c = arg;
1.73 nicm 1215: struct msg_stdin_data stdindata;
1.108 nicm 1216: const char *data;
1.162 nicm 1217: ssize_t datalen;
1.149 nicm 1218: struct session *s;
1.1 nicm 1219:
1.162 nicm 1220: if (c->flags & CLIENT_DEAD)
1221: return;
1222:
1223: if (imsg == NULL) {
1224: server_client_lost(c);
1225: return;
1226: }
1.1 nicm 1227:
1.162 nicm 1228: data = imsg->data;
1229: datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
1.1 nicm 1230:
1.162 nicm 1231: switch (imsg->hdr.type) {
1232: case MSG_IDENTIFY_FLAGS:
1233: case MSG_IDENTIFY_TERM:
1234: case MSG_IDENTIFY_TTYNAME:
1235: case MSG_IDENTIFY_CWD:
1236: case MSG_IDENTIFY_STDIN:
1237: case MSG_IDENTIFY_ENVIRON:
1238: case MSG_IDENTIFY_CLIENTPID:
1239: case MSG_IDENTIFY_DONE:
1240: server_client_dispatch_identify(c, imsg);
1241: break;
1242: case MSG_COMMAND:
1243: server_client_dispatch_command(c, imsg);
1244: break;
1245: case MSG_STDIN:
1246: if (datalen != sizeof stdindata)
1247: fatalx("bad MSG_STDIN size");
1248: memcpy(&stdindata, data, sizeof stdindata);
1.36 nicm 1249:
1.162 nicm 1250: if (c->stdin_callback == NULL)
1.33 nicm 1251: break;
1.162 nicm 1252: if (stdindata.size <= 0)
1253: c->stdin_closed = 1;
1254: else {
1255: evbuffer_add(c->stdin_data, stdindata.data,
1256: stdindata.size);
1257: }
1258: c->stdin_callback(c, c->stdin_closed,
1259: c->stdin_callback_data);
1260: break;
1261: case MSG_RESIZE:
1262: if (datalen != 0)
1263: fatalx("bad MSG_RESIZE size");
1.1 nicm 1264:
1.162 nicm 1265: if (c->flags & CLIENT_CONTROL)
1.1 nicm 1266: break;
1.162 nicm 1267: if (tty_resize(&c->tty)) {
1268: recalculate_sizes();
1269: server_redraw_client(c);
1270: }
1.174 nicm 1271: if (c->session != NULL)
1.196 nicm 1272: notify_client("client-resized", c);
1.162 nicm 1273: break;
1274: case MSG_EXITING:
1275: if (datalen != 0)
1276: fatalx("bad MSG_EXITING size");
1.1 nicm 1277:
1.162 nicm 1278: c->session = NULL;
1279: tty_close(&c->tty);
1280: proc_send(c->peer, MSG_EXITED, -1, NULL, 0);
1281: break;
1282: case MSG_WAKEUP:
1283: case MSG_UNLOCK:
1284: if (datalen != 0)
1285: fatalx("bad MSG_WAKEUP size");
1.121 nicm 1286:
1.162 nicm 1287: if (!(c->flags & CLIENT_SUSPENDED))
1288: break;
1289: c->flags &= ~CLIENT_SUSPENDED;
1.10 nicm 1290:
1.162 nicm 1291: if (c->tty.fd == -1) /* exited in the meantime */
1.1 nicm 1292: break;
1.162 nicm 1293: s = c->session;
1.1 nicm 1294:
1.162 nicm 1295: if (gettimeofday(&c->activity_time, NULL) != 0)
1296: fatal("gettimeofday failed");
1297:
1298: tty_start_tty(&c->tty);
1299: server_redraw_client(c);
1300: recalculate_sizes();
1.184 nicm 1301:
1302: if (s != NULL)
1303: session_update_activity(s, &c->activity_time);
1.162 nicm 1304: break;
1305: case MSG_SHELL:
1306: if (datalen != 0)
1307: fatalx("bad MSG_SHELL size");
1.1 nicm 1308:
1.162 nicm 1309: server_client_dispatch_shell(c);
1310: break;
1.1 nicm 1311: }
1312: }
1313:
1.194 nicm 1314: /* Callback when command is done. */
1315: static enum cmd_retval
1.195 nicm 1316: server_client_command_done(struct cmdq_item *item, __unused void *data)
1.194 nicm 1317: {
1.195 nicm 1318: struct client *c = item->client;
1.194 nicm 1319:
1320: if (~c->flags & CLIENT_ATTACHED)
1321: c->flags |= CLIENT_EXIT;
1322: return (CMD_RETURN_NORMAL);
1323: }
1324:
1325: /* Show an error message. */
1326: static enum cmd_retval
1.195 nicm 1327: server_client_command_error(struct cmdq_item *item, void *data)
1.194 nicm 1328: {
1329: char *error = data;
1330:
1.195 nicm 1331: cmdq_error(item, "%s", error);
1.194 nicm 1332: free(error);
1333:
1334: return (CMD_RETURN_NORMAL);
1335: }
1336:
1.1 nicm 1337: /* Handle command message. */
1.190 nicm 1338: static void
1.162 nicm 1339: server_client_dispatch_command(struct client *c, struct imsg *imsg)
1.1 nicm 1340: {
1.108 nicm 1341: struct msg_command_data data;
1342: char *buf;
1343: size_t len;
1344: struct cmd_list *cmdlist = NULL;
1345: int argc;
1346: char **argv, *cause;
1347:
1348: if (imsg->hdr.len - IMSG_HEADER_SIZE < sizeof data)
1349: fatalx("bad MSG_COMMAND size");
1350: memcpy(&data, imsg->data, sizeof data);
1351:
1.124 nicm 1352: buf = (char *)imsg->data + sizeof data;
1.108 nicm 1353: len = imsg->hdr.len - IMSG_HEADER_SIZE - sizeof data;
1354: if (len > 0 && buf[len - 1] != '\0')
1355: fatalx("bad MSG_COMMAND string");
1356:
1357: argc = data.argc;
1358: if (cmd_unpack_argv(buf, len, argc, &argv) != 0) {
1.194 nicm 1359: cause = xstrdup("command too long");
1.1 nicm 1360: goto error;
1361: }
1362:
1363: if (argc == 0) {
1364: argc = 1;
1365: argv = xcalloc(1, sizeof *argv);
1366: *argv = xstrdup("new-session");
1367: }
1368:
1.94 nicm 1369: if ((cmdlist = cmd_list_parse(argc, argv, NULL, 0, &cause)) == NULL) {
1.1 nicm 1370: cmd_free_argv(argc, argv);
1371: goto error;
1372: }
1373: cmd_free_argv(argc, argv);
1374:
1.194 nicm 1375: cmdq_append(c, cmdq_get_command(cmdlist, NULL, NULL, 0));
1376: cmdq_append(c, cmdq_get_callback(server_client_command_done, NULL));
1.1 nicm 1377: cmd_list_free(cmdlist);
1378: return;
1379:
1380: error:
1.194 nicm 1381: cmdq_append(c, cmdq_get_callback(server_client_command_error, cause));
1382:
1.1 nicm 1383: if (cmdlist != NULL)
1384: cmd_list_free(cmdlist);
1.88 nicm 1385:
1.36 nicm 1386: c->flags |= CLIENT_EXIT;
1.1 nicm 1387: }
1388:
1389: /* Handle identify message. */
1.190 nicm 1390: static void
1.162 nicm 1391: server_client_dispatch_identify(struct client *c, struct imsg *imsg)
1.1 nicm 1392: {
1.165 nicm 1393: const char *data, *home;
1.109 nicm 1394: size_t datalen;
1395: int flags;
1396:
1397: if (c->flags & CLIENT_IDENTIFIED)
1398: fatalx("out-of-order identify message");
1399:
1400: data = imsg->data;
1401: datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
1402:
1403: switch (imsg->hdr.type) {
1404: case MSG_IDENTIFY_FLAGS:
1405: if (datalen != sizeof flags)
1406: fatalx("bad MSG_IDENTIFY_FLAGS size");
1407: memcpy(&flags, data, sizeof flags);
1408: c->flags |= flags;
1.158 nicm 1409: log_debug("client %p IDENTIFY_FLAGS %#x", c, flags);
1.109 nicm 1410: break;
1411: case MSG_IDENTIFY_TERM:
1.110 nicm 1412: if (datalen == 0 || data[datalen - 1] != '\0')
1.109 nicm 1413: fatalx("bad MSG_IDENTIFY_TERM string");
1414: c->term = xstrdup(data);
1.158 nicm 1415: log_debug("client %p IDENTIFY_TERM %s", c, data);
1.109 nicm 1416: break;
1417: case MSG_IDENTIFY_TTYNAME:
1.110 nicm 1418: if (datalen == 0 || data[datalen - 1] != '\0')
1.109 nicm 1419: fatalx("bad MSG_IDENTIFY_TTYNAME string");
1420: c->ttyname = xstrdup(data);
1.158 nicm 1421: log_debug("client %p IDENTIFY_TTYNAME %s", c, data);
1.109 nicm 1422: break;
1423: case MSG_IDENTIFY_CWD:
1.155 nicm 1424: if (datalen == 0 || data[datalen - 1] != '\0')
1425: fatalx("bad MSG_IDENTIFY_CWD string");
1.165 nicm 1426: if (access(data, X_OK) == 0)
1427: c->cwd = xstrdup(data);
1428: else if ((home = find_home()) != NULL)
1429: c->cwd = xstrdup(home);
1430: else
1431: c->cwd = xstrdup("/");
1.158 nicm 1432: log_debug("client %p IDENTIFY_CWD %s", c, data);
1.109 nicm 1433: break;
1434: case MSG_IDENTIFY_STDIN:
1435: if (datalen != 0)
1436: fatalx("bad MSG_IDENTIFY_STDIN size");
1437: c->fd = imsg->fd;
1.158 nicm 1438: log_debug("client %p IDENTIFY_STDIN %d", c, imsg->fd);
1.109 nicm 1439: break;
1440: case MSG_IDENTIFY_ENVIRON:
1.110 nicm 1441: if (datalen == 0 || data[datalen - 1] != '\0')
1.109 nicm 1442: fatalx("bad MSG_IDENTIFY_ENVIRON string");
1443: if (strchr(data, '=') != NULL)
1.164 nicm 1444: environ_put(c->environ, data);
1.158 nicm 1445: log_debug("client %p IDENTIFY_ENVIRON %s", c, data);
1.143 nicm 1446: break;
1447: case MSG_IDENTIFY_CLIENTPID:
1448: if (datalen != sizeof c->pid)
1449: fatalx("bad MSG_IDENTIFY_CLIENTPID size");
1450: memcpy(&c->pid, data, sizeof c->pid);
1.158 nicm 1451: log_debug("client %p IDENTIFY_CLIENTPID %ld", c, (long)c->pid);
1.109 nicm 1452: break;
1453: default:
1454: break;
1455: }
1456:
1457: if (imsg->hdr.type != MSG_IDENTIFY_DONE)
1458: return;
1459: c->flags |= CLIENT_IDENTIFIED;
1.75 nicm 1460:
1.109 nicm 1461: if (c->flags & CLIENT_CONTROL) {
1.75 nicm 1462: c->stdin_callback = control_callback;
1.109 nicm 1463:
1.97 nicm 1464: evbuffer_free(c->stderr_data);
1465: c->stderr_data = c->stdout_data;
1.109 nicm 1466:
1467: if (c->flags & CLIENT_CONTROLCONTROL)
1.96 nicm 1468: evbuffer_add_printf(c->stdout_data, "\033P1000p");
1.162 nicm 1469: proc_send(c->peer, MSG_STDIN, -1, NULL, 0);
1.75 nicm 1470:
1471: c->tty.fd = -1;
1472:
1.109 nicm 1473: close(c->fd);
1474: c->fd = -1;
1475:
1.75 nicm 1476: return;
1477: }
1.1 nicm 1478:
1.109 nicm 1479: if (c->fd == -1)
1.104 nicm 1480: return;
1.145 nicm 1481: if (tty_init(&c->tty, c, c->fd, c->term) != 0) {
1.109 nicm 1482: close(c->fd);
1483: c->fd = -1;
1.80 nicm 1484: return;
1485: }
1.109 nicm 1486: if (c->flags & CLIENT_UTF8)
1.1 nicm 1487: c->tty.flags |= TTY_UTF8;
1.109 nicm 1488: if (c->flags & CLIENT_256COLOURS)
1.1 nicm 1489: c->tty.term_flags |= TERM_256COLOURS;
1490:
1491: tty_resize(&c->tty);
1492:
1.109 nicm 1493: if (!(c->flags & CLIENT_CONTROL))
1.86 nicm 1494: c->flags |= CLIENT_TERMINAL;
1.1 nicm 1495: }
1496:
1497: /* Handle shell message. */
1.190 nicm 1498: static void
1.162 nicm 1499: server_client_dispatch_shell(struct client *c)
1.1 nicm 1500: {
1.107 nicm 1501: const char *shell;
1.25 nicm 1502:
1.163 nicm 1503: shell = options_get_string(global_s_options, "default-shell");
1.1 nicm 1504: if (*shell == '\0' || areshell(shell))
1505: shell = _PATH_BSHELL;
1.162 nicm 1506: proc_send_s(c->peer, MSG_SHELL, shell);
1.25 nicm 1507:
1.162 nicm 1508: proc_kill_peer(c->peer);
1.169 nicm 1509: }
1510:
1511: /* Event callback to push more stdout data if any left. */
1512: static void
1.170 nicm 1513: server_client_stdout_cb(__unused int fd, __unused short events, void *arg)
1.169 nicm 1514: {
1515: struct client *c = arg;
1516:
1517: if (~c->flags & CLIENT_DEAD)
1518: server_client_push_stdout(c);
1519: server_client_unref(c);
1520: }
1521:
1522: /* Push stdout to client if possible. */
1523: void
1524: server_client_push_stdout(struct client *c)
1525: {
1526: struct msg_stdout_data data;
1527: size_t sent, left;
1528:
1529: left = EVBUFFER_LENGTH(c->stdout_data);
1530: while (left != 0) {
1531: sent = left;
1532: if (sent > sizeof data.data)
1533: sent = sizeof data.data;
1534: memcpy(data.data, EVBUFFER_DATA(c->stdout_data), sent);
1535: data.size = sent;
1536:
1537: if (proc_send(c->peer, MSG_STDOUT, -1, &data, sizeof data) != 0)
1538: break;
1539: evbuffer_drain(c->stdout_data, sent);
1540:
1541: left = EVBUFFER_LENGTH(c->stdout_data);
1542: log_debug("%s: client %p, sent %zu, left %zu", __func__, c,
1543: sent, left);
1544: }
1545: if (left != 0) {
1546: c->references++;
1547: event_once(-1, EV_TIMEOUT, server_client_stdout_cb, c, NULL);
1548: log_debug("%s: client %p, queued", __func__, c);
1549: }
1550: }
1551:
1552: /* Event callback to push more stderr data if any left. */
1553: static void
1.170 nicm 1554: server_client_stderr_cb(__unused int fd, __unused short events, void *arg)
1.169 nicm 1555: {
1556: struct client *c = arg;
1557:
1558: if (~c->flags & CLIENT_DEAD)
1559: server_client_push_stderr(c);
1560: server_client_unref(c);
1561: }
1562:
1563: /* Push stderr to client if possible. */
1564: void
1565: server_client_push_stderr(struct client *c)
1566: {
1567: struct msg_stderr_data data;
1568: size_t sent, left;
1569:
1570: if (c->stderr_data == c->stdout_data) {
1571: server_client_push_stdout(c);
1572: return;
1573: }
1574:
1575: left = EVBUFFER_LENGTH(c->stderr_data);
1576: while (left != 0) {
1577: sent = left;
1578: if (sent > sizeof data.data)
1579: sent = sizeof data.data;
1580: memcpy(data.data, EVBUFFER_DATA(c->stderr_data), sent);
1581: data.size = sent;
1582:
1583: if (proc_send(c->peer, MSG_STDERR, -1, &data, sizeof data) != 0)
1584: break;
1585: evbuffer_drain(c->stderr_data, sent);
1586:
1587: left = EVBUFFER_LENGTH(c->stderr_data);
1588: log_debug("%s: client %p, sent %zu, left %zu", __func__, c,
1589: sent, left);
1590: }
1591: if (left != 0) {
1592: c->references++;
1593: event_once(-1, EV_TIMEOUT, server_client_stderr_cb, c, NULL);
1594: log_debug("%s: client %p, queued", __func__, c);
1595: }
1.1 nicm 1596: }