Annotation of src/usr.bin/tmux/server-client.c, Revision 1.135
1.135 ! nicm 1: /* $OpenBSD: server-client.c,v 1.134 2015/04/22 15:30:11 nicm Exp $ */
1.1 nicm 2:
3: /*
4: * Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net>
5: *
6: * Permission to use, copy, modify, and distribute this software for any
7: * purpose with or without fee is hereby granted, provided that the above
8: * copyright notice and this permission notice appear in all copies.
9: *
10: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14: * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15: * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16: * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17: */
18:
19: #include <sys/types.h>
1.92 nicm 20: #include <sys/ioctl.h>
1.1 nicm 21:
1.114 benno 22: #include <errno.h>
1.12 nicm 23: #include <event.h>
1.1 nicm 24: #include <fcntl.h>
1.98 nicm 25: #include <paths.h>
26: #include <stdlib.h>
1.1 nicm 27: #include <string.h>
1.4 nicm 28: #include <time.h>
1.1 nicm 29: #include <unistd.h>
30:
31: #include "tmux.h"
32:
1.132 nicm 33: void server_client_key_table(struct client *, const char *);
1.91 nicm 34: void server_client_check_focus(struct window_pane *);
1.92 nicm 35: void server_client_check_resize(struct window_pane *);
1.131 nicm 36: int server_client_check_mouse(struct client *);
1.17 nicm 37: void server_client_repeat_timer(int, short, void *);
1.36 nicm 38: void server_client_check_exit(struct client *);
1.1 nicm 39: void server_client_check_redraw(struct client *);
40: void server_client_set_title(struct client *);
1.18 nicm 41: void server_client_reset_state(struct client *);
1.82 nicm 42: int server_client_assume_paste(struct session *);
1.1 nicm 43:
44: int server_client_msg_dispatch(struct client *);
1.108 nicm 45: void server_client_msg_command(struct client *, struct imsg *);
1.109 nicm 46: void server_client_msg_identify(struct client *, struct imsg *);
1.1 nicm 47: void server_client_msg_shell(struct client *);
48:
1.132 nicm 49: /* Set client key table. */
50: void
51: server_client_key_table(struct client *c, const char *name)
52: {
53: key_bindings_unref_table(c->keytable);
54: c->keytable = key_bindings_get_table(name, 1);
55: c->keytable->references++;
56: }
57:
1.1 nicm 58: /* Create a new client. */
59: void
60: server_client_create(int fd)
61: {
62: struct client *c;
63:
1.49 nicm 64: setblocking(fd, 0);
1.1 nicm 65:
66: c = xcalloc(1, sizeof *c);
67: c->references = 0;
68: imsg_init(&c->ibuf, fd);
1.14 nicm 69: server_update_event(c);
1.25 nicm 70:
1.10 nicm 71: if (gettimeofday(&c->creation_time, NULL) != 0)
1.1 nicm 72: fatal("gettimeofday failed");
1.11 nicm 73: memcpy(&c->activity_time, &c->creation_time, sizeof c->activity_time);
1.111 nicm 74:
75: environ_init(&c->environ);
1.1 nicm 76:
1.94 nicm 77: c->cmdq = cmdq_new(c);
78: c->cmdq->client_exit = 1;
79:
1.116 nicm 80: c->stdin_data = evbuffer_new();
81: c->stdout_data = evbuffer_new();
82: c->stderr_data = evbuffer_new();
1.33 nicm 83:
1.1 nicm 84: c->tty.fd = -1;
85: c->title = NULL;
86:
87: c->session = NULL;
1.45 nicm 88: c->last_session = NULL;
1.1 nicm 89: c->tty.sx = 80;
90: c->tty.sy = 24;
91:
92: screen_init(&c->status, c->tty.sx, 1, 0);
1.51 nicm 93: RB_INIT(&c->status_new);
94: RB_INIT(&c->status_old);
1.1 nicm 95:
96: c->message_string = NULL;
1.21 nicm 97: ARRAY_INIT(&c->message_log);
1.1 nicm 98:
99: c->prompt_string = NULL;
100: c->prompt_buffer = NULL;
101: c->prompt_index = 0;
102:
1.93 nicm 103: c->flags |= CLIENT_FOCUSED;
104:
1.132 nicm 105: c->keytable = key_bindings_get_table("root", 1);
106: c->keytable->references++;
107:
1.17 nicm 108: evtimer_set(&c->repeat_timer, server_client_repeat_timer, c);
109:
1.135 ! nicm 110: TAILQ_INSERT_TAIL(&clients, c, entry);
1.1 nicm 111: log_debug("new client %d", fd);
1.72 nicm 112: }
113:
114: /* Open client terminal if needed. */
115: int
1.119 nicm 116: server_client_open(struct client *c, char **cause)
1.72 nicm 117: {
1.75 nicm 118: if (c->flags & CLIENT_CONTROL)
119: return (0);
1.120 nicm 120:
121: if (strcmp(c->ttyname, "/dev/tty") == 0) {
122: *cause = xstrdup("can't use /dev/tty");
123: return (-1);
124: }
1.75 nicm 125:
1.72 nicm 126: if (!(c->flags & CLIENT_TERMINAL)) {
1.116 nicm 127: *cause = xstrdup("not a terminal");
1.72 nicm 128: return (-1);
129: }
130:
1.119 nicm 131: if (tty_open(&c->tty, cause) != 0)
1.72 nicm 132: return (-1);
133:
134: return (0);
1.1 nicm 135: }
136:
137: /* Lost a client. */
138: void
139: server_client_lost(struct client *c)
140: {
1.21 nicm 141: struct message_entry *msg;
142: u_int i;
1.1 nicm 143:
1.135 ! nicm 144: TAILQ_REMOVE(&clients, c, entry);
1.1 nicm 145: log_debug("lost client %d", c->ibuf.fd);
146:
147: /*
148: * If CLIENT_TERMINAL hasn't been set, then tty_init hasn't been called
149: * and tty_free might close an unrelated fd.
150: */
151: if (c->flags & CLIENT_TERMINAL)
152: tty_free(&c->tty);
1.109 nicm 153: free(c->ttyname);
154: free(c->term);
1.1 nicm 155:
1.113 nicm 156: evbuffer_free(c->stdin_data);
157: evbuffer_free(c->stdout_data);
1.97 nicm 158: if (c->stderr_data != c->stdout_data)
1.126 nicm 159: evbuffer_free(c->stderr_data);
1.33 nicm 160:
1.51 nicm 161: status_free_jobs(&c->status_new);
162: status_free_jobs(&c->status_old);
1.1 nicm 163: screen_free(&c->status);
164:
1.77 nicm 165: free(c->title);
1.109 nicm 166: close(c->cwd);
1.1 nicm 167:
1.17 nicm 168: evtimer_del(&c->repeat_timer);
169:
1.132 nicm 170: key_bindings_unref_table(c->keytable);
171:
1.69 nicm 172: if (event_initialized(&c->identify_timer))
173: evtimer_del(&c->identify_timer);
1.15 nicm 174:
1.77 nicm 175: free(c->message_string);
1.116 nicm 176: if (event_initialized(&c->message_timer))
1.69 nicm 177: evtimer_del(&c->message_timer);
1.21 nicm 178: for (i = 0; i < ARRAY_LENGTH(&c->message_log); i++) {
179: msg = &ARRAY_ITEM(&c->message_log, i);
1.77 nicm 180: free(msg->msg);
1.21 nicm 181: }
182: ARRAY_FREE(&c->message_log);
1.1 nicm 183:
1.77 nicm 184: free(c->prompt_string);
185: free(c->prompt_buffer);
1.61 nicm 186:
1.94 nicm 187: c->cmdq->dead = 1;
188: cmdq_free(c->cmdq);
189: c->cmdq = NULL;
190:
1.61 nicm 191: environ_free(&c->environ);
1.1 nicm 192:
193: close(c->ibuf.fd);
194: imsg_clear(&c->ibuf);
1.69 nicm 195: if (event_initialized(&c->event))
196: event_del(&c->event);
1.13 nicm 197:
1.135 ! nicm 198: TAILQ_INSERT_TAIL(&dead_clients, c, entry);
1.1 nicm 199: c->flags |= CLIENT_DEAD;
1.71 nicm 200:
201: server_add_accept(0); /* may be more file descriptors now */
1.1 nicm 202:
203: recalculate_sizes();
1.41 nicm 204: server_check_unattached();
1.19 nicm 205: server_update_socket();
1.9 nicm 206: }
207:
1.1 nicm 208: /* Process a single client event. */
209: void
1.12 nicm 210: server_client_callback(int fd, short events, void *data)
1.1 nicm 211: {
212: struct client *c = data;
1.7 nicm 213:
214: if (c->flags & CLIENT_DEAD)
215: return;
1.1 nicm 216:
217: if (fd == c->ibuf.fd) {
1.122 krw 218: if (events & EV_WRITE && msgbuf_write(&c->ibuf.w) <= 0 &&
1.114 benno 219: errno != EAGAIN)
1.1 nicm 220: goto client_lost;
221:
222: if (c->flags & CLIENT_BAD) {
223: if (c->ibuf.w.queued == 0)
224: goto client_lost;
225: return;
226: }
227:
1.12 nicm 228: if (events & EV_READ && server_client_msg_dispatch(c) != 0)
1.1 nicm 229: goto client_lost;
230: }
1.14 nicm 231:
1.73 nicm 232: server_push_stdout(c);
233: server_push_stderr(c);
234:
1.25 nicm 235: server_update_event(c);
1.1 nicm 236: return;
237:
238: client_lost:
239: server_client_lost(c);
240: }
241:
1.16 nicm 242: /* Handle client status timer. */
243: void
244: server_client_status_timer(void)
245: {
246: struct client *c;
247: struct session *s;
248: struct timeval tv;
1.20 nicm 249: int interval;
250: time_t difference;
1.16 nicm 251:
252: if (gettimeofday(&tv, NULL) != 0)
253: fatal("gettimeofday failed");
254:
1.135 ! nicm 255: TAILQ_FOREACH(c, &clients, entry) {
! 256: if (c->session == NULL)
1.16 nicm 257: continue;
258: if (c->message_string != NULL || c->prompt_string != NULL) {
259: /*
260: * Don't need timed redraw for messages/prompts so bail
261: * now. The status timer isn't reset when they are
262: * redrawn anyway.
263: */
264: continue;
265: }
266: s = c->session;
267:
268: if (!options_get_number(&s->options, "status"))
269: continue;
270: interval = options_get_number(&s->options, "status-interval");
271:
1.20 nicm 272: difference = tv.tv_sec - c->status_timer.tv_sec;
1.117 nicm 273: if (interval != 0 && difference >= interval) {
1.51 nicm 274: status_update_jobs(c);
1.16 nicm 275: c->flags |= CLIENT_STATUS;
276: }
277: }
278: }
279:
1.66 nicm 280: /* Check for mouse keys. */
1.131 nicm 281: int
282: server_client_check_mouse(struct client *c)
1.66 nicm 283: {
1.131 nicm 284: struct session *s = c->session;
285: struct mouse_event *m = &c->tty.mouse;
286: struct window *w;
287: struct window_pane *wp;
288: enum { NOTYPE, DOWN, UP, DRAG, WHEEL } type = NOTYPE;
289: enum { NOWHERE, PANE, STATUS, BORDER } where = NOWHERE;
290: u_int x, y, b;
291: int key;
292:
293: log_debug("mouse %02x at %u,%u (last %u,%u) (%d)", m->b, m->x, m->y,
294: m->lx, m->ly, c->tty.mouse_drag_flag);
295:
296: /* What type of event is this? */
297: if (MOUSE_DRAG(m->b)) {
298: type = DRAG;
299: if (c->tty.mouse_drag_flag) {
300: x = m->x, y = m->y, b = m->b;
301: log_debug("drag update at %u,%u", x, y);
302: } else {
303: x = m->lx, y = m->ly, b = m->lb;
304: log_debug("drag start at %u,%u", x, y);
305: }
306: } else if (MOUSE_WHEEL(m->b)) {
307: type = WHEEL;
308: x = m->x, y = m->y, b = m->b;
309: log_debug("wheel at %u,%u", x, y);
310: } else if (MOUSE_BUTTONS(m->b) == 3) {
311: type = UP;
312: x = m->x, y = m->y, b = m->lb;
313: log_debug("up at %u,%u", x, y);
314: } else {
315: type = DOWN;
316: x = m->x, y = m->y, b = m->b;
317: log_debug("down at %u,%u", x, y);
318: }
319: if (type == NOTYPE)
320: return (KEYC_NONE);
321:
322: /* Always save the session. */
323: m->s = s->id;
324:
325: /* Is this on the status line? */
326: m->statusat = status_at_line(c);
327: if (m->statusat != -1 && y == (u_int)m->statusat) {
328: w = status_get_window_at(c, x);
329: if (w == NULL)
330: return (KEYC_NONE);
331: m->w = w->id;
332: where = STATUS;
333: } else
334: m->w = -1;
335:
336: /* Not on status line. Adjust position and check for border or pane. */
337: if (where == NOWHERE) {
338: if (m->statusat == 0 && y > 0)
339: y--;
340: else if (m->statusat > 0 && y >= (u_int)m->statusat)
341: y = m->statusat - 1;
342:
343: TAILQ_FOREACH(wp, &s->curw->window->panes, entry) {
344: if ((wp->xoff + wp->sx == x &&
345: wp->yoff <= 1 + y &&
346: wp->yoff + wp->sy >= y) ||
347: (wp->yoff + wp->sy == y &&
348: wp->xoff <= 1 + x &&
349: wp->xoff + wp->sx >= x))
350: break;
351: }
352: if (wp != NULL)
353: where = BORDER;
354: else {
355: wp = window_get_active_at(s->curw->window, x, y);
356: if (wp != NULL)
357: where = PANE;
358: }
359: if (where == NOWHERE)
360: return (KEYC_NONE);
361: m->wp = wp->id;
362: m->w = wp->window->id;
363: } else
364: m->wp = -1;
365:
366: /* Stop dragging if needed. */
367: if (type != DRAG && c->tty.mouse_drag_flag) {
368: if (c->tty.mouse_drag_release != NULL)
369: c->tty.mouse_drag_release(c, m);
370:
371: c->tty.mouse_drag_update = NULL;
372: c->tty.mouse_drag_release = NULL;
373:
374: c->tty.mouse_drag_flag = 0;
1.133 nicm 375: return (KEYC_MOUSE); /* not a key, but still may want to pass */
1.131 nicm 376: }
377:
378: /* Convert to a key binding. */
379: key = KEYC_NONE;
380: switch (type) {
381: case NOTYPE:
382: break;
383: case DRAG:
384: if (c->tty.mouse_drag_update != NULL)
385: c->tty.mouse_drag_update(c, m);
386: else {
387: switch (MOUSE_BUTTONS(b)) {
388: case 0:
389: if (where == PANE)
390: key = KEYC_MOUSEDRAG1_PANE;
391: if (where == STATUS)
392: key = KEYC_MOUSEDRAG1_STATUS;
393: if (where == BORDER)
394: key = KEYC_MOUSEDRAG1_BORDER;
395: break;
396: case 1:
397: if (where == PANE)
398: key = KEYC_MOUSEDRAG2_PANE;
399: if (where == STATUS)
400: key = KEYC_MOUSEDRAG2_STATUS;
401: if (where == BORDER)
402: key = KEYC_MOUSEDRAG2_BORDER;
403: break;
404: case 2:
405: if (where == PANE)
406: key = KEYC_MOUSEDRAG3_PANE;
407: if (where == STATUS)
408: key = KEYC_MOUSEDRAG3_STATUS;
409: if (where == BORDER)
410: key = KEYC_MOUSEDRAG3_BORDER;
411: break;
412: }
413: }
1.66 nicm 414:
1.131 nicm 415: c->tty.mouse_drag_flag = 1;
416: break;
417: case WHEEL:
418: if (MOUSE_BUTTONS(b) == MOUSE_WHEEL_UP) {
419: if (where == PANE)
420: key = KEYC_WHEELUP_PANE;
421: if (where == STATUS)
422: key = KEYC_WHEELUP_STATUS;
423: if (where == BORDER)
424: key = KEYC_WHEELUP_BORDER;
425: } else {
426: if (where == PANE)
427: key = KEYC_WHEELDOWN_PANE;
428: if (where == STATUS)
429: key = KEYC_WHEELDOWN_STATUS;
430: if (where == BORDER)
431: key = KEYC_WHEELDOWN_BORDER;
432: }
433: break;
434: case UP:
435: switch (MOUSE_BUTTONS(b)) {
436: case 0:
437: if (where == PANE)
438: key = KEYC_MOUSEUP1_PANE;
439: if (where == STATUS)
440: key = KEYC_MOUSEUP1_STATUS;
441: if (where == BORDER)
442: key = KEYC_MOUSEUP1_BORDER;
443: break;
444: case 1:
445: if (where == PANE)
446: key = KEYC_MOUSEUP2_PANE;
447: if (where == STATUS)
448: key = KEYC_MOUSEUP2_STATUS;
449: if (where == BORDER)
450: key = KEYC_MOUSEUP2_BORDER;
451: break;
452: case 2:
453: if (where == PANE)
454: key = KEYC_MOUSEUP3_PANE;
455: if (where == STATUS)
456: key = KEYC_MOUSEUP3_STATUS;
457: if (where == BORDER)
458: key = KEYC_MOUSEUP3_BORDER;
459: break;
460: }
461: break;
462: case DOWN:
463: switch (MOUSE_BUTTONS(b)) {
464: case 0:
465: if (where == PANE)
466: key = KEYC_MOUSEDOWN1_PANE;
467: if (where == STATUS)
468: key = KEYC_MOUSEDOWN1_STATUS;
469: if (where == BORDER)
470: key = KEYC_MOUSEDOWN1_BORDER;
471: break;
472: case 1:
473: if (where == PANE)
474: key = KEYC_MOUSEDOWN2_PANE;
475: if (where == STATUS)
476: key = KEYC_MOUSEDOWN2_STATUS;
477: if (where == BORDER)
478: key = KEYC_MOUSEDOWN2_BORDER;
479: break;
480: case 2:
481: if (where == PANE)
482: key = KEYC_MOUSEDOWN3_PANE;
483: if (where == STATUS)
484: key = KEYC_MOUSEDOWN3_STATUS;
485: if (where == BORDER)
486: key = KEYC_MOUSEDOWN3_BORDER;
487: break;
1.66 nicm 488: }
1.131 nicm 489: break;
1.66 nicm 490: }
1.131 nicm 491: if (key == KEYC_NONE)
492: return (KEYC_NONE);
1.66 nicm 493:
1.131 nicm 494: /* Apply modifiers if any. */
495: if (b & MOUSE_MASK_META)
496: key |= KEYC_ESCAPE;
497: if (b & MOUSE_MASK_CTRL)
498: key |= KEYC_CTRL;
499: if (b & MOUSE_MASK_SHIFT)
500: key |= KEYC_SHIFT;
1.66 nicm 501:
1.131 nicm 502: return (key);
1.66 nicm 503: }
504:
1.82 nicm 505: /* Is this fast enough to probably be a paste? */
506: int
507: server_client_assume_paste(struct session *s)
508: {
509: struct timeval tv;
1.84 nicm 510: int t;
1.82 nicm 511:
512: if ((t = options_get_number(&s->options, "assume-paste-time")) == 0)
1.83 nicm 513: return (0);
1.82 nicm 514:
515: timersub(&s->activity_time, &s->last_activity_time, &tv);
516: if (tv.tv_sec == 0 && tv.tv_usec < t * 1000)
1.83 nicm 517: return (1);
518: return (0);
1.82 nicm 519: }
520:
1.18 nicm 521: /* Handle data key input from client. */
522: void
1.74 nicm 523: server_client_handle_key(struct client *c, int key)
1.18 nicm 524: {
1.131 nicm 525: struct mouse_event *m = &c->tty.mouse;
1.132 nicm 526: struct session *s = c->session;
1.18 nicm 527: struct window *w;
528: struct window_pane *wp;
529: struct timeval tv;
1.132 nicm 530: struct key_table *table = c->keytable;
531: struct key_binding bd_find, *bd;
532: int xtimeout;
1.18 nicm 533:
534: /* Check the client is good to accept input. */
1.132 nicm 535: if (s == NULL || (c->flags & (CLIENT_DEAD|CLIENT_SUSPENDED)) != 0)
1.18 nicm 536: return;
1.132 nicm 537: w = s->curw->window;
538: wp = w->active;
1.86 nicm 539:
1.131 nicm 540: /* No session, do nothing. */
1.18 nicm 541: if (c->session == NULL)
542: return;
543: s = c->session;
1.131 nicm 544: w = c->session->curw->window;
545: wp = w->active;
1.18 nicm 546:
547: /* Update the activity timer. */
548: if (gettimeofday(&c->activity_time, NULL) != 0)
549: fatal("gettimeofday failed");
1.82 nicm 550: memcpy(&s->last_activity_time, &s->activity_time,
551: sizeof s->last_activity_time);
1.18 nicm 552: memcpy(&s->activity_time, &c->activity_time, sizeof s->activity_time);
553:
1.132 nicm 554: /* Number keys jump to pane in identify mode. */
1.25 nicm 555: if (c->flags & CLIENT_IDENTIFY && key >= '0' && key <= '9') {
1.30 nicm 556: if (c->flags & CLIENT_READONLY)
557: return;
1.95 nicm 558: window_unzoom(w);
1.18 nicm 559: wp = window_pane_at_index(w, key - '0');
560: if (wp != NULL && window_pane_visible(wp))
561: window_set_active_pane(w, wp);
562: server_clear_identify(c);
563: return;
564: }
565:
566: /* Handle status line. */
1.30 nicm 567: if (!(c->flags & CLIENT_READONLY)) {
568: status_message_clear(c);
569: server_clear_identify(c);
570: }
1.18 nicm 571: if (c->prompt_string != NULL) {
1.30 nicm 572: if (!(c->flags & CLIENT_READONLY))
573: status_prompt_key(c, key);
1.18 nicm 574: return;
575: }
576:
577: /* Check for mouse keys. */
578: if (key == KEYC_MOUSE) {
1.30 nicm 579: if (c->flags & CLIENT_READONLY)
580: return;
1.131 nicm 581: key = server_client_check_mouse(c);
582: if (key == KEYC_NONE)
583: return;
584:
585: m->valid = 1;
586: m->key = key;
587:
588: if (!options_get_number(&s->options, "mouse")) {
589: window_pane_key(wp, c, s, key, m);
590: return;
591: }
592: } else
593: m->valid = 0;
1.18 nicm 594:
1.132 nicm 595: /* Treat everything as a regular key when pasting is detected. */
596: if (server_client_assume_paste(s)) {
597: if (!(c->flags & CLIENT_READONLY))
598: window_pane_key(wp, c, s, key, m);
599: return;
600: }
1.18 nicm 601:
1.132 nicm 602: retry:
603: /* Try to see if there is a key binding in the current table. */
604: bd_find.key = key;
605: bd = RB_FIND(key_bindings, &table->key_bindings, &bd_find);
606: if (bd != NULL) {
607: /*
608: * Key was matched in this table. If currently repeating but a
609: * non-repeating binding was found, stop repeating and try
610: * again in the root table.
611: */
612: if ((c->flags & CLIENT_REPEAT) && !bd->can_repeat) {
613: server_client_key_table(c, "root");
614: c->flags &= ~CLIENT_REPEAT;
1.85 nicm 615: server_status_client(c);
1.132 nicm 616: goto retry;
1.18 nicm 617: }
1.82 nicm 618:
1.132 nicm 619: /*
620: * Take a reference to this table to make sure the key binding
621: * doesn't disappear.
622: */
623: table->references++;
624:
625: /*
626: * If this is a repeating key, start the timer. Otherwise reset
627: * the client back to the root table.
628: */
629: xtimeout = options_get_number(&s->options, "repeat-time");
630: if (xtimeout != 0 && bd->can_repeat) {
631: c->flags |= CLIENT_REPEAT;
632:
633: tv.tv_sec = xtimeout / 1000;
634: tv.tv_usec = (xtimeout % 1000) * 1000L;
635: evtimer_del(&c->repeat_timer);
636: evtimer_add(&c->repeat_timer, &tv);
637: } else {
1.18 nicm 638: c->flags &= ~CLIENT_REPEAT;
1.132 nicm 639: server_client_key_table(c, "root");
1.18 nicm 640: }
1.132 nicm 641: server_status_client(c);
642:
643: /* Dispatch the key binding. */
644: key_bindings_dispatch(bd, c, m);
645: key_bindings_unref_table(table);
1.18 nicm 646: return;
647: }
648:
1.132 nicm 649: /*
650: * No match in this table. If repeating, switch the client back to the
651: * root table and try again.
652: */
653: if (c->flags & CLIENT_REPEAT) {
654: server_client_key_table(c, "root");
1.18 nicm 655: c->flags &= ~CLIENT_REPEAT;
1.132 nicm 656: server_status_client(c);
657: goto retry;
1.18 nicm 658: }
659:
1.132 nicm 660: /* If no match and we're not in the root table, that's it. */
661: if (strcmp(c->keytable->name, "root") != 0) {
662: server_client_key_table(c, "root");
663: server_status_client(c);
664: return;
1.18 nicm 665: }
666:
1.132 nicm 667: /*
668: * No match, but in the root table. Prefix switches to the prefix table
669: * and everything else is passed through.
670: */
671: if (key == options_get_number(&s->options, "prefix") ||
672: key == options_get_number(&s->options, "prefix2")) {
673: server_client_key_table(c, "prefix");
674: server_status_client(c);
675: } else if (!(c->flags & CLIENT_READONLY))
676: window_pane_key(wp, c, s, key, m);
1.18 nicm 677: }
678:
1.2 nicm 679: /* Client functions that need to happen every loop. */
680: void
681: server_client_loop(void)
682: {
683: struct client *c;
684: struct window *w;
685: struct window_pane *wp;
686:
1.135 ! nicm 687: TAILQ_FOREACH(c, &clients, entry) {
1.36 nicm 688: server_client_check_exit(c);
689: if (c->session != NULL) {
690: server_client_check_redraw(c);
691: server_client_reset_state(c);
692: }
1.2 nicm 693: }
694:
695: /*
696: * Any windows will have been redrawn as part of clients, so clear
1.92 nicm 697: * their flags now. Also check pane focus and resize.
1.2 nicm 698: */
1.134 nicm 699: RB_FOREACH(w, windows, &windows) {
1.2 nicm 700: w->flags &= ~WINDOW_REDRAW;
1.91 nicm 701: TAILQ_FOREACH(wp, &w->panes, entry) {
1.101 nicm 702: if (wp->fd != -1) {
703: server_client_check_focus(wp);
704: server_client_check_resize(wp);
705: }
1.2 nicm 706: wp->flags &= ~PANE_REDRAW;
1.91 nicm 707: }
1.2 nicm 708: }
1.92 nicm 709: }
710:
711: /* Check if pane should be resized. */
712: void
713: server_client_check_resize(struct window_pane *wp)
714: {
715: struct winsize ws;
716:
1.101 nicm 717: if (!(wp->flags & PANE_RESIZE))
1.92 nicm 718: return;
719:
720: memset(&ws, 0, sizeof ws);
721: ws.ws_col = wp->sx;
722: ws.ws_row = wp->sy;
723:
1.100 nicm 724: if (ioctl(wp->fd, TIOCSWINSZ, &ws) == -1)
1.92 nicm 725: fatal("ioctl failed");
726:
727: wp->flags &= ~PANE_RESIZE;
1.91 nicm 728: }
729:
730: /* Check whether pane should be focused. */
731: void
732: server_client_check_focus(struct window_pane *wp)
733: {
1.93 nicm 734: struct client *c;
1.102 nicm 735: int push;
1.103 nicm 736:
737: /* Are focus events off? */
738: if (!options_get_number(&global_options, "focus-events"))
739: return;
1.102 nicm 740:
741: /* Do we need to push the focus state? */
742: push = wp->flags & PANE_FOCUSPUSH;
743: wp->flags &= ~PANE_FOCUSPUSH;
1.91 nicm 744:
745: /* If we don't care about focus, forget it. */
746: if (!(wp->base.mode & MODE_FOCUSON))
747: return;
748:
749: /* If we're not the active pane in our window, we're not focused. */
750: if (wp->window->active != wp)
751: goto not_focused;
752:
753: /* If we're in a mode, we're not focused. */
754: if (wp->screen != &wp->base)
755: goto not_focused;
756:
757: /*
1.93 nicm 758: * If our window is the current window in any focused clients with an
759: * attached session, we're focused.
1.91 nicm 760: */
1.135 ! nicm 761: TAILQ_FOREACH(c, &clients, entry) {
! 762: if (c->session == NULL || !(c->flags & CLIENT_FOCUSED))
1.91 nicm 763: continue;
1.93 nicm 764: if (c->session->flags & SESSION_UNATTACHED)
765: continue;
766:
767: if (c->session->curw->window == wp->window)
1.91 nicm 768: goto focused;
769: }
770:
771: not_focused:
1.102 nicm 772: if (push || (wp->flags & PANE_FOCUSED))
1.91 nicm 773: bufferevent_write(wp->event, "\033[O", 3);
774: wp->flags &= ~PANE_FOCUSED;
775: return;
776:
777: focused:
1.102 nicm 778: if (push || !(wp->flags & PANE_FOCUSED))
1.91 nicm 779: bufferevent_write(wp->event, "\033[I", 3);
780: wp->flags |= PANE_FOCUSED;
1.2 nicm 781: }
782:
1.18 nicm 783: /*
784: * Update cursor position and mode settings. The scroll region and attributes
785: * are cleared when idle (waiting for an event) as this is the most likely time
786: * a user may interrupt tmux, for example with ~^Z in ssh(1). This is a
787: * compromise between excessive resets and likelihood of an interrupt.
788: *
789: * tty_region/tty_reset/tty_update_mode already take care of not resetting
790: * things that are already in their default state.
791: */
1.1 nicm 792: void
1.18 nicm 793: server_client_reset_state(struct client *c)
1.1 nicm 794: {
1.18 nicm 795: struct window *w = c->session->curw->window;
796: struct window_pane *wp = w->active;
797: struct screen *s = wp->screen;
798: struct options *oo = &c->session->options;
1.66 nicm 799: int status, mode, o;
1.60 nicm 800:
801: if (c->flags & CLIENT_SUSPENDED)
802: return;
1.1 nicm 803:
1.86 nicm 804: if (c->flags & CLIENT_CONTROL)
805: return;
806:
1.1 nicm 807: tty_region(&c->tty, 0, c->tty.sy - 1);
808:
809: status = options_get_number(oo, "status");
810: if (!window_pane_visible(wp) || wp->yoff + s->cy >= c->tty.sy - status)
811: tty_cursor(&c->tty, 0, 0);
1.66 nicm 812: else {
1.126 nicm 813: o = status && options_get_number(oo, "status-position") == 0;
1.66 nicm 814: tty_cursor(&c->tty, wp->xoff + s->cx, o + wp->yoff + s->cy);
815: }
1.1 nicm 816:
1.50 nicm 817: /*
1.131 nicm 818: * Set mouse mode if requested. To support dragging, always use button
819: * mode.
1.57 nicm 820: */
821: mode = s->mode;
1.131 nicm 822: if (options_get_number(oo, "mouse"))
823: mode = (mode & ~ALL_MOUSE_MODES) | MODE_MOUSE_BUTTON;
1.48 nicm 824:
825: /*
826: * Set UTF-8 mouse input if required. If the terminal is UTF-8, the
827: * user has set mouse-utf8 and any mouse mode is in effect, turn on
828: * UTF-8 mouse input. If the receiving terminal hasn't requested it
829: * (that is, it isn't in s->mode), then it'll be converted in
830: * input_mouse.
831: */
832: if ((c->tty.flags & TTY_UTF8) &&
833: (mode & ALL_MOUSE_MODES) && options_get_number(oo, "mouse-utf8"))
834: mode |= MODE_MOUSE_UTF8;
835: else
836: mode &= ~MODE_MOUSE_UTF8;
837:
838: /* Set the terminal mode and reset attributes. */
1.59 nicm 839: tty_update_mode(&c->tty, mode, s);
1.1 nicm 840: tty_reset(&c->tty);
1.17 nicm 841: }
842:
843: /* Repeat time callback. */
844: void
845: server_client_repeat_timer(unused int fd, unused short events, void *data)
846: {
847: struct client *c = data;
848:
1.85 nicm 849: if (c->flags & CLIENT_REPEAT) {
1.132 nicm 850: server_client_key_table(c, "root");
851: c->flags &= ~CLIENT_REPEAT;
852: server_status_client(c);
1.85 nicm 853: }
1.1 nicm 854: }
855:
1.36 nicm 856: /* Check if client should be exited. */
857: void
858: server_client_check_exit(struct client *c)
859: {
860: if (!(c->flags & CLIENT_EXIT))
861: return;
862:
1.73 nicm 863: if (EVBUFFER_LENGTH(c->stdin_data) != 0)
864: return;
865: if (EVBUFFER_LENGTH(c->stdout_data) != 0)
1.36 nicm 866: return;
1.73 nicm 867: if (EVBUFFER_LENGTH(c->stderr_data) != 0)
1.36 nicm 868: return;
869:
1.107 nicm 870: server_write_client(c, MSG_EXIT, &c->retval, sizeof c->retval);
1.36 nicm 871: c->flags &= ~CLIENT_EXIT;
1.38 nicm 872: }
873:
1.1 nicm 874: /* Check for client redraws. */
875: void
876: server_client_check_redraw(struct client *c)
877: {
878: struct session *s = c->session;
879: struct window_pane *wp;
880: int flags, redraw;
881:
1.86 nicm 882: if (c->flags & (CLIENT_CONTROL|CLIENT_SUSPENDED))
1.79 nicm 883: return;
884:
1.1 nicm 885: flags = c->tty.flags & TTY_FREEZE;
886: c->tty.flags &= ~TTY_FREEZE;
887:
888: if (c->flags & (CLIENT_REDRAW|CLIENT_STATUS)) {
889: if (options_get_number(&s->options, "set-titles"))
890: server_client_set_title(c);
1.12 nicm 891:
1.1 nicm 892: if (c->message_string != NULL)
893: redraw = status_message_redraw(c);
894: else if (c->prompt_string != NULL)
895: redraw = status_prompt_redraw(c);
896: else
897: redraw = status_redraw(c);
898: if (!redraw)
899: c->flags &= ~CLIENT_STATUS;
900: }
901:
902: if (c->flags & CLIENT_REDRAW) {
1.115 nicm 903: screen_redraw_screen(c, 1, 1, 1);
1.27 nicm 904: c->flags &= ~(CLIENT_STATUS|CLIENT_BORDERS);
1.38 nicm 905: } else if (c->flags & CLIENT_REDRAWWINDOW) {
906: TAILQ_FOREACH(wp, &c->session->curw->window->panes, entry)
907: screen_redraw_pane(c, wp);
908: c->flags &= ~CLIENT_REDRAWWINDOW;
1.1 nicm 909: } else {
910: TAILQ_FOREACH(wp, &c->session->curw->window->panes, entry) {
911: if (wp->flags & PANE_REDRAW)
912: screen_redraw_pane(c, wp);
913: }
914: }
915:
1.27 nicm 916: if (c->flags & CLIENT_BORDERS)
1.115 nicm 917: screen_redraw_screen(c, 0, 0, 1);
1.27 nicm 918:
1.1 nicm 919: if (c->flags & CLIENT_STATUS)
1.115 nicm 920: screen_redraw_screen(c, 0, 1, 0);
1.1 nicm 921:
922: c->tty.flags |= flags;
923:
1.27 nicm 924: c->flags &= ~(CLIENT_REDRAW|CLIENT_STATUS|CLIENT_BORDERS);
1.1 nicm 925: }
926:
927: /* Set client title. */
928: void
929: server_client_set_title(struct client *c)
930: {
1.128 nicm 931: struct session *s = c->session;
932: const char *template;
933: char *title;
934: struct format_tree *ft;
1.1 nicm 935:
936: template = options_get_string(&s->options, "set-titles-string");
1.25 nicm 937:
1.128 nicm 938: ft = format_create();
939: format_defaults(ft, c, NULL, NULL, NULL);
940:
941: title = format_expand_time(ft, template, time(NULL));
1.1 nicm 942: if (c->title == NULL || strcmp(title, c->title) != 0) {
1.77 nicm 943: free(c->title);
1.1 nicm 944: c->title = xstrdup(title);
945: tty_set_title(&c->tty, c->title);
946: }
1.77 nicm 947: free(title);
1.128 nicm 948:
949: format_free(ft);
1.1 nicm 950: }
951:
952: /* Dispatch message from client. */
953: int
954: server_client_msg_dispatch(struct client *c)
955: {
956: struct imsg imsg;
1.73 nicm 957: struct msg_stdin_data stdindata;
1.108 nicm 958: const char *data;
1.1 nicm 959: ssize_t n, datalen;
960:
1.8 deraadt 961: if ((n = imsg_read(&c->ibuf)) == -1 || n == 0)
962: return (-1);
1.1 nicm 963:
964: for (;;) {
965: if ((n = imsg_get(&c->ibuf, &imsg)) == -1)
966: return (-1);
967: if (n == 0)
968: return (0);
1.108 nicm 969:
970: data = imsg.data;
1.1 nicm 971: datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
972:
973: if (imsg.hdr.peerid != PROTOCOL_VERSION) {
974: server_write_client(c, MSG_VERSION, NULL, 0);
975: c->flags |= CLIENT_BAD;
1.112 nicm 976: if (imsg.fd != -1)
977: close(imsg.fd);
1.1 nicm 978: imsg_free(&imsg);
979: continue;
980: }
981:
1.129 nicm 982: log_debug("got %u from client %d", imsg.hdr.type, c->ibuf.fd);
1.1 nicm 983: switch (imsg.hdr.type) {
1.109 nicm 984: case MSG_IDENTIFY_FLAGS:
985: case MSG_IDENTIFY_TERM:
986: case MSG_IDENTIFY_TTYNAME:
987: case MSG_IDENTIFY_CWD:
988: case MSG_IDENTIFY_STDIN:
989: case MSG_IDENTIFY_ENVIRON:
990: case MSG_IDENTIFY_DONE:
991: server_client_msg_identify(c, &imsg);
1.1 nicm 992: break;
1.108 nicm 993: case MSG_COMMAND:
994: server_client_msg_command(c, &imsg);
995: break;
1.73 nicm 996: case MSG_STDIN:
997: if (datalen != sizeof stdindata)
998: fatalx("bad MSG_STDIN size");
1.108 nicm 999: memcpy(&stdindata, data, sizeof stdindata);
1.36 nicm 1000:
1.73 nicm 1001: if (c->stdin_callback == NULL)
1002: break;
1003: if (stdindata.size <= 0)
1004: c->stdin_closed = 1;
1005: else {
1006: evbuffer_add(c->stdin_data, stdindata.data,
1007: stdindata.size);
1008: }
1009: c->stdin_callback(c, c->stdin_closed,
1010: c->stdin_callback_data);
1.33 nicm 1011: break;
1.1 nicm 1012: case MSG_RESIZE:
1013: if (datalen != 0)
1014: fatalx("bad MSG_RESIZE size");
1015:
1.86 nicm 1016: if (c->flags & CLIENT_CONTROL)
1017: break;
1.32 nicm 1018: if (tty_resize(&c->tty)) {
1019: recalculate_sizes();
1020: server_redraw_client(c);
1021: }
1.1 nicm 1022: break;
1023: case MSG_EXITING:
1024: if (datalen != 0)
1025: fatalx("bad MSG_EXITING size");
1026:
1027: c->session = NULL;
1028: tty_close(&c->tty);
1029: server_write_client(c, MSG_EXITED, NULL, 0);
1030: break;
1031: case MSG_WAKEUP:
1032: case MSG_UNLOCK:
1033: if (datalen != 0)
1034: fatalx("bad MSG_WAKEUP size");
1035:
1036: if (!(c->flags & CLIENT_SUSPENDED))
1037: break;
1038: c->flags &= ~CLIENT_SUSPENDED;
1.121 nicm 1039:
1040: if (c->tty.fd == -1) /* exited in the meantime */
1041: break;
1.10 nicm 1042:
1.11 nicm 1043: if (gettimeofday(&c->activity_time, NULL) != 0)
1044: fatal("gettimeofday");
1.47 nicm 1045: if (c->session != NULL)
1046: session_update_activity(c->session);
1.10 nicm 1047:
1.1 nicm 1048: tty_start_tty(&c->tty);
1049: server_redraw_client(c);
1050: recalculate_sizes();
1051: break;
1052: case MSG_SHELL:
1053: if (datalen != 0)
1054: fatalx("bad MSG_SHELL size");
1055:
1056: server_client_msg_shell(c);
1057: break;
1058: }
1059:
1060: imsg_free(&imsg);
1061: }
1062: }
1063:
1064: /* Handle command message. */
1065: void
1.108 nicm 1066: server_client_msg_command(struct client *c, struct imsg *imsg)
1.1 nicm 1067: {
1.108 nicm 1068: struct msg_command_data data;
1069: char *buf;
1070: size_t len;
1071: struct cmd_list *cmdlist = NULL;
1072: int argc;
1073: char **argv, *cause;
1074:
1075: if (imsg->hdr.len - IMSG_HEADER_SIZE < sizeof data)
1076: fatalx("bad MSG_COMMAND size");
1077: memcpy(&data, imsg->data, sizeof data);
1078:
1.124 nicm 1079: buf = (char *)imsg->data + sizeof data;
1.108 nicm 1080: len = imsg->hdr.len - IMSG_HEADER_SIZE - sizeof data;
1081: if (len > 0 && buf[len - 1] != '\0')
1082: fatalx("bad MSG_COMMAND string");
1083:
1084: argc = data.argc;
1085: if (cmd_unpack_argv(buf, len, argc, &argv) != 0) {
1.94 nicm 1086: cmdq_error(c->cmdq, "command too long");
1.1 nicm 1087: goto error;
1088: }
1089:
1090: if (argc == 0) {
1091: argc = 1;
1092: argv = xcalloc(1, sizeof *argv);
1093: *argv = xstrdup("new-session");
1094: }
1095:
1.94 nicm 1096: if ((cmdlist = cmd_list_parse(argc, argv, NULL, 0, &cause)) == NULL) {
1097: cmdq_error(c->cmdq, "%s", cause);
1.1 nicm 1098: cmd_free_argv(argc, argv);
1099: goto error;
1100: }
1101: cmd_free_argv(argc, argv);
1102:
1.113 nicm 1103: if (c != cfg_client || cfg_finished)
1.131 nicm 1104: cmdq_run(c->cmdq, cmdlist, NULL);
1.113 nicm 1105: else
1.131 nicm 1106: cmdq_append(c->cmdq, cmdlist, NULL);
1.1 nicm 1107: cmd_list_free(cmdlist);
1108: return;
1109:
1110: error:
1111: if (cmdlist != NULL)
1112: cmd_list_free(cmdlist);
1.88 nicm 1113:
1.36 nicm 1114: c->flags |= CLIENT_EXIT;
1.1 nicm 1115: }
1116:
1117: /* Handle identify message. */
1118: void
1.109 nicm 1119: server_client_msg_identify(struct client *c, struct imsg *imsg)
1.1 nicm 1120: {
1.109 nicm 1121: const char *data;
1122: size_t datalen;
1123: int flags;
1124:
1125: if (c->flags & CLIENT_IDENTIFIED)
1126: fatalx("out-of-order identify message");
1127:
1128: data = imsg->data;
1129: datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
1130:
1131: switch (imsg->hdr.type) {
1132: case MSG_IDENTIFY_FLAGS:
1133: if (datalen != sizeof flags)
1134: fatalx("bad MSG_IDENTIFY_FLAGS size");
1135: memcpy(&flags, data, sizeof flags);
1136: c->flags |= flags;
1137: break;
1138: case MSG_IDENTIFY_TERM:
1.110 nicm 1139: if (datalen == 0 || data[datalen - 1] != '\0')
1.109 nicm 1140: fatalx("bad MSG_IDENTIFY_TERM string");
1141: c->term = xstrdup(data);
1142: break;
1143: case MSG_IDENTIFY_TTYNAME:
1.110 nicm 1144: if (datalen == 0 || data[datalen - 1] != '\0')
1.109 nicm 1145: fatalx("bad MSG_IDENTIFY_TTYNAME string");
1146: c->ttyname = xstrdup(data);
1147: break;
1148: case MSG_IDENTIFY_CWD:
1149: if (datalen != 0)
1150: fatalx("bad MSG_IDENTIFY_CWD size");
1151: c->cwd = imsg->fd;
1152: break;
1153: case MSG_IDENTIFY_STDIN:
1154: if (datalen != 0)
1155: fatalx("bad MSG_IDENTIFY_STDIN size");
1156: c->fd = imsg->fd;
1157: break;
1158: case MSG_IDENTIFY_ENVIRON:
1.110 nicm 1159: if (datalen == 0 || data[datalen - 1] != '\0')
1.109 nicm 1160: fatalx("bad MSG_IDENTIFY_ENVIRON string");
1161: if (strchr(data, '=') != NULL)
1162: environ_put(&c->environ, data);
1163: break;
1164: default:
1165: break;
1166: }
1167:
1168: if (imsg->hdr.type != MSG_IDENTIFY_DONE)
1169: return;
1170: c->flags |= CLIENT_IDENTIFIED;
1171:
1172: #ifdef __CYGWIN__
1173: c->fd = open(c->ttyname, O_RDWR|O_NOCTTY);
1174: c->cwd = open(".", O_RDONLY);
1175: #endif
1.75 nicm 1176:
1.109 nicm 1177: if (c->flags & CLIENT_CONTROL) {
1.75 nicm 1178: c->stdin_callback = control_callback;
1.109 nicm 1179:
1.97 nicm 1180: evbuffer_free(c->stderr_data);
1181: c->stderr_data = c->stdout_data;
1.109 nicm 1182:
1183: if (c->flags & CLIENT_CONTROLCONTROL)
1.96 nicm 1184: evbuffer_add_printf(c->stdout_data, "\033P1000p");
1.79 nicm 1185: server_write_client(c, MSG_STDIN, NULL, 0);
1.75 nicm 1186:
1187: c->tty.fd = -1;
1188: c->tty.log_fd = -1;
1189:
1.109 nicm 1190: close(c->fd);
1191: c->fd = -1;
1192:
1.75 nicm 1193: return;
1194: }
1.1 nicm 1195:
1.109 nicm 1196: if (c->fd == -1)
1.104 nicm 1197: return;
1.109 nicm 1198: if (!isatty(c->fd)) {
1199: close(c->fd);
1200: c->fd = -1;
1.80 nicm 1201: return;
1202: }
1.109 nicm 1203: tty_init(&c->tty, c, c->fd, c->term);
1204: if (c->flags & CLIENT_UTF8)
1.1 nicm 1205: c->tty.flags |= TTY_UTF8;
1.109 nicm 1206: if (c->flags & CLIENT_256COLOURS)
1.1 nicm 1207: c->tty.term_flags |= TERM_256COLOURS;
1208:
1209: tty_resize(&c->tty);
1210:
1.109 nicm 1211: if (!(c->flags & CLIENT_CONTROL))
1.86 nicm 1212: c->flags |= CLIENT_TERMINAL;
1.1 nicm 1213: }
1214:
1215: /* Handle shell message. */
1216: void
1217: server_client_msg_shell(struct client *c)
1218: {
1.107 nicm 1219: const char *shell;
1.25 nicm 1220:
1.1 nicm 1221: shell = options_get_string(&global_s_options, "default-shell");
1222: if (*shell == '\0' || areshell(shell))
1223: shell = _PATH_BSHELL;
1.107 nicm 1224: server_write_client(c, MSG_SHELL, shell, strlen(shell) + 1);
1.25 nicm 1225:
1.1 nicm 1226: c->flags |= CLIENT_BAD; /* it will die after exec */
1227: }