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