Annotation of src/usr.bin/tmux/server-client.c, Revision 1.213
1.213 ! nicm 1: /* $OpenBSD: server-client.c,v 1.212 2017/02/09 12:09:33 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.211 nicm 966: /* Resize timer event. */
1.188 nicm 967: static void
968: server_client_resize_event(__unused int fd, __unused short events, void *data)
1.92 nicm 969: {
1.188 nicm 970: struct window_pane *wp = data;
971: struct winsize ws;
972:
973: evtimer_del(&wp->resize_timer);
1.92 nicm 974:
1.101 nicm 975: if (!(wp->flags & PANE_RESIZE))
1.92 nicm 976: return;
977:
978: memset(&ws, 0, sizeof ws);
979: ws.ws_col = wp->sx;
980: ws.ws_row = wp->sy;
981:
1.100 nicm 982: if (ioctl(wp->fd, TIOCSWINSZ, &ws) == -1)
1.92 nicm 983: fatal("ioctl failed");
984:
985: wp->flags &= ~PANE_RESIZE;
1.188 nicm 986: }
987:
988: /* Check if pane should be resized. */
1.190 nicm 989: static void
1.188 nicm 990: server_client_check_resize(struct window_pane *wp)
991: {
992: struct timeval tv = { .tv_usec = 250000 };
993:
994: if (!(wp->flags & PANE_RESIZE))
995: return;
996:
997: if (!event_initialized(&wp->resize_timer))
998: evtimer_set(&wp->resize_timer, server_client_resize_event, wp);
999:
1000: /*
1001: * The first resize should happen immediately, so if the timer is not
1002: * running, do it now.
1003: */
1004: if (!evtimer_pending(&wp->resize_timer, NULL))
1005: server_client_resize_event(-1, 0, wp);
1006:
1007: /*
1008: * If the pane is in the alternate screen, let the timer expire and
1009: * resize to give the application a chance to redraw. If not, keep
1010: * pushing the timer back.
1011: */
1012: if (wp->saved_grid != NULL && evtimer_pending(&wp->resize_timer, NULL))
1013: return;
1014: evtimer_del(&wp->resize_timer);
1015: evtimer_add(&wp->resize_timer, &tv);
1.91 nicm 1016: }
1017:
1018: /* Check whether pane should be focused. */
1.190 nicm 1019: static void
1.91 nicm 1020: server_client_check_focus(struct window_pane *wp)
1021: {
1.93 nicm 1022: struct client *c;
1.102 nicm 1023: int push;
1024:
1025: /* Do we need to push the focus state? */
1026: push = wp->flags & PANE_FOCUSPUSH;
1027: wp->flags &= ~PANE_FOCUSPUSH;
1.91 nicm 1028:
1029: /* If we don't care about focus, forget it. */
1030: if (!(wp->base.mode & MODE_FOCUSON))
1031: return;
1032:
1033: /* If we're not the active pane in our window, we're not focused. */
1034: if (wp->window->active != wp)
1035: goto not_focused;
1036:
1037: /* If we're in a mode, we're not focused. */
1038: if (wp->screen != &wp->base)
1039: goto not_focused;
1040:
1041: /*
1.93 nicm 1042: * If our window is the current window in any focused clients with an
1043: * attached session, we're focused.
1.91 nicm 1044: */
1.135 nicm 1045: TAILQ_FOREACH(c, &clients, entry) {
1046: if (c->session == NULL || !(c->flags & CLIENT_FOCUSED))
1.91 nicm 1047: continue;
1.93 nicm 1048: if (c->session->flags & SESSION_UNATTACHED)
1049: continue;
1050:
1051: if (c->session->curw->window == wp->window)
1.91 nicm 1052: goto focused;
1053: }
1054:
1055: not_focused:
1.102 nicm 1056: if (push || (wp->flags & PANE_FOCUSED))
1.91 nicm 1057: bufferevent_write(wp->event, "\033[O", 3);
1058: wp->flags &= ~PANE_FOCUSED;
1059: return;
1060:
1061: focused:
1.102 nicm 1062: if (push || !(wp->flags & PANE_FOCUSED))
1.91 nicm 1063: bufferevent_write(wp->event, "\033[I", 3);
1064: wp->flags |= PANE_FOCUSED;
1.2 nicm 1065: }
1066:
1.18 nicm 1067: /*
1068: * Update cursor position and mode settings. The scroll region and attributes
1069: * are cleared when idle (waiting for an event) as this is the most likely time
1070: * a user may interrupt tmux, for example with ~^Z in ssh(1). This is a
1071: * compromise between excessive resets and likelihood of an interrupt.
1072: *
1073: * tty_region/tty_reset/tty_update_mode already take care of not resetting
1074: * things that are already in their default state.
1075: */
1.190 nicm 1076: static void
1.18 nicm 1077: server_client_reset_state(struct client *c)
1.1 nicm 1078: {
1.18 nicm 1079: struct window *w = c->session->curw->window;
1.209 nicm 1080: struct window_pane *wp = w->active, *loop;
1.18 nicm 1081: struct screen *s = wp->screen;
1.163 nicm 1082: struct options *oo = c->session->options;
1.66 nicm 1083: int status, mode, o;
1.60 nicm 1084:
1.186 nicm 1085: if (c->flags & (CLIENT_CONTROL|CLIENT_SUSPENDED))
1.86 nicm 1086: return;
1087:
1.199 nicm 1088: tty_region_off(&c->tty);
1089: tty_margin_off(&c->tty);
1.1 nicm 1090:
1091: status = options_get_number(oo, "status");
1092: if (!window_pane_visible(wp) || wp->yoff + s->cy >= c->tty.sy - status)
1093: tty_cursor(&c->tty, 0, 0);
1.66 nicm 1094: else {
1.126 nicm 1095: o = status && options_get_number(oo, "status-position") == 0;
1.66 nicm 1096: tty_cursor(&c->tty, wp->xoff + s->cx, o + wp->yoff + s->cy);
1097: }
1.1 nicm 1098:
1.50 nicm 1099: /*
1.131 nicm 1100: * Set mouse mode if requested. To support dragging, always use button
1101: * mode.
1.57 nicm 1102: */
1103: mode = s->mode;
1.209 nicm 1104: if (options_get_number(oo, "mouse")) {
1105: mode &= ~ALL_MOUSE_MODES;
1106: TAILQ_FOREACH(loop, &w->panes, entry) {
1107: if (loop->screen->mode & MODE_MOUSE_ALL)
1108: mode |= MODE_MOUSE_ALL;
1109: }
1110: if (~mode & MODE_MOUSE_ALL)
1111: mode |= MODE_MOUSE_BUTTON;
1112: }
1.48 nicm 1113:
1114: /* Set the terminal mode and reset attributes. */
1.59 nicm 1115: tty_update_mode(&c->tty, mode, s);
1.1 nicm 1116: tty_reset(&c->tty);
1.17 nicm 1117: }
1118:
1119: /* Repeat time callback. */
1.190 nicm 1120: static void
1.170 nicm 1121: server_client_repeat_timer(__unused int fd, __unused short events, void *data)
1.17 nicm 1122: {
1123: struct client *c = data;
1124:
1.85 nicm 1125: if (c->flags & CLIENT_REPEAT) {
1.178 nicm 1126: server_client_set_key_table(c, NULL);
1.132 nicm 1127: c->flags &= ~CLIENT_REPEAT;
1128: server_status_client(c);
1.85 nicm 1129: }
1.192 nicm 1130: }
1131:
1132: /* Double-click callback. */
1133: static void
1134: server_client_click_timer(__unused int fd, __unused short events, void *data)
1135: {
1136: struct client *c = data;
1137:
1138: c->flags &= ~(CLIENT_DOUBLECLICK|CLIENT_TRIPLECLICK);
1.1 nicm 1139: }
1140:
1.36 nicm 1141: /* Check if client should be exited. */
1.190 nicm 1142: static void
1.36 nicm 1143: server_client_check_exit(struct client *c)
1144: {
1145: if (!(c->flags & CLIENT_EXIT))
1146: return;
1147:
1.73 nicm 1148: if (EVBUFFER_LENGTH(c->stdin_data) != 0)
1149: return;
1150: if (EVBUFFER_LENGTH(c->stdout_data) != 0)
1.36 nicm 1151: return;
1.73 nicm 1152: if (EVBUFFER_LENGTH(c->stderr_data) != 0)
1.36 nicm 1153: return;
1154:
1.162 nicm 1155: proc_send(c->peer, MSG_EXIT, -1, &c->retval, sizeof c->retval);
1.36 nicm 1156: c->flags &= ~CLIENT_EXIT;
1.38 nicm 1157: }
1158:
1.1 nicm 1159: /* Check for client redraws. */
1.190 nicm 1160: static void
1.1 nicm 1161: server_client_check_redraw(struct client *c)
1162: {
1163: struct session *s = c->session;
1.137 nicm 1164: struct tty *tty = &c->tty;
1.1 nicm 1165: struct window_pane *wp;
1.189 nicm 1166: int flags, masked;
1.1 nicm 1167:
1.86 nicm 1168: if (c->flags & (CLIENT_CONTROL|CLIENT_SUSPENDED))
1.79 nicm 1169: return;
1170:
1.1 nicm 1171: if (c->flags & (CLIENT_REDRAW|CLIENT_STATUS)) {
1.163 nicm 1172: if (options_get_number(s->options, "set-titles"))
1.1 nicm 1173: server_client_set_title(c);
1.189 nicm 1174: screen_redraw_update(c); /* will adjust flags */
1.1 nicm 1175: }
1176:
1.137 nicm 1177: flags = tty->flags & (TTY_FREEZE|TTY_NOCURSOR);
1178: tty->flags = (tty->flags & ~TTY_FREEZE) | TTY_NOCURSOR;
1179:
1.1 nicm 1180: if (c->flags & CLIENT_REDRAW) {
1.137 nicm 1181: tty_update_mode(tty, tty->mode, NULL);
1.115 nicm 1182: screen_redraw_screen(c, 1, 1, 1);
1.27 nicm 1183: c->flags &= ~(CLIENT_STATUS|CLIENT_BORDERS);
1.38 nicm 1184: } else if (c->flags & CLIENT_REDRAWWINDOW) {
1.137 nicm 1185: tty_update_mode(tty, tty->mode, NULL);
1.38 nicm 1186: TAILQ_FOREACH(wp, &c->session->curw->window->panes, entry)
1187: screen_redraw_pane(c, wp);
1188: c->flags &= ~CLIENT_REDRAWWINDOW;
1.1 nicm 1189: } else {
1190: TAILQ_FOREACH(wp, &c->session->curw->window->panes, entry) {
1.137 nicm 1191: if (wp->flags & PANE_REDRAW) {
1192: tty_update_mode(tty, tty->mode, NULL);
1.1 nicm 1193: screen_redraw_pane(c, wp);
1.137 nicm 1194: }
1.1 nicm 1195: }
1196: }
1197:
1.185 nicm 1198: masked = c->flags & (CLIENT_BORDERS|CLIENT_STATUS);
1199: if (masked != 0)
1.137 nicm 1200: tty_update_mode(tty, tty->mode, NULL);
1.185 nicm 1201: if (masked == CLIENT_BORDERS)
1.115 nicm 1202: screen_redraw_screen(c, 0, 0, 1);
1.185 nicm 1203: else if (masked == CLIENT_STATUS)
1.115 nicm 1204: screen_redraw_screen(c, 0, 1, 0);
1.185 nicm 1205: else if (masked != 0)
1206: screen_redraw_screen(c, 0, 1, 1);
1.1 nicm 1207:
1.137 nicm 1208: tty->flags = (tty->flags & ~(TTY_FREEZE|TTY_NOCURSOR)) | flags;
1209: tty_update_mode(tty, tty->mode, NULL);
1.1 nicm 1210:
1.153 nicm 1211: c->flags &= ~(CLIENT_REDRAW|CLIENT_BORDERS|CLIENT_STATUS|
1212: CLIENT_STATUSFORCE);
1.1 nicm 1213: }
1214:
1215: /* Set client title. */
1.190 nicm 1216: static void
1.1 nicm 1217: server_client_set_title(struct client *c)
1218: {
1.128 nicm 1219: struct session *s = c->session;
1220: const char *template;
1221: char *title;
1222: struct format_tree *ft;
1.1 nicm 1223:
1.163 nicm 1224: template = options_get_string(s->options, "set-titles-string");
1.25 nicm 1225:
1.210 nicm 1226: ft = format_create(NULL, FORMAT_NONE, 0);
1.128 nicm 1227: format_defaults(ft, c, NULL, NULL, NULL);
1228:
1229: title = format_expand_time(ft, template, time(NULL));
1.1 nicm 1230: if (c->title == NULL || strcmp(title, c->title) != 0) {
1.77 nicm 1231: free(c->title);
1.1 nicm 1232: c->title = xstrdup(title);
1233: tty_set_title(&c->tty, c->title);
1234: }
1.77 nicm 1235: free(title);
1.128 nicm 1236:
1237: format_free(ft);
1.1 nicm 1238: }
1239:
1240: /* Dispatch message from client. */
1.190 nicm 1241: static void
1.162 nicm 1242: server_client_dispatch(struct imsg *imsg, void *arg)
1.1 nicm 1243: {
1.162 nicm 1244: struct client *c = arg;
1.73 nicm 1245: struct msg_stdin_data stdindata;
1.108 nicm 1246: const char *data;
1.162 nicm 1247: ssize_t datalen;
1.149 nicm 1248: struct session *s;
1.1 nicm 1249:
1.162 nicm 1250: if (c->flags & CLIENT_DEAD)
1251: return;
1252:
1253: if (imsg == NULL) {
1254: server_client_lost(c);
1255: return;
1256: }
1.1 nicm 1257:
1.162 nicm 1258: data = imsg->data;
1259: datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
1.1 nicm 1260:
1.162 nicm 1261: switch (imsg->hdr.type) {
1262: case MSG_IDENTIFY_FLAGS:
1263: case MSG_IDENTIFY_TERM:
1264: case MSG_IDENTIFY_TTYNAME:
1265: case MSG_IDENTIFY_CWD:
1266: case MSG_IDENTIFY_STDIN:
1267: case MSG_IDENTIFY_ENVIRON:
1268: case MSG_IDENTIFY_CLIENTPID:
1269: case MSG_IDENTIFY_DONE:
1270: server_client_dispatch_identify(c, imsg);
1271: break;
1272: case MSG_COMMAND:
1273: server_client_dispatch_command(c, imsg);
1274: break;
1275: case MSG_STDIN:
1276: if (datalen != sizeof stdindata)
1277: fatalx("bad MSG_STDIN size");
1278: memcpy(&stdindata, data, sizeof stdindata);
1.36 nicm 1279:
1.162 nicm 1280: if (c->stdin_callback == NULL)
1.33 nicm 1281: break;
1.162 nicm 1282: if (stdindata.size <= 0)
1283: c->stdin_closed = 1;
1284: else {
1285: evbuffer_add(c->stdin_data, stdindata.data,
1286: stdindata.size);
1287: }
1288: c->stdin_callback(c, c->stdin_closed,
1289: c->stdin_callback_data);
1290: break;
1291: case MSG_RESIZE:
1292: if (datalen != 0)
1293: fatalx("bad MSG_RESIZE size");
1.1 nicm 1294:
1.162 nicm 1295: if (c->flags & CLIENT_CONTROL)
1.1 nicm 1296: break;
1.162 nicm 1297: if (tty_resize(&c->tty)) {
1298: recalculate_sizes();
1299: server_redraw_client(c);
1300: }
1.174 nicm 1301: if (c->session != NULL)
1.196 nicm 1302: notify_client("client-resized", c);
1.162 nicm 1303: break;
1304: case MSG_EXITING:
1305: if (datalen != 0)
1306: fatalx("bad MSG_EXITING size");
1.1 nicm 1307:
1.162 nicm 1308: c->session = NULL;
1309: tty_close(&c->tty);
1310: proc_send(c->peer, MSG_EXITED, -1, NULL, 0);
1311: break;
1312: case MSG_WAKEUP:
1313: case MSG_UNLOCK:
1314: if (datalen != 0)
1315: fatalx("bad MSG_WAKEUP size");
1.121 nicm 1316:
1.162 nicm 1317: if (!(c->flags & CLIENT_SUSPENDED))
1318: break;
1319: c->flags &= ~CLIENT_SUSPENDED;
1.10 nicm 1320:
1.162 nicm 1321: if (c->tty.fd == -1) /* exited in the meantime */
1.1 nicm 1322: break;
1.162 nicm 1323: s = c->session;
1.1 nicm 1324:
1.162 nicm 1325: if (gettimeofday(&c->activity_time, NULL) != 0)
1326: fatal("gettimeofday failed");
1327:
1328: tty_start_tty(&c->tty);
1329: server_redraw_client(c);
1330: recalculate_sizes();
1.184 nicm 1331:
1332: if (s != NULL)
1333: session_update_activity(s, &c->activity_time);
1.162 nicm 1334: break;
1335: case MSG_SHELL:
1336: if (datalen != 0)
1337: fatalx("bad MSG_SHELL size");
1.1 nicm 1338:
1.162 nicm 1339: server_client_dispatch_shell(c);
1340: break;
1.1 nicm 1341: }
1342: }
1343:
1.194 nicm 1344: /* Callback when command is done. */
1345: static enum cmd_retval
1.195 nicm 1346: server_client_command_done(struct cmdq_item *item, __unused void *data)
1.194 nicm 1347: {
1.195 nicm 1348: struct client *c = item->client;
1.194 nicm 1349:
1350: if (~c->flags & CLIENT_ATTACHED)
1351: c->flags |= CLIENT_EXIT;
1352: return (CMD_RETURN_NORMAL);
1353: }
1354:
1355: /* Show an error message. */
1356: static enum cmd_retval
1.195 nicm 1357: server_client_command_error(struct cmdq_item *item, void *data)
1.194 nicm 1358: {
1359: char *error = data;
1360:
1.195 nicm 1361: cmdq_error(item, "%s", error);
1.194 nicm 1362: free(error);
1363:
1364: return (CMD_RETURN_NORMAL);
1365: }
1366:
1.1 nicm 1367: /* Handle command message. */
1.190 nicm 1368: static void
1.162 nicm 1369: server_client_dispatch_command(struct client *c, struct imsg *imsg)
1.1 nicm 1370: {
1.108 nicm 1371: struct msg_command_data data;
1372: char *buf;
1373: size_t len;
1374: struct cmd_list *cmdlist = NULL;
1375: int argc;
1376: char **argv, *cause;
1377:
1378: if (imsg->hdr.len - IMSG_HEADER_SIZE < sizeof data)
1379: fatalx("bad MSG_COMMAND size");
1380: memcpy(&data, imsg->data, sizeof data);
1381:
1.124 nicm 1382: buf = (char *)imsg->data + sizeof data;
1.108 nicm 1383: len = imsg->hdr.len - IMSG_HEADER_SIZE - sizeof data;
1384: if (len > 0 && buf[len - 1] != '\0')
1385: fatalx("bad MSG_COMMAND string");
1386:
1387: argc = data.argc;
1388: if (cmd_unpack_argv(buf, len, argc, &argv) != 0) {
1.194 nicm 1389: cause = xstrdup("command too long");
1.1 nicm 1390: goto error;
1391: }
1392:
1393: if (argc == 0) {
1394: argc = 1;
1395: argv = xcalloc(1, sizeof *argv);
1396: *argv = xstrdup("new-session");
1397: }
1398:
1.94 nicm 1399: if ((cmdlist = cmd_list_parse(argc, argv, NULL, 0, &cause)) == NULL) {
1.1 nicm 1400: cmd_free_argv(argc, argv);
1401: goto error;
1402: }
1403: cmd_free_argv(argc, argv);
1404:
1.194 nicm 1405: cmdq_append(c, cmdq_get_command(cmdlist, NULL, NULL, 0));
1406: cmdq_append(c, cmdq_get_callback(server_client_command_done, NULL));
1.1 nicm 1407: cmd_list_free(cmdlist);
1408: return;
1409:
1410: error:
1.194 nicm 1411: cmdq_append(c, cmdq_get_callback(server_client_command_error, cause));
1412:
1.1 nicm 1413: if (cmdlist != NULL)
1414: cmd_list_free(cmdlist);
1.88 nicm 1415:
1.36 nicm 1416: c->flags |= CLIENT_EXIT;
1.1 nicm 1417: }
1418:
1419: /* Handle identify message. */
1.190 nicm 1420: static void
1.162 nicm 1421: server_client_dispatch_identify(struct client *c, struct imsg *imsg)
1.1 nicm 1422: {
1.165 nicm 1423: const char *data, *home;
1.109 nicm 1424: size_t datalen;
1425: int flags;
1426:
1427: if (c->flags & CLIENT_IDENTIFIED)
1428: fatalx("out-of-order identify message");
1429:
1430: data = imsg->data;
1431: datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
1432:
1433: switch (imsg->hdr.type) {
1434: case MSG_IDENTIFY_FLAGS:
1435: if (datalen != sizeof flags)
1436: fatalx("bad MSG_IDENTIFY_FLAGS size");
1437: memcpy(&flags, data, sizeof flags);
1438: c->flags |= flags;
1.158 nicm 1439: log_debug("client %p IDENTIFY_FLAGS %#x", c, flags);
1.109 nicm 1440: break;
1441: case MSG_IDENTIFY_TERM:
1.110 nicm 1442: if (datalen == 0 || data[datalen - 1] != '\0')
1.109 nicm 1443: fatalx("bad MSG_IDENTIFY_TERM string");
1444: c->term = xstrdup(data);
1.158 nicm 1445: log_debug("client %p IDENTIFY_TERM %s", c, data);
1.109 nicm 1446: break;
1447: case MSG_IDENTIFY_TTYNAME:
1.110 nicm 1448: if (datalen == 0 || data[datalen - 1] != '\0')
1.109 nicm 1449: fatalx("bad MSG_IDENTIFY_TTYNAME string");
1450: c->ttyname = xstrdup(data);
1.158 nicm 1451: log_debug("client %p IDENTIFY_TTYNAME %s", c, data);
1.109 nicm 1452: break;
1453: case MSG_IDENTIFY_CWD:
1.155 nicm 1454: if (datalen == 0 || data[datalen - 1] != '\0')
1455: fatalx("bad MSG_IDENTIFY_CWD string");
1.165 nicm 1456: if (access(data, X_OK) == 0)
1457: c->cwd = xstrdup(data);
1458: else if ((home = find_home()) != NULL)
1459: c->cwd = xstrdup(home);
1460: else
1461: c->cwd = xstrdup("/");
1.158 nicm 1462: log_debug("client %p IDENTIFY_CWD %s", c, data);
1.109 nicm 1463: break;
1464: case MSG_IDENTIFY_STDIN:
1465: if (datalen != 0)
1466: fatalx("bad MSG_IDENTIFY_STDIN size");
1467: c->fd = imsg->fd;
1.158 nicm 1468: log_debug("client %p IDENTIFY_STDIN %d", c, imsg->fd);
1.109 nicm 1469: break;
1470: case MSG_IDENTIFY_ENVIRON:
1.110 nicm 1471: if (datalen == 0 || data[datalen - 1] != '\0')
1.109 nicm 1472: fatalx("bad MSG_IDENTIFY_ENVIRON string");
1473: if (strchr(data, '=') != NULL)
1.164 nicm 1474: environ_put(c->environ, data);
1.158 nicm 1475: log_debug("client %p IDENTIFY_ENVIRON %s", c, data);
1.143 nicm 1476: break;
1477: case MSG_IDENTIFY_CLIENTPID:
1478: if (datalen != sizeof c->pid)
1479: fatalx("bad MSG_IDENTIFY_CLIENTPID size");
1480: memcpy(&c->pid, data, sizeof c->pid);
1.158 nicm 1481: log_debug("client %p IDENTIFY_CLIENTPID %ld", c, (long)c->pid);
1.109 nicm 1482: break;
1483: default:
1484: break;
1485: }
1486:
1487: if (imsg->hdr.type != MSG_IDENTIFY_DONE)
1488: return;
1489: c->flags |= CLIENT_IDENTIFIED;
1.75 nicm 1490:
1.109 nicm 1491: if (c->flags & CLIENT_CONTROL) {
1.75 nicm 1492: c->stdin_callback = control_callback;
1.109 nicm 1493:
1.97 nicm 1494: evbuffer_free(c->stderr_data);
1495: c->stderr_data = c->stdout_data;
1.109 nicm 1496:
1497: if (c->flags & CLIENT_CONTROLCONTROL)
1.96 nicm 1498: evbuffer_add_printf(c->stdout_data, "\033P1000p");
1.162 nicm 1499: proc_send(c->peer, MSG_STDIN, -1, NULL, 0);
1.75 nicm 1500:
1501: c->tty.fd = -1;
1502:
1.109 nicm 1503: close(c->fd);
1504: c->fd = -1;
1505:
1.75 nicm 1506: return;
1507: }
1.1 nicm 1508:
1.109 nicm 1509: if (c->fd == -1)
1.104 nicm 1510: return;
1.145 nicm 1511: if (tty_init(&c->tty, c, c->fd, c->term) != 0) {
1.109 nicm 1512: close(c->fd);
1513: c->fd = -1;
1.80 nicm 1514: return;
1515: }
1.109 nicm 1516: if (c->flags & CLIENT_UTF8)
1.1 nicm 1517: c->tty.flags |= TTY_UTF8;
1.109 nicm 1518: if (c->flags & CLIENT_256COLOURS)
1.1 nicm 1519: c->tty.term_flags |= TERM_256COLOURS;
1520:
1521: tty_resize(&c->tty);
1522:
1.109 nicm 1523: if (!(c->flags & CLIENT_CONTROL))
1.86 nicm 1524: c->flags |= CLIENT_TERMINAL;
1.1 nicm 1525: }
1526:
1527: /* Handle shell message. */
1.190 nicm 1528: static void
1.162 nicm 1529: server_client_dispatch_shell(struct client *c)
1.1 nicm 1530: {
1.107 nicm 1531: const char *shell;
1.25 nicm 1532:
1.163 nicm 1533: shell = options_get_string(global_s_options, "default-shell");
1.1 nicm 1534: if (*shell == '\0' || areshell(shell))
1535: shell = _PATH_BSHELL;
1.162 nicm 1536: proc_send_s(c->peer, MSG_SHELL, shell);
1.25 nicm 1537:
1.162 nicm 1538: proc_kill_peer(c->peer);
1.169 nicm 1539: }
1540:
1541: /* Event callback to push more stdout data if any left. */
1542: static void
1.170 nicm 1543: server_client_stdout_cb(__unused int fd, __unused short events, void *arg)
1.169 nicm 1544: {
1545: struct client *c = arg;
1546:
1547: if (~c->flags & CLIENT_DEAD)
1548: server_client_push_stdout(c);
1549: server_client_unref(c);
1550: }
1551:
1552: /* Push stdout to client if possible. */
1553: void
1554: server_client_push_stdout(struct client *c)
1555: {
1556: struct msg_stdout_data data;
1557: size_t sent, left;
1558:
1559: left = EVBUFFER_LENGTH(c->stdout_data);
1560: while (left != 0) {
1561: sent = left;
1562: if (sent > sizeof data.data)
1563: sent = sizeof data.data;
1564: memcpy(data.data, EVBUFFER_DATA(c->stdout_data), sent);
1565: data.size = sent;
1566:
1567: if (proc_send(c->peer, MSG_STDOUT, -1, &data, sizeof data) != 0)
1568: break;
1569: evbuffer_drain(c->stdout_data, sent);
1570:
1571: left = EVBUFFER_LENGTH(c->stdout_data);
1572: log_debug("%s: client %p, sent %zu, left %zu", __func__, c,
1573: sent, left);
1574: }
1575: if (left != 0) {
1576: c->references++;
1577: event_once(-1, EV_TIMEOUT, server_client_stdout_cb, c, NULL);
1578: log_debug("%s: client %p, queued", __func__, c);
1579: }
1580: }
1581:
1582: /* Event callback to push more stderr data if any left. */
1583: static void
1.170 nicm 1584: server_client_stderr_cb(__unused int fd, __unused short events, void *arg)
1.169 nicm 1585: {
1586: struct client *c = arg;
1587:
1588: if (~c->flags & CLIENT_DEAD)
1589: server_client_push_stderr(c);
1590: server_client_unref(c);
1591: }
1592:
1593: /* Push stderr to client if possible. */
1594: void
1595: server_client_push_stderr(struct client *c)
1596: {
1597: struct msg_stderr_data data;
1598: size_t sent, left;
1599:
1600: if (c->stderr_data == c->stdout_data) {
1601: server_client_push_stdout(c);
1602: return;
1603: }
1604:
1605: left = EVBUFFER_LENGTH(c->stderr_data);
1606: while (left != 0) {
1607: sent = left;
1608: if (sent > sizeof data.data)
1609: sent = sizeof data.data;
1610: memcpy(data.data, EVBUFFER_DATA(c->stderr_data), sent);
1611: data.size = sent;
1612:
1613: if (proc_send(c->peer, MSG_STDERR, -1, &data, sizeof data) != 0)
1614: break;
1615: evbuffer_drain(c->stderr_data, sent);
1616:
1617: left = EVBUFFER_LENGTH(c->stderr_data);
1618: log_debug("%s: client %p, sent %zu, left %zu", __func__, c,
1619: sent, left);
1620: }
1621: if (left != 0) {
1622: c->references++;
1623: event_once(-1, EV_TIMEOUT, server_client_stderr_cb, c, NULL);
1624: log_debug("%s: client %p, queued", __func__, c);
1.212 nicm 1625: }
1626: }
1627:
1628: /* Add to client message log. */
1629: void
1630: server_client_add_message(struct client *c, const char *fmt, ...)
1631: {
1632: struct message_entry *msg, *msg1;
1633: char *s;
1634: va_list ap;
1635: u_int limit;
1636:
1637: va_start(ap, fmt);
1638: xvasprintf(&s, fmt, ap);
1639: va_end(ap);
1640:
1641: log_debug("%s: message %s", c->tty.path, s);
1642:
1643: msg = xcalloc(1, sizeof *msg);
1644: msg->msg_time = time(NULL);
1645: msg->msg_num = c->message_next++;
1646: msg->msg = s;
1647: TAILQ_INSERT_TAIL(&c->message_log, msg, entry);
1648:
1649: limit = options_get_number(global_options, "message-limit");
1650: TAILQ_FOREACH_SAFE(msg, &c->message_log, entry, msg1) {
1651: if (msg->msg_num + limit >= c->message_next)
1652: break;
1653: free(msg->msg);
1654: TAILQ_REMOVE(&c->message_log, msg, entry);
1655: free(msg);
1.169 nicm 1656: }
1.213 ! nicm 1657: }
! 1658:
! 1659: /* Get client working directory. */
! 1660: const char *
! 1661: server_client_get_cwd(struct client *c)
! 1662: {
! 1663: struct session *s;
! 1664:
! 1665: if (c != NULL && c->session == NULL && c->cwd != NULL)
! 1666: return (c->cwd);
! 1667: if (c != NULL && (s = c->session) != NULL && s->cwd != NULL)
! 1668: return (s->cwd);
! 1669: return (".");
! 1670: }
! 1671:
! 1672: /* Resolve an absolute path or relative to client working directory. */
! 1673: char *
! 1674: server_client_get_path(struct client *c, const char *file)
! 1675: {
! 1676: char *path, resolved[PATH_MAX];
! 1677:
! 1678: if (*file == '/')
! 1679: path = xstrdup(file);
! 1680: else
! 1681: xasprintf(&path, "%s/%s", server_client_get_cwd(c), file);
! 1682: if (realpath(path, resolved) == NULL)
! 1683: return (path);
! 1684: free(path);
! 1685: return (xstrdup(resolved));
1.1 nicm 1686: }