Merge branch 'obsd-master'
[tmux.git] / server-client.c
blob9ced4482e0ebe1dc784289df180fa1b842cc272c
1 /* $OpenBSD$ */
3 /*
4 * Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
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.
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.
19 #include <sys/types.h>
20 #include <sys/ioctl.h>
21 #include <sys/uio.h>
23 #include <errno.h>
24 #include <fcntl.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <time.h>
28 #include <unistd.h>
30 #include "tmux.h"
32 enum mouse_where {
33 NOWHERE,
34 PANE,
35 STATUS,
36 STATUS_LEFT,
37 STATUS_RIGHT,
38 STATUS_DEFAULT,
39 BORDER,
40 SCROLLBAR_UP,
41 SCROLLBAR_SLIDER,
42 SCROLLBAR_DOWN
45 static void server_client_free(int, short, void *);
46 static void server_client_check_pane_resize(struct window_pane *);
47 static void server_client_check_pane_buffer(struct window_pane *);
48 static void server_client_check_window_resize(struct window *);
49 static key_code server_client_check_mouse(struct client *, struct key_event *);
50 static void server_client_repeat_timer(int, short, void *);
51 static void server_client_click_timer(int, short, void *);
52 static void server_client_check_exit(struct client *);
53 static void server_client_check_redraw(struct client *);
54 static void server_client_check_modes(struct client *);
55 static void server_client_set_title(struct client *);
56 static void server_client_set_path(struct client *);
57 static void server_client_reset_state(struct client *);
58 static void server_client_update_latest(struct client *);
60 static void server_client_dispatch(struct imsg *, void *);
61 static void server_client_dispatch_command(struct client *, struct imsg *);
62 static void server_client_dispatch_identify(struct client *, struct imsg *);
63 static void server_client_dispatch_shell(struct client *);
65 /* Compare client windows. */
66 static int
67 server_client_window_cmp(struct client_window *cw1,
68 struct client_window *cw2)
70 if (cw1->window < cw2->window)
71 return (-1);
72 if (cw1->window > cw2->window)
73 return (1);
74 return (0);
76 RB_GENERATE(client_windows, client_window, entry, server_client_window_cmp);
78 /* Number of attached clients. */
79 u_int
80 server_client_how_many(void)
82 struct client *c;
83 u_int n;
85 n = 0;
86 TAILQ_FOREACH(c, &clients, entry) {
87 if (c->session != NULL && (~c->flags & CLIENT_UNATTACHEDFLAGS))
88 n++;
90 return (n);
93 /* Overlay timer callback. */
94 static void
95 server_client_overlay_timer(__unused int fd, __unused short events, void *data)
97 server_client_clear_overlay(data);
100 /* Set an overlay on client. */
101 void
102 server_client_set_overlay(struct client *c, u_int delay,
103 overlay_check_cb checkcb, overlay_mode_cb modecb,
104 overlay_draw_cb drawcb, overlay_key_cb keycb, overlay_free_cb freecb,
105 overlay_resize_cb resizecb, void *data)
107 struct timeval tv;
109 if (c->overlay_draw != NULL)
110 server_client_clear_overlay(c);
112 tv.tv_sec = delay / 1000;
113 tv.tv_usec = (delay % 1000) * 1000L;
115 if (event_initialized(&c->overlay_timer))
116 evtimer_del(&c->overlay_timer);
117 evtimer_set(&c->overlay_timer, server_client_overlay_timer, c);
118 if (delay != 0)
119 evtimer_add(&c->overlay_timer, &tv);
121 c->overlay_check = checkcb;
122 c->overlay_mode = modecb;
123 c->overlay_draw = drawcb;
124 c->overlay_key = keycb;
125 c->overlay_free = freecb;
126 c->overlay_resize = resizecb;
127 c->overlay_data = data;
129 if (c->overlay_check == NULL)
130 c->tty.flags |= TTY_FREEZE;
131 if (c->overlay_mode == NULL)
132 c->tty.flags |= TTY_NOCURSOR;
133 window_update_focus(c->session->curw->window);
134 server_redraw_client(c);
137 /* Clear overlay mode on client. */
138 void
139 server_client_clear_overlay(struct client *c)
141 if (c->overlay_draw == NULL)
142 return;
144 if (event_initialized(&c->overlay_timer))
145 evtimer_del(&c->overlay_timer);
147 if (c->overlay_free != NULL)
148 c->overlay_free(c, c->overlay_data);
150 c->overlay_check = NULL;
151 c->overlay_mode = NULL;
152 c->overlay_draw = NULL;
153 c->overlay_key = NULL;
154 c->overlay_free = NULL;
155 c->overlay_data = NULL;
157 c->tty.flags &= ~(TTY_FREEZE|TTY_NOCURSOR);
158 window_update_focus(c->session->curw->window);
159 server_redraw_client(c);
163 * Given overlay position and dimensions, return parts of the input range which
164 * are visible.
166 void
167 server_client_overlay_range(u_int x, u_int y, u_int sx, u_int sy, u_int px,
168 u_int py, u_int nx, struct overlay_ranges *r)
170 u_int ox, onx;
172 /* Return up to 2 ranges. */
173 r->px[2] = 0;
174 r->nx[2] = 0;
176 /* Trivial case of no overlap in the y direction. */
177 if (py < y || py > y + sy - 1) {
178 r->px[0] = px;
179 r->nx[0] = nx;
180 r->px[1] = 0;
181 r->nx[1] = 0;
182 return;
185 /* Visible bit to the left of the popup. */
186 if (px < x) {
187 r->px[0] = px;
188 r->nx[0] = x - px;
189 if (r->nx[0] > nx)
190 r->nx[0] = nx;
191 } else {
192 r->px[0] = 0;
193 r->nx[0] = 0;
196 /* Visible bit to the right of the popup. */
197 ox = x + sx;
198 if (px > ox)
199 ox = px;
200 onx = px + nx;
201 if (onx > ox) {
202 r->px[1] = ox;
203 r->nx[1] = onx - ox;
204 } else {
205 r->px[1] = 0;
206 r->nx[1] = 0;
210 /* Check if this client is inside this server. */
212 server_client_check_nested(struct client *c)
214 struct environ_entry *envent;
215 struct window_pane *wp;
217 envent = environ_find(c->environ, "TMUX");
218 if (envent == NULL || *envent->value == '\0')
219 return (0);
221 RB_FOREACH(wp, window_pane_tree, &all_window_panes) {
222 if (strcmp(wp->tty, c->ttyname) == 0)
223 return (1);
225 return (0);
228 /* Set client key table. */
229 void
230 server_client_set_key_table(struct client *c, const char *name)
232 if (name == NULL)
233 name = server_client_get_key_table(c);
235 key_bindings_unref_table(c->keytable);
236 c->keytable = key_bindings_get_table(name, 1);
237 c->keytable->references++;
238 if (gettimeofday(&c->keytable->activity_time, NULL) != 0)
239 fatal("gettimeofday failed");
242 static uint64_t
243 server_client_key_table_activity_diff(struct client *c)
245 struct timeval diff;
247 timersub(&c->activity_time, &c->keytable->activity_time, &diff);
248 return ((diff.tv_sec * 1000ULL) + (diff.tv_usec / 1000ULL));
251 /* Get default key table. */
252 const char *
253 server_client_get_key_table(struct client *c)
255 struct session *s = c->session;
256 const char *name;
258 if (s == NULL)
259 return ("root");
261 name = options_get_string(s->options, "key-table");
262 if (*name == '\0')
263 return ("root");
264 return (name);
267 /* Is this table the default key table? */
268 static int
269 server_client_is_default_key_table(struct client *c, struct key_table *table)
271 return (strcmp(table->name, server_client_get_key_table(c)) == 0);
274 /* Create a new client. */
275 struct client *
276 server_client_create(int fd)
278 struct client *c;
280 setblocking(fd, 0);
282 c = xcalloc(1, sizeof *c);
283 c->references = 1;
284 c->peer = proc_add_peer(server_proc, fd, server_client_dispatch, c);
286 if (gettimeofday(&c->creation_time, NULL) != 0)
287 fatal("gettimeofday failed");
288 memcpy(&c->activity_time, &c->creation_time, sizeof c->activity_time);
290 c->environ = environ_create();
292 c->fd = -1;
293 c->out_fd = -1;
295 c->queue = cmdq_new();
296 RB_INIT(&c->windows);
297 RB_INIT(&c->files);
299 c->tty.sx = 80;
300 c->tty.sy = 24;
302 status_init(c);
303 c->flags |= CLIENT_FOCUSED;
305 c->keytable = key_bindings_get_table("root", 1);
306 c->keytable->references++;
308 evtimer_set(&c->repeat_timer, server_client_repeat_timer, c);
309 evtimer_set(&c->click_timer, server_client_click_timer, c);
311 TAILQ_INSERT_TAIL(&clients, c, entry);
312 log_debug("new client %p", c);
313 return (c);
316 /* Open client terminal if needed. */
318 server_client_open(struct client *c, char **cause)
320 const char *ttynam = _PATH_TTY;
322 if (c->flags & CLIENT_CONTROL)
323 return (0);
325 if (strcmp(c->ttyname, ttynam) == 0||
326 ((isatty(STDIN_FILENO) &&
327 (ttynam = ttyname(STDIN_FILENO)) != NULL &&
328 strcmp(c->ttyname, ttynam) == 0) ||
329 (isatty(STDOUT_FILENO) &&
330 (ttynam = ttyname(STDOUT_FILENO)) != NULL &&
331 strcmp(c->ttyname, ttynam) == 0) ||
332 (isatty(STDERR_FILENO) &&
333 (ttynam = ttyname(STDERR_FILENO)) != NULL &&
334 strcmp(c->ttyname, ttynam) == 0))) {
335 xasprintf(cause, "can't use %s", c->ttyname);
336 return (-1);
339 if (!(c->flags & CLIENT_TERMINAL)) {
340 *cause = xstrdup("not a terminal");
341 return (-1);
344 if (tty_open(&c->tty, cause) != 0)
345 return (-1);
347 return (0);
350 /* Lost an attached client. */
351 static void
352 server_client_attached_lost(struct client *c)
354 struct session *s;
355 struct window *w;
356 struct client *loop;
357 struct client *found;
359 log_debug("lost attached client %p", c);
362 * By this point the session in the client has been cleared so walk all
363 * windows to find any with this client as the latest.
365 RB_FOREACH(w, windows, &windows) {
366 if (w->latest != c)
367 continue;
369 found = NULL;
370 TAILQ_FOREACH(loop, &clients, entry) {
371 s = loop->session;
372 if (loop == c || s == NULL || s->curw->window != w)
373 continue;
374 if (found == NULL || timercmp(&loop->activity_time,
375 &found->activity_time, >))
376 found = loop;
378 if (found != NULL)
379 server_client_update_latest(found);
383 /* Set client session. */
384 void
385 server_client_set_session(struct client *c, struct session *s)
387 struct session *old = c->session;
389 if (s != NULL && c->session != NULL && c->session != s)
390 c->last_session = c->session;
391 else if (s == NULL)
392 c->last_session = NULL;
393 c->session = s;
394 c->flags |= CLIENT_FOCUSED;
396 if (old != NULL && old->curw != NULL)
397 window_update_focus(old->curw->window);
398 if (s != NULL) {
399 recalculate_sizes();
400 window_update_focus(s->curw->window);
401 session_update_activity(s, NULL);
402 gettimeofday(&s->last_attached_time, NULL);
403 s->curw->flags &= ~WINLINK_ALERTFLAGS;
404 s->curw->window->latest = c;
405 alerts_check_session(s);
406 tty_update_client_offset(c);
407 status_timer_start(c);
408 notify_client("client-session-changed", c);
409 server_redraw_client(c);
412 server_check_unattached();
413 server_update_socket();
416 /* Lost a client. */
417 void
418 server_client_lost(struct client *c)
420 struct client_file *cf, *cf1;
421 struct client_window *cw, *cw1;
423 c->flags |= CLIENT_DEAD;
425 server_client_clear_overlay(c);
426 status_prompt_clear(c);
427 status_message_clear(c);
429 RB_FOREACH_SAFE(cf, client_files, &c->files, cf1) {
430 cf->error = EINTR;
431 file_fire_done(cf);
433 RB_FOREACH_SAFE(cw, client_windows, &c->windows, cw1) {
434 RB_REMOVE(client_windows, &c->windows, cw);
435 free(cw);
438 TAILQ_REMOVE(&clients, c, entry);
439 log_debug("lost client %p", c);
441 if (c->flags & CLIENT_ATTACHED) {
442 server_client_attached_lost(c);
443 notify_client("client-detached", c);
446 if (c->flags & CLIENT_CONTROL)
447 control_stop(c);
448 if (c->flags & CLIENT_TERMINAL)
449 tty_free(&c->tty);
450 free(c->ttyname);
451 free(c->clipboard_panes);
453 free(c->term_name);
454 free(c->term_type);
455 tty_term_free_list(c->term_caps, c->term_ncaps);
457 status_free(c);
459 free(c->title);
460 free((void *)c->cwd);
462 evtimer_del(&c->repeat_timer);
463 evtimer_del(&c->click_timer);
465 key_bindings_unref_table(c->keytable);
467 free(c->message_string);
468 if (event_initialized(&c->message_timer))
469 evtimer_del(&c->message_timer);
471 free(c->prompt_saved);
472 free(c->prompt_string);
473 free(c->prompt_buffer);
475 format_lost_client(c);
476 environ_free(c->environ);
478 proc_remove_peer(c->peer);
479 c->peer = NULL;
481 if (c->out_fd != -1)
482 close(c->out_fd);
483 if (c->fd != -1) {
484 close(c->fd);
485 c->fd = -1;
487 server_client_unref(c);
489 server_add_accept(0); /* may be more file descriptors now */
491 recalculate_sizes();
492 server_check_unattached();
493 server_update_socket();
496 /* Remove reference from a client. */
497 void
498 server_client_unref(struct client *c)
500 log_debug("unref client %p (%d references)", c, c->references);
502 c->references--;
503 if (c->references == 0)
504 event_once(-1, EV_TIMEOUT, server_client_free, c, NULL);
507 /* Free dead client. */
508 static void
509 server_client_free(__unused int fd, __unused short events, void *arg)
511 struct client *c = arg;
513 log_debug("free client %p (%d references)", c, c->references);
515 cmdq_free(c->queue);
517 if (c->references == 0) {
518 free((void *)c->name);
519 free(c);
523 /* Suspend a client. */
524 void
525 server_client_suspend(struct client *c)
527 struct session *s = c->session;
529 if (s == NULL || (c->flags & CLIENT_UNATTACHEDFLAGS))
530 return;
532 tty_stop_tty(&c->tty);
533 c->flags |= CLIENT_SUSPENDED;
534 proc_send(c->peer, MSG_SUSPEND, -1, NULL, 0);
537 /* Detach a client. */
538 void
539 server_client_detach(struct client *c, enum msgtype msgtype)
541 struct session *s = c->session;
543 if (s == NULL || (c->flags & CLIENT_NODETACHFLAGS))
544 return;
546 c->flags |= CLIENT_EXIT;
548 c->exit_type = CLIENT_EXIT_DETACH;
549 c->exit_msgtype = msgtype;
550 c->exit_session = xstrdup(s->name);
553 /* Execute command to replace a client. */
554 void
555 server_client_exec(struct client *c, const char *cmd)
557 struct session *s = c->session;
558 char *msg;
559 const char *shell;
560 size_t cmdsize, shellsize;
562 if (*cmd == '\0')
563 return;
564 cmdsize = strlen(cmd) + 1;
566 if (s != NULL)
567 shell = options_get_string(s->options, "default-shell");
568 else
569 shell = options_get_string(global_s_options, "default-shell");
570 if (!checkshell(shell))
571 shell = _PATH_BSHELL;
572 shellsize = strlen(shell) + 1;
574 msg = xmalloc(cmdsize + shellsize);
575 memcpy(msg, cmd, cmdsize);
576 memcpy(msg + cmdsize, shell, shellsize);
578 proc_send(c->peer, MSG_EXEC, -1, msg, cmdsize + shellsize);
579 free(msg);
582 static enum mouse_where
583 server_client_check_mouse_in_pane(struct window_pane *wp, u_int px, u_int py,
584 u_int *sl_mpos)
586 struct window *w = wp->window;
587 struct options *wo = w->options;
588 struct window_pane *fwp;
589 int pane_status, sb, sb_pos, sb_w, sb_pad;
590 u_int line, sl_top, sl_bottom;
592 sb = options_get_number(wo, "pane-scrollbars");
593 sb_pos = options_get_number(wo, "pane-scrollbars-position");
594 pane_status = options_get_number(wo, "pane-border-status");
595 sb_pos = options_get_number(wo, "pane-scrollbars-position");
597 if (window_pane_show_scrollbar(wp, sb)) {
598 sb_w = wp->scrollbar_style.width;
599 sb_pad = wp->scrollbar_style.pad;
600 } else {
601 sb_w = 0;
602 sb_pad = 0;
604 if (pane_status == PANE_STATUS_TOP)
605 line = wp->yoff - 1;
606 else if (pane_status == PANE_STATUS_BOTTOM)
607 line = wp->yoff + wp->sy;
609 /* Check if point is within the pane or scrollbar. */
610 if (((pane_status != PANE_STATUS_OFF && py != line) ||
611 (wp->yoff == 0 && py < wp->sy) ||
612 (py >= wp->yoff && py < wp->yoff + wp->sy)) &&
613 ((sb_pos == PANE_SCROLLBARS_RIGHT &&
614 px < wp->xoff + wp->sx + sb_pad + sb_w) ||
615 (sb_pos == PANE_SCROLLBARS_LEFT &&
616 px < wp->xoff + wp->sx - sb_pad - sb_w))) {
617 /* Check if in the scrollbar. */
618 if ((sb_pos == PANE_SCROLLBARS_RIGHT &&
619 (px >= wp->xoff + wp->sx + sb_pad &&
620 px < wp->xoff + wp->sx + sb_pad + sb_w)) ||
621 (sb_pos == PANE_SCROLLBARS_LEFT &&
622 (px >= wp->xoff - sb_pad - sb_w &&
623 px < wp->xoff - sb_pad))) {
624 /* Check where inside the scrollbar. */
625 sl_top = wp->yoff + wp->sb_slider_y;
626 sl_bottom = (wp->yoff + wp->sb_slider_y +
627 wp->sb_slider_h - 1);
628 if (py < sl_top)
629 return (SCROLLBAR_UP);
630 else if (py >= sl_top &&
631 py <= sl_bottom) {
632 *sl_mpos = (py - wp->sb_slider_y - wp->yoff);
633 return (SCROLLBAR_SLIDER);
634 } else /* py > sl_bottom */
635 return (SCROLLBAR_DOWN);
636 } else {
637 /* Must be inside the pane. */
638 return (PANE);
640 } else if (~w->flags & WINDOW_ZOOMED) {
641 /* Try the pane borders if not zoomed. */
642 TAILQ_FOREACH(fwp, &w->panes, entry) {
643 if ((((sb_pos == PANE_SCROLLBARS_RIGHT &&
644 fwp->xoff + fwp->sx + sb_pad + sb_w == px) ||
645 (sb_pos == PANE_SCROLLBARS_LEFT &&
646 fwp->xoff + fwp->sx == px)) &&
647 fwp->yoff <= 1 + py &&
648 fwp->yoff + fwp->sy >= py) ||
649 (fwp->yoff + fwp->sy == py &&
650 fwp->xoff <= 1 + px &&
651 fwp->xoff + fwp->sx >= px))
652 break;
654 if (fwp != NULL) {
655 wp = fwp;
656 return (BORDER);
659 return (NOWHERE);
662 /* Check for mouse keys. */
663 static key_code
664 server_client_check_mouse(struct client *c, struct key_event *event)
666 struct mouse_event *m = &event->m;
667 struct session *s = c->session, *fs;
668 struct window *w = s->curw->window;
669 struct winlink *fwl;
670 struct window_pane *wp, *fwp;
671 u_int x, y, b, sx, sy, px, py, sl_mpos = 0;
672 int ignore = 0;
673 key_code key;
674 struct timeval tv;
675 struct style_range *sr;
676 enum { NOTYPE,
677 MOVE,
678 DOWN,
680 DRAG,
681 WHEEL,
682 SECOND,
683 DOUBLE,
684 TRIPLE } type = NOTYPE;
685 enum mouse_where where = NOWHERE;
687 log_debug("%s mouse %02x at %u,%u (last %u,%u) (%d)", c->name, m->b,
688 m->x, m->y, m->lx, m->ly, c->tty.mouse_drag_flag);
690 /* What type of event is this? */
691 if (event->key == KEYC_DOUBLECLICK) {
692 type = DOUBLE;
693 x = m->x, y = m->y, b = m->b;
694 ignore = 1;
695 log_debug("double-click at %u,%u", x, y);
696 } else if ((m->sgr_type != ' ' &&
697 MOUSE_DRAG(m->sgr_b) &&
698 MOUSE_RELEASE(m->sgr_b)) ||
699 (m->sgr_type == ' ' &&
700 MOUSE_DRAG(m->b) &&
701 MOUSE_RELEASE(m->b) &&
702 MOUSE_RELEASE(m->lb))) {
703 type = MOVE;
704 x = m->x, y = m->y, b = 0;
705 log_debug("move at %u,%u", x, y);
706 } else if (MOUSE_DRAG(m->b)) {
707 type = DRAG;
708 if (c->tty.mouse_drag_flag) {
709 x = m->x, y = m->y, b = m->b;
710 if (x == m->lx && y == m->ly)
711 return (KEYC_UNKNOWN);
712 log_debug("drag update at %u,%u", x, y);
713 } else {
714 x = m->lx, y = m->ly, b = m->lb;
715 log_debug("drag start at %u,%u", x, y);
717 } else if (MOUSE_WHEEL(m->b)) {
718 type = WHEEL;
719 x = m->x, y = m->y, b = m->b;
720 log_debug("wheel at %u,%u", x, y);
721 } else if (MOUSE_RELEASE(m->b)) {
722 type = UP;
723 x = m->x, y = m->y, b = m->lb;
724 if (m->sgr_type == 'm')
725 b = m->sgr_b;
726 log_debug("up at %u,%u", x, y);
727 } else {
728 if (c->flags & CLIENT_DOUBLECLICK) {
729 evtimer_del(&c->click_timer);
730 c->flags &= ~CLIENT_DOUBLECLICK;
731 if (m->b == c->click_button) {
732 type = SECOND;
733 x = m->x, y = m->y, b = m->b;
734 log_debug("second-click at %u,%u", x, y);
735 c->flags |= CLIENT_TRIPLECLICK;
737 } else if (c->flags & CLIENT_TRIPLECLICK) {
738 evtimer_del(&c->click_timer);
739 c->flags &= ~CLIENT_TRIPLECLICK;
740 if (m->b == c->click_button) {
741 type = TRIPLE;
742 x = m->x, y = m->y, b = m->b;
743 log_debug("triple-click at %u,%u", x, y);
744 goto have_event;
748 /* DOWN is the only remaining event type. */
749 if (type == NOTYPE) {
750 type = DOWN;
751 x = m->x, y = m->y, b = m->b;
752 log_debug("down at %u,%u", x, y);
753 c->flags |= CLIENT_DOUBLECLICK;
756 if (KEYC_CLICK_TIMEOUT != 0) {
757 memcpy(&c->click_event, m, sizeof c->click_event);
758 c->click_button = m->b;
760 log_debug("click timer started");
761 tv.tv_sec = KEYC_CLICK_TIMEOUT / 1000;
762 tv.tv_usec = (KEYC_CLICK_TIMEOUT % 1000) * 1000L;
763 evtimer_del(&c->click_timer);
764 evtimer_add(&c->click_timer, &tv);
768 have_event:
769 if (type == NOTYPE)
770 return (KEYC_UNKNOWN);
772 /* Save the session. */
773 m->s = s->id;
774 m->w = -1;
775 m->wp = -1;
776 m->ignore = ignore;
778 /* Is this on the status line? */
779 m->statusat = status_at_line(c);
780 m->statuslines = status_line_size(c);
781 if (m->statusat != -1 &&
782 y >= (u_int)m->statusat &&
783 y < m->statusat + m->statuslines) {
784 sr = status_get_range(c, x, y - m->statusat);
785 if (sr == NULL) {
786 where = STATUS_DEFAULT;
787 } else {
788 switch (sr->type) {
789 case STYLE_RANGE_NONE:
790 return (KEYC_UNKNOWN);
791 case STYLE_RANGE_LEFT:
792 log_debug("mouse range: left");
793 where = STATUS_LEFT;
794 break;
795 case STYLE_RANGE_RIGHT:
796 log_debug("mouse range: right");
797 where = STATUS_RIGHT;
798 break;
799 case STYLE_RANGE_PANE:
800 fwp = window_pane_find_by_id(sr->argument);
801 if (fwp == NULL)
802 return (KEYC_UNKNOWN);
803 m->wp = sr->argument;
805 log_debug("mouse range: pane %%%u", m->wp);
806 where = STATUS;
807 break;
808 case STYLE_RANGE_WINDOW:
809 fwl = winlink_find_by_index(&s->windows,
810 sr->argument);
811 if (fwl == NULL)
812 return (KEYC_UNKNOWN);
813 m->w = fwl->window->id;
815 log_debug("mouse range: window @%u", m->w);
816 where = STATUS;
817 break;
818 case STYLE_RANGE_SESSION:
819 fs = session_find_by_id(sr->argument);
820 if (fs == NULL)
821 return (KEYC_UNKNOWN);
822 m->s = sr->argument;
824 log_debug("mouse range: session $%u", m->s);
825 where = STATUS;
826 break;
827 case STYLE_RANGE_USER:
828 where = STATUS;
829 break;
835 * Not on status line. Adjust position and check for border, pane, or
836 * scrollbar.
838 if (where == NOWHERE) {
839 if (c->tty.mouse_scrolling_flag)
840 where = SCROLLBAR_SLIDER;
841 else {
842 px = x;
843 if (m->statusat == 0 && y >= m->statuslines)
844 py = y - m->statuslines;
845 else if (m->statusat > 0 && y >= (u_int)m->statusat)
846 py = m->statusat - 1;
847 else
848 py = y;
850 tty_window_offset(&c->tty, &m->ox, &m->oy, &sx, &sy);
851 log_debug("mouse window @%u at %u,%u (%ux%u)",
852 w->id, m->ox, m->oy, sx, sy);
853 if (px > sx || py > sy)
854 return (KEYC_UNKNOWN);
855 px = px + m->ox;
856 py = py + m->oy;
858 /* Try inside the pane. */
859 wp = window_get_active_at(w, px, py);
860 if (wp == NULL)
861 return (KEYC_UNKNOWN);
862 where = server_client_check_mouse_in_pane(wp, px, py,
863 &sl_mpos);
865 if (where == PANE) {
866 log_debug("mouse %u,%u on pane %%%u", x, y,
867 wp->id);
868 } else if (where == BORDER)
869 log_debug("mouse on pane %%%u border", wp->id);
870 else if (where == SCROLLBAR_UP ||
871 where == SCROLLBAR_SLIDER ||
872 where == SCROLLBAR_DOWN) {
873 log_debug("mouse on pane %%%u scrollbar",
874 wp->id);
876 m->wp = wp->id;
877 m->w = wp->window->id;
879 } else
880 m->wp = -1;
882 /* Stop dragging if needed. */
883 if (type != DRAG &&
884 type != WHEEL &&
885 type != DOUBLE &&
886 type != TRIPLE &&
887 c->tty.mouse_drag_flag != 0) {
888 if (c->tty.mouse_drag_release != NULL)
889 c->tty.mouse_drag_release(c, m);
891 c->tty.mouse_drag_update = NULL;
892 c->tty.mouse_drag_release = NULL;
893 c->tty.mouse_scrolling_flag = 0;
896 * End a mouse drag by passing a MouseDragEnd key corresponding
897 * to the button that started the drag.
899 switch (c->tty.mouse_drag_flag - 1) {
900 case MOUSE_BUTTON_1:
901 if (where == PANE)
902 key = KEYC_MOUSEDRAGEND1_PANE;
903 if (where == STATUS)
904 key = KEYC_MOUSEDRAGEND1_STATUS;
905 if (where == STATUS_LEFT)
906 key = KEYC_MOUSEDRAGEND1_STATUS_LEFT;
907 if (where == STATUS_RIGHT)
908 key = KEYC_MOUSEDRAGEND1_STATUS_RIGHT;
909 if (where == STATUS_DEFAULT)
910 key = KEYC_MOUSEDRAGEND1_STATUS_DEFAULT;
911 if (where == SCROLLBAR_SLIDER)
912 key = KEYC_MOUSEDRAGEND1_SCROLLBAR_SLIDER;
913 if (where == BORDER)
914 key = KEYC_MOUSEDRAGEND1_BORDER;
915 break;
916 case MOUSE_BUTTON_2:
917 if (where == PANE)
918 key = KEYC_MOUSEDRAGEND2_PANE;
919 if (where == STATUS)
920 key = KEYC_MOUSEDRAGEND2_STATUS;
921 if (where == STATUS_LEFT)
922 key = KEYC_MOUSEDRAGEND2_STATUS_LEFT;
923 if (where == STATUS_RIGHT)
924 key = KEYC_MOUSEDRAGEND2_STATUS_RIGHT;
925 if (where == STATUS_DEFAULT)
926 key = KEYC_MOUSEDRAGEND2_STATUS_DEFAULT;
927 if (where == SCROLLBAR_SLIDER)
928 key = KEYC_MOUSEDRAGEND2_SCROLLBAR_SLIDER;
929 if (where == BORDER)
930 key = KEYC_MOUSEDRAGEND2_BORDER;
931 break;
932 case MOUSE_BUTTON_3:
933 if (where == PANE)
934 key = KEYC_MOUSEDRAGEND3_PANE;
935 if (where == STATUS)
936 key = KEYC_MOUSEDRAGEND3_STATUS;
937 if (where == STATUS_LEFT)
938 key = KEYC_MOUSEDRAGEND3_STATUS_LEFT;
939 if (where == STATUS_RIGHT)
940 key = KEYC_MOUSEDRAGEND3_STATUS_RIGHT;
941 if (where == STATUS_DEFAULT)
942 key = KEYC_MOUSEDRAGEND3_STATUS_DEFAULT;
943 if (where == SCROLLBAR_SLIDER)
944 key = KEYC_MOUSEDRAGEND3_SCROLLBAR_SLIDER;
945 if (where == BORDER)
946 key = KEYC_MOUSEDRAGEND3_BORDER;
947 break;
948 case MOUSE_BUTTON_6:
949 if (where == PANE)
950 key = KEYC_MOUSEDRAGEND6_PANE;
951 if (where == STATUS)
952 key = KEYC_MOUSEDRAGEND6_STATUS;
953 if (where == STATUS_LEFT)
954 key = KEYC_MOUSEDRAGEND6_STATUS_LEFT;
955 if (where == STATUS_RIGHT)
956 key = KEYC_MOUSEDRAGEND6_STATUS_RIGHT;
957 if (where == STATUS_DEFAULT)
958 key = KEYC_MOUSEDRAGEND6_STATUS_DEFAULT;
959 if (where == SCROLLBAR_SLIDER)
960 key = KEYC_MOUSEDRAGEND6_SCROLLBAR_SLIDER;
961 if (where == BORDER)
962 key = KEYC_MOUSEDRAGEND6_BORDER;
963 break;
964 case MOUSE_BUTTON_7:
965 if (where == PANE)
966 key = KEYC_MOUSEDRAGEND7_PANE;
967 if (where == STATUS)
968 key = KEYC_MOUSEDRAGEND7_STATUS;
969 if (where == STATUS_LEFT)
970 key = KEYC_MOUSEDRAGEND7_STATUS_LEFT;
971 if (where == STATUS_RIGHT)
972 key = KEYC_MOUSEDRAGEND7_STATUS_RIGHT;
973 if (where == STATUS_DEFAULT)
974 key = KEYC_MOUSEDRAGEND7_STATUS_DEFAULT;
975 if (where == SCROLLBAR_SLIDER)
976 key = KEYC_MOUSEDRAGEND7_SCROLLBAR_SLIDER;
977 if (where == BORDER)
978 key = KEYC_MOUSEDRAGEND7_BORDER;
979 break;
980 case MOUSE_BUTTON_8:
981 if (where == PANE)
982 key = KEYC_MOUSEDRAGEND8_PANE;
983 if (where == STATUS)
984 key = KEYC_MOUSEDRAGEND8_STATUS;
985 if (where == STATUS_LEFT)
986 key = KEYC_MOUSEDRAGEND8_STATUS_LEFT;
987 if (where == STATUS_RIGHT)
988 key = KEYC_MOUSEDRAGEND8_STATUS_RIGHT;
989 if (where == STATUS_DEFAULT)
990 key = KEYC_MOUSEDRAGEND8_STATUS_DEFAULT;
991 if (where == SCROLLBAR_SLIDER)
992 key = KEYC_MOUSEDRAGEND8_SCROLLBAR_SLIDER;
993 if (where == BORDER)
994 key = KEYC_MOUSEDRAGEND8_BORDER;
995 break;
996 case MOUSE_BUTTON_9:
997 if (where == PANE)
998 key = KEYC_MOUSEDRAGEND9_PANE;
999 if (where == STATUS)
1000 key = KEYC_MOUSEDRAGEND9_STATUS;
1001 if (where == STATUS_LEFT)
1002 key = KEYC_MOUSEDRAGEND9_STATUS_LEFT;
1003 if (where == STATUS_RIGHT)
1004 key = KEYC_MOUSEDRAGEND9_STATUS_RIGHT;
1005 if (where == STATUS_DEFAULT)
1006 key = KEYC_MOUSEDRAGEND9_STATUS_DEFAULT;
1007 if (where == SCROLLBAR_SLIDER)
1008 key = KEYC_MOUSEDRAGEND9_SCROLLBAR_SLIDER;
1009 if (where == BORDER)
1010 key = KEYC_MOUSEDRAGEND9_BORDER;
1011 break;
1012 case MOUSE_BUTTON_10:
1013 if (where == PANE)
1014 key = KEYC_MOUSEDRAGEND10_PANE;
1015 if (where == STATUS)
1016 key = KEYC_MOUSEDRAGEND10_STATUS;
1017 if (where == STATUS_LEFT)
1018 key = KEYC_MOUSEDRAGEND10_STATUS_LEFT;
1019 if (where == STATUS_RIGHT)
1020 key = KEYC_MOUSEDRAGEND10_STATUS_RIGHT;
1021 if (where == STATUS_DEFAULT)
1022 key = KEYC_MOUSEDRAGEND10_STATUS_DEFAULT;
1023 if (where == SCROLLBAR_SLIDER)
1024 key = KEYC_MOUSEDRAGEND10_SCROLLBAR_SLIDER;
1025 if (where == BORDER)
1026 key = KEYC_MOUSEDRAGEND10_BORDER;
1027 break;
1028 case MOUSE_BUTTON_11:
1029 if (where == PANE)
1030 key = KEYC_MOUSEDRAGEND11_PANE;
1031 if (where == STATUS)
1032 key = KEYC_MOUSEDRAGEND11_STATUS;
1033 if (where == STATUS_LEFT)
1034 key = KEYC_MOUSEDRAGEND11_STATUS_LEFT;
1035 if (where == STATUS_RIGHT)
1036 key = KEYC_MOUSEDRAGEND11_STATUS_RIGHT;
1037 if (where == STATUS_DEFAULT)
1038 key = KEYC_MOUSEDRAGEND11_STATUS_DEFAULT;
1039 if (where == SCROLLBAR_SLIDER)
1040 key = KEYC_MOUSEDRAGEND11_SCROLLBAR_SLIDER;
1041 if (where == BORDER)
1042 key = KEYC_MOUSEDRAGEND11_BORDER;
1043 break;
1044 default:
1045 key = KEYC_MOUSE;
1046 break;
1048 c->tty.mouse_drag_flag = 0;
1049 c->tty.mouse_slider_mpos = -1;
1050 goto out;
1053 /* Convert to a key binding. */
1054 key = KEYC_UNKNOWN;
1055 switch (type) {
1056 case NOTYPE:
1057 break;
1058 case MOVE:
1059 if (where == PANE)
1060 key = KEYC_MOUSEMOVE_PANE;
1061 if (where == STATUS)
1062 key = KEYC_MOUSEMOVE_STATUS;
1063 if (where == STATUS_LEFT)
1064 key = KEYC_MOUSEMOVE_STATUS_LEFT;
1065 if (where == STATUS_RIGHT)
1066 key = KEYC_MOUSEMOVE_STATUS_RIGHT;
1067 if (where == STATUS_DEFAULT)
1068 key = KEYC_MOUSEMOVE_STATUS_DEFAULT;
1069 if (where == BORDER)
1070 key = KEYC_MOUSEMOVE_BORDER;
1071 break;
1072 case DRAG:
1073 if (c->tty.mouse_drag_update != NULL)
1074 key = KEYC_DRAGGING;
1075 else {
1076 switch (MOUSE_BUTTONS(b)) {
1077 case MOUSE_BUTTON_1:
1078 if (where == PANE)
1079 key = KEYC_MOUSEDRAG1_PANE;
1080 if (where == STATUS)
1081 key = KEYC_MOUSEDRAG1_STATUS;
1082 if (where == STATUS_LEFT)
1083 key = KEYC_MOUSEDRAG1_STATUS_LEFT;
1084 if (where == STATUS_RIGHT)
1085 key = KEYC_MOUSEDRAG1_STATUS_RIGHT;
1086 if (where == STATUS_DEFAULT)
1087 key = KEYC_MOUSEDRAG1_STATUS_DEFAULT;
1088 if (where == SCROLLBAR_UP)
1089 key = KEYC_MOUSEDRAG1_SCROLLBAR_UP;
1090 if (where == SCROLLBAR_SLIDER)
1091 key = KEYC_MOUSEDRAG1_SCROLLBAR_SLIDER;
1092 if (where == SCROLLBAR_DOWN)
1093 key = KEYC_MOUSEDRAG1_SCROLLBAR_DOWN;
1094 if (where == BORDER)
1095 key = KEYC_MOUSEDRAG1_BORDER;
1096 break;
1097 case MOUSE_BUTTON_2:
1098 if (where == PANE)
1099 key = KEYC_MOUSEDRAG2_PANE;
1100 if (where == STATUS)
1101 key = KEYC_MOUSEDRAG2_STATUS;
1102 if (where == STATUS_LEFT)
1103 key = KEYC_MOUSEDRAG2_STATUS_LEFT;
1104 if (where == STATUS_RIGHT)
1105 key = KEYC_MOUSEDRAG2_STATUS_RIGHT;
1106 if (where == STATUS_DEFAULT)
1107 key = KEYC_MOUSEDRAG2_STATUS_DEFAULT;
1108 if (where == SCROLLBAR_UP)
1109 key = KEYC_MOUSEDRAG2_SCROLLBAR_UP;
1110 if (where == SCROLLBAR_SLIDER)
1111 key = KEYC_MOUSEDRAG2_SCROLLBAR_SLIDER;
1112 if (where == SCROLLBAR_DOWN)
1113 key = KEYC_MOUSEDRAG2_SCROLLBAR_DOWN;
1114 if (where == BORDER)
1115 key = KEYC_MOUSEDRAG2_BORDER;
1116 break;
1117 case MOUSE_BUTTON_3:
1118 if (where == PANE)
1119 key = KEYC_MOUSEDRAG3_PANE;
1120 if (where == STATUS)
1121 key = KEYC_MOUSEDRAG3_STATUS;
1122 if (where == STATUS_LEFT)
1123 key = KEYC_MOUSEDRAG3_STATUS_LEFT;
1124 if (where == STATUS_RIGHT)
1125 key = KEYC_MOUSEDRAG3_STATUS_RIGHT;
1126 if (where == STATUS_DEFAULT)
1127 key = KEYC_MOUSEDRAG3_STATUS_DEFAULT;
1128 if (where == SCROLLBAR_UP)
1129 key = KEYC_MOUSEDRAG3_SCROLLBAR_UP;
1130 if (where == SCROLLBAR_SLIDER)
1131 key = KEYC_MOUSEDRAG3_SCROLLBAR_SLIDER;
1132 if (where == SCROLLBAR_DOWN)
1133 key = KEYC_MOUSEDRAG3_SCROLLBAR_DOWN;
1134 if (where == BORDER)
1135 key = KEYC_MOUSEDRAG3_BORDER;
1136 break;
1137 case MOUSE_BUTTON_6:
1138 if (where == PANE)
1139 key = KEYC_MOUSEDRAG6_PANE;
1140 if (where == STATUS)
1141 key = KEYC_MOUSEDRAG6_STATUS;
1142 if (where == STATUS_LEFT)
1143 key = KEYC_MOUSEDRAG6_STATUS_LEFT;
1144 if (where == STATUS_RIGHT)
1145 key = KEYC_MOUSEDRAG6_STATUS_RIGHT;
1146 if (where == STATUS_DEFAULT)
1147 key = KEYC_MOUSEDRAG6_STATUS_DEFAULT;
1148 if (where == SCROLLBAR_UP)
1149 key = KEYC_MOUSEDRAG6_SCROLLBAR_UP;
1150 if (where == SCROLLBAR_SLIDER)
1151 key = KEYC_MOUSEDRAG6_SCROLLBAR_SLIDER;
1152 if (where == SCROLLBAR_DOWN)
1153 key = KEYC_MOUSEDRAG6_SCROLLBAR_DOWN;
1154 if (where == BORDER)
1155 key = KEYC_MOUSEDRAG6_BORDER;
1156 break;
1157 case MOUSE_BUTTON_7:
1158 if (where == PANE)
1159 key = KEYC_MOUSEDRAG7_PANE;
1160 if (where == STATUS)
1161 key = KEYC_MOUSEDRAG7_STATUS;
1162 if (where == STATUS_LEFT)
1163 key = KEYC_MOUSEDRAG7_STATUS_LEFT;
1164 if (where == STATUS_RIGHT)
1165 key = KEYC_MOUSEDRAG7_STATUS_RIGHT;
1166 if (where == STATUS_DEFAULT)
1167 key = KEYC_MOUSEDRAG7_STATUS_DEFAULT;
1168 if (where == SCROLLBAR_UP)
1169 key = KEYC_MOUSEDRAG7_SCROLLBAR_UP;
1170 if (where == SCROLLBAR_SLIDER)
1171 key = KEYC_MOUSEDRAG7_SCROLLBAR_SLIDER;
1172 if (where == SCROLLBAR_DOWN)
1173 key = KEYC_MOUSEDRAG7_SCROLLBAR_DOWN;
1174 if (where == BORDER)
1175 key = KEYC_MOUSEDRAG7_BORDER;
1176 break;
1177 case MOUSE_BUTTON_8:
1178 if (where == PANE)
1179 key = KEYC_MOUSEDRAG8_PANE;
1180 if (where == STATUS)
1181 key = KEYC_MOUSEDRAG8_STATUS;
1182 if (where == STATUS_LEFT)
1183 key = KEYC_MOUSEDRAG8_STATUS_LEFT;
1184 if (where == STATUS_RIGHT)
1185 key = KEYC_MOUSEDRAG8_STATUS_RIGHT;
1186 if (where == STATUS_DEFAULT)
1187 key = KEYC_MOUSEDRAG8_STATUS_DEFAULT;
1188 if (where == SCROLLBAR_UP)
1189 key = KEYC_MOUSEDRAG8_SCROLLBAR_UP;
1190 if (where == SCROLLBAR_SLIDER)
1191 key = KEYC_MOUSEDRAG8_SCROLLBAR_SLIDER;
1192 if (where == SCROLLBAR_DOWN)
1193 key = KEYC_MOUSEDRAG8_SCROLLBAR_DOWN;
1194 if (where == BORDER)
1195 key = KEYC_MOUSEDRAG8_BORDER;
1196 break;
1197 case MOUSE_BUTTON_9:
1198 if (where == PANE)
1199 key = KEYC_MOUSEDRAG9_PANE;
1200 if (where == STATUS)
1201 key = KEYC_MOUSEDRAG9_STATUS;
1202 if (where == STATUS_LEFT)
1203 key = KEYC_MOUSEDRAG9_STATUS_LEFT;
1204 if (where == STATUS_RIGHT)
1205 key = KEYC_MOUSEDRAG9_STATUS_RIGHT;
1206 if (where == STATUS_DEFAULT)
1207 key = KEYC_MOUSEDRAG9_STATUS_DEFAULT;
1208 if (where == SCROLLBAR_UP)
1209 key = KEYC_MOUSEDRAG9_SCROLLBAR_UP;
1210 if (where == SCROLLBAR_SLIDER)
1211 key = KEYC_MOUSEDRAG9_SCROLLBAR_SLIDER;
1212 if (where == SCROLLBAR_DOWN)
1213 key = KEYC_MOUSEDRAG9_SCROLLBAR_DOWN;
1214 if (where == BORDER)
1215 key = KEYC_MOUSEDRAG9_BORDER;
1216 break;
1217 case MOUSE_BUTTON_10:
1218 if (where == PANE)
1219 key = KEYC_MOUSEDRAG10_PANE;
1220 if (where == STATUS)
1221 key = KEYC_MOUSEDRAG10_STATUS;
1222 if (where == STATUS_LEFT)
1223 key = KEYC_MOUSEDRAG10_STATUS_LEFT;
1224 if (where == STATUS_RIGHT)
1225 key = KEYC_MOUSEDRAG10_STATUS_RIGHT;
1226 if (where == STATUS_DEFAULT)
1227 key = KEYC_MOUSEDRAG10_STATUS_DEFAULT;
1228 if (where == SCROLLBAR_UP)
1229 key = KEYC_MOUSEDRAG10_SCROLLBAR_UP;
1230 if (where == SCROLLBAR_SLIDER)
1231 key = KEYC_MOUSEDRAG10_SCROLLBAR_SLIDER;
1232 if (where == SCROLLBAR_DOWN)
1233 key = KEYC_MOUSEDRAG10_SCROLLBAR_DOWN;
1234 if (where == BORDER)
1235 key = KEYC_MOUSEDRAG10_BORDER;
1236 break;
1237 case MOUSE_BUTTON_11:
1238 if (where == PANE)
1239 key = KEYC_MOUSEDRAG11_PANE;
1240 if (where == STATUS)
1241 key = KEYC_MOUSEDRAG11_STATUS;
1242 if (where == STATUS_LEFT)
1243 key = KEYC_MOUSEDRAG11_STATUS_LEFT;
1244 if (where == STATUS_RIGHT)
1245 key = KEYC_MOUSEDRAG11_STATUS_RIGHT;
1246 if (where == STATUS_DEFAULT)
1247 key = KEYC_MOUSEDRAG11_STATUS_DEFAULT;
1248 if (where == SCROLLBAR_UP)
1249 key = KEYC_MOUSEDRAG11_SCROLLBAR_UP;
1250 if (where == SCROLLBAR_SLIDER)
1251 key = KEYC_MOUSEDRAG11_SCROLLBAR_SLIDER;
1252 if (where == SCROLLBAR_DOWN)
1253 key = KEYC_MOUSEDRAG11_SCROLLBAR_DOWN;
1254 if (where == BORDER)
1255 key = KEYC_MOUSEDRAG11_BORDER;
1256 break;
1261 * Begin a drag by setting the flag to a non-zero value that
1262 * corresponds to the mouse button in use. If starting to drag
1263 * the scrollbar, store the relative position in the slider
1264 * where the user grabbed.
1266 c->tty.mouse_drag_flag = MOUSE_BUTTONS(b) + 1;
1267 if (c->tty.mouse_scrolling_flag == 0 &&
1268 where == SCROLLBAR_SLIDER) {
1269 c->tty.mouse_scrolling_flag = 1;
1270 c->tty.mouse_slider_mpos = sl_mpos;
1272 break;
1273 case WHEEL:
1274 if (MOUSE_BUTTONS(b) == MOUSE_WHEEL_UP) {
1275 if (where == PANE)
1276 key = KEYC_WHEELUP_PANE;
1277 if (where == STATUS)
1278 key = KEYC_WHEELUP_STATUS;
1279 if (where == STATUS_LEFT)
1280 key = KEYC_WHEELUP_STATUS_LEFT;
1281 if (where == STATUS_RIGHT)
1282 key = KEYC_WHEELUP_STATUS_RIGHT;
1283 if (where == STATUS_DEFAULT)
1284 key = KEYC_WHEELUP_STATUS_DEFAULT;
1285 if (where == BORDER)
1286 key = KEYC_WHEELUP_BORDER;
1287 } else {
1288 if (where == PANE)
1289 key = KEYC_WHEELDOWN_PANE;
1290 if (where == STATUS)
1291 key = KEYC_WHEELDOWN_STATUS;
1292 if (where == STATUS_LEFT)
1293 key = KEYC_WHEELDOWN_STATUS_LEFT;
1294 if (where == STATUS_RIGHT)
1295 key = KEYC_WHEELDOWN_STATUS_RIGHT;
1296 if (where == STATUS_DEFAULT)
1297 key = KEYC_WHEELDOWN_STATUS_DEFAULT;
1298 if (where == BORDER)
1299 key = KEYC_WHEELDOWN_BORDER;
1301 break;
1302 case UP:
1303 switch (MOUSE_BUTTONS(b)) {
1304 case MOUSE_BUTTON_1:
1305 if (where == PANE)
1306 key = KEYC_MOUSEUP1_PANE;
1307 if (where == STATUS)
1308 key = KEYC_MOUSEUP1_STATUS;
1309 if (where == STATUS_LEFT)
1310 key = KEYC_MOUSEUP1_STATUS_LEFT;
1311 if (where == STATUS_RIGHT)
1312 key = KEYC_MOUSEUP1_STATUS_RIGHT;
1313 if (where == STATUS_DEFAULT)
1314 key = KEYC_MOUSEUP1_STATUS_DEFAULT;
1315 if (where == SCROLLBAR_UP)
1316 key = KEYC_MOUSEUP1_SCROLLBAR_UP;
1317 if (where == SCROLLBAR_SLIDER)
1318 key = KEYC_MOUSEUP1_SCROLLBAR_SLIDER;
1319 if (where == SCROLLBAR_DOWN)
1320 key = KEYC_MOUSEUP1_SCROLLBAR_DOWN;
1321 if (where == BORDER)
1322 key = KEYC_MOUSEUP1_BORDER;
1323 break;
1324 case MOUSE_BUTTON_2:
1325 if (where == PANE)
1326 key = KEYC_MOUSEUP2_PANE;
1327 if (where == STATUS)
1328 key = KEYC_MOUSEUP2_STATUS;
1329 if (where == STATUS_LEFT)
1330 key = KEYC_MOUSEUP2_STATUS_LEFT;
1331 if (where == STATUS_RIGHT)
1332 key = KEYC_MOUSEUP2_STATUS_RIGHT;
1333 if (where == STATUS_DEFAULT)
1334 key = KEYC_MOUSEUP2_STATUS_DEFAULT;
1335 if (where == SCROLLBAR_UP)
1336 key = KEYC_MOUSEUP2_SCROLLBAR_UP;
1337 if (where == SCROLLBAR_SLIDER)
1338 key = KEYC_MOUSEUP2_SCROLLBAR_SLIDER;
1339 if (where == SCROLLBAR_DOWN)
1340 key = KEYC_MOUSEUP2_SCROLLBAR_DOWN;
1341 if (where == BORDER)
1342 key = KEYC_MOUSEUP2_BORDER;
1343 break;
1344 case MOUSE_BUTTON_3:
1345 if (where == PANE)
1346 key = KEYC_MOUSEUP3_PANE;
1347 if (where == STATUS)
1348 key = KEYC_MOUSEUP3_STATUS;
1349 if (where == STATUS_LEFT)
1350 key = KEYC_MOUSEUP3_STATUS_LEFT;
1351 if (where == STATUS_RIGHT)
1352 key = KEYC_MOUSEUP3_STATUS_RIGHT;
1353 if (where == STATUS_DEFAULT)
1354 key = KEYC_MOUSEUP3_STATUS_DEFAULT;
1355 if (where == SCROLLBAR_UP)
1356 key = KEYC_MOUSEUP3_SCROLLBAR_UP;
1357 if (where == SCROLLBAR_SLIDER)
1358 key = KEYC_MOUSEUP3_SCROLLBAR_SLIDER;
1359 if (where == SCROLLBAR_DOWN)
1360 key = KEYC_MOUSEUP3_SCROLLBAR_DOWN;
1361 if (where == BORDER)
1362 key = KEYC_MOUSEUP3_BORDER;
1363 break;
1364 case MOUSE_BUTTON_6:
1365 if (where == PANE)
1366 key = KEYC_MOUSEUP6_PANE;
1367 if (where == STATUS)
1368 key = KEYC_MOUSEUP6_STATUS;
1369 if (where == STATUS_LEFT)
1370 key = KEYC_MOUSEUP6_STATUS_LEFT;
1371 if (where == STATUS_RIGHT)
1372 key = KEYC_MOUSEUP6_STATUS_RIGHT;
1373 if (where == STATUS_DEFAULT)
1374 key = KEYC_MOUSEUP6_STATUS_DEFAULT;
1375 if (where == SCROLLBAR_UP)
1376 key = KEYC_MOUSEUP6_SCROLLBAR_UP;
1377 if (where == SCROLLBAR_SLIDER)
1378 key = KEYC_MOUSEUP6_SCROLLBAR_SLIDER;
1379 if (where == SCROLLBAR_DOWN)
1380 key = KEYC_MOUSEUP6_SCROLLBAR_DOWN;
1381 if (where == BORDER)
1382 key = KEYC_MOUSEUP6_BORDER;
1383 break;
1384 case MOUSE_BUTTON_7:
1385 if (where == PANE)
1386 key = KEYC_MOUSEUP7_PANE;
1387 if (where == STATUS)
1388 key = KEYC_MOUSEUP7_STATUS;
1389 if (where == STATUS_LEFT)
1390 key = KEYC_MOUSEUP7_STATUS_LEFT;
1391 if (where == STATUS_RIGHT)
1392 key = KEYC_MOUSEUP7_STATUS_RIGHT;
1393 if (where == STATUS_DEFAULT)
1394 key = KEYC_MOUSEUP7_STATUS_DEFAULT;
1395 if (where == SCROLLBAR_UP)
1396 key = KEYC_MOUSEUP7_SCROLLBAR_UP;
1397 if (where == SCROLLBAR_SLIDER)
1398 key = KEYC_MOUSEUP7_SCROLLBAR_SLIDER;
1399 if (where == SCROLLBAR_DOWN)
1400 key = KEYC_MOUSEUP7_SCROLLBAR_DOWN;
1401 if (where == BORDER)
1402 key = KEYC_MOUSEUP7_BORDER;
1403 break;
1404 case MOUSE_BUTTON_8:
1405 if (where == PANE)
1406 key = KEYC_MOUSEUP8_PANE;
1407 if (where == STATUS)
1408 key = KEYC_MOUSEUP8_STATUS;
1409 if (where == STATUS_LEFT)
1410 key = KEYC_MOUSEUP8_STATUS_LEFT;
1411 if (where == STATUS_RIGHT)
1412 key = KEYC_MOUSEUP8_STATUS_RIGHT;
1413 if (where == STATUS_DEFAULT)
1414 key = KEYC_MOUSEUP8_STATUS_DEFAULT;
1415 if (where == SCROLLBAR_UP)
1416 key = KEYC_MOUSEUP8_SCROLLBAR_UP;
1417 if (where == SCROLLBAR_SLIDER)
1418 key = KEYC_MOUSEUP8_SCROLLBAR_SLIDER;
1419 if (where == SCROLLBAR_DOWN)
1420 key = KEYC_MOUSEUP8_SCROLLBAR_DOWN;
1421 if (where == BORDER)
1422 key = KEYC_MOUSEUP8_BORDER;
1423 break;
1424 case MOUSE_BUTTON_9:
1425 if (where == PANE)
1426 key = KEYC_MOUSEUP9_PANE;
1427 if (where == STATUS)
1428 key = KEYC_MOUSEUP9_STATUS;
1429 if (where == STATUS_LEFT)
1430 key = KEYC_MOUSEUP9_STATUS_LEFT;
1431 if (where == STATUS_RIGHT)
1432 key = KEYC_MOUSEUP9_STATUS_RIGHT;
1433 if (where == STATUS_DEFAULT)
1434 key = KEYC_MOUSEUP9_STATUS_DEFAULT;
1435 if (where == SCROLLBAR_UP)
1436 key = KEYC_MOUSEUP9_SCROLLBAR_UP;
1437 if (where == SCROLLBAR_SLIDER)
1438 key = KEYC_MOUSEUP9_SCROLLBAR_SLIDER;
1439 if (where == SCROLLBAR_DOWN)
1440 key = KEYC_MOUSEUP9_SCROLLBAR_DOWN;
1441 if (where == BORDER)
1442 key = KEYC_MOUSEUP9_BORDER;
1443 break;
1444 case MOUSE_BUTTON_10:
1445 if (where == PANE)
1446 key = KEYC_MOUSEUP1_PANE;
1447 if (where == STATUS)
1448 key = KEYC_MOUSEUP1_STATUS;
1449 if (where == STATUS_LEFT)
1450 key = KEYC_MOUSEUP1_STATUS_LEFT;
1451 if (where == STATUS_RIGHT)
1452 key = KEYC_MOUSEUP1_STATUS_RIGHT;
1453 if (where == STATUS_DEFAULT)
1454 key = KEYC_MOUSEUP10_STATUS_DEFAULT;
1455 if (where == SCROLLBAR_UP)
1456 key = KEYC_MOUSEUP10_SCROLLBAR_UP;
1457 if (where == SCROLLBAR_SLIDER)
1458 key = KEYC_MOUSEUP10_SCROLLBAR_SLIDER;
1459 if (where == SCROLLBAR_DOWN)
1460 key = KEYC_MOUSEUP1_SCROLLBAR_DOWN;
1461 if (where == BORDER)
1462 key = KEYC_MOUSEUP1_BORDER;
1463 break;
1464 case MOUSE_BUTTON_11:
1465 if (where == PANE)
1466 key = KEYC_MOUSEUP11_PANE;
1467 if (where == STATUS)
1468 key = KEYC_MOUSEUP11_STATUS;
1469 if (where == STATUS_LEFT)
1470 key = KEYC_MOUSEUP11_STATUS_LEFT;
1471 if (where == STATUS_RIGHT)
1472 key = KEYC_MOUSEUP11_STATUS_RIGHT;
1473 if (where == STATUS_DEFAULT)
1474 key = KEYC_MOUSEUP11_STATUS_DEFAULT;
1475 if (where == SCROLLBAR_UP)
1476 key = KEYC_MOUSEUP11_SCROLLBAR_UP;
1477 if (where == SCROLLBAR_SLIDER)
1478 key = KEYC_MOUSEUP11_SCROLLBAR_SLIDER;
1479 if (where == SCROLLBAR_DOWN)
1480 key = KEYC_MOUSEUP11_SCROLLBAR_DOWN;
1481 if (where == BORDER)
1482 key = KEYC_MOUSEUP11_BORDER;
1483 break;
1485 break;
1486 case DOWN:
1487 switch (MOUSE_BUTTONS(b)) {
1488 case MOUSE_BUTTON_1:
1489 if (where == PANE)
1490 key = KEYC_MOUSEDOWN1_PANE;
1491 if (where == STATUS)
1492 key = KEYC_MOUSEDOWN1_STATUS;
1493 if (where == STATUS_LEFT)
1494 key = KEYC_MOUSEDOWN1_STATUS_LEFT;
1495 if (where == STATUS_RIGHT)
1496 key = KEYC_MOUSEDOWN1_STATUS_RIGHT;
1497 if (where == STATUS_DEFAULT)
1498 key = KEYC_MOUSEDOWN1_STATUS_DEFAULT;
1499 if (where == SCROLLBAR_UP)
1500 key = KEYC_MOUSEDOWN1_SCROLLBAR_UP;
1501 if (where == SCROLLBAR_SLIDER)
1502 key = KEYC_MOUSEDOWN1_SCROLLBAR_SLIDER;
1503 if (where == SCROLLBAR_DOWN)
1504 key = KEYC_MOUSEDOWN1_SCROLLBAR_DOWN;
1505 if (where == BORDER)
1506 key = KEYC_MOUSEDOWN1_BORDER;
1507 break;
1508 case MOUSE_BUTTON_2:
1509 if (where == PANE)
1510 key = KEYC_MOUSEDOWN2_PANE;
1511 if (where == STATUS)
1512 key = KEYC_MOUSEDOWN2_STATUS;
1513 if (where == STATUS_LEFT)
1514 key = KEYC_MOUSEDOWN2_STATUS_LEFT;
1515 if (where == STATUS_RIGHT)
1516 key = KEYC_MOUSEDOWN2_STATUS_RIGHT;
1517 if (where == STATUS_DEFAULT)
1518 key = KEYC_MOUSEDOWN2_STATUS_DEFAULT;
1519 if (where == SCROLLBAR_UP)
1520 key = KEYC_MOUSEDOWN2_SCROLLBAR_UP;
1521 if (where == SCROLLBAR_SLIDER)
1522 key = KEYC_MOUSEDOWN2_SCROLLBAR_SLIDER;
1523 if (where == SCROLLBAR_DOWN)
1524 key = KEYC_MOUSEDOWN2_SCROLLBAR_DOWN;
1525 if (where == BORDER)
1526 key = KEYC_MOUSEDOWN2_BORDER;
1527 break;
1528 case MOUSE_BUTTON_3:
1529 if (where == PANE)
1530 key = KEYC_MOUSEDOWN3_PANE;
1531 if (where == STATUS)
1532 key = KEYC_MOUSEDOWN3_STATUS;
1533 if (where == STATUS_LEFT)
1534 key = KEYC_MOUSEDOWN3_STATUS_LEFT;
1535 if (where == STATUS_RIGHT)
1536 key = KEYC_MOUSEDOWN3_STATUS_RIGHT;
1537 if (where == STATUS_DEFAULT)
1538 key = KEYC_MOUSEDOWN3_STATUS_DEFAULT;
1539 if (where == SCROLLBAR_UP)
1540 key = KEYC_MOUSEDOWN3_SCROLLBAR_UP;
1541 if (where == SCROLLBAR_SLIDER)
1542 key = KEYC_MOUSEDOWN3_SCROLLBAR_SLIDER;
1543 if (where == SCROLLBAR_DOWN)
1544 key = KEYC_MOUSEDOWN3_SCROLLBAR_DOWN;
1545 if (where == BORDER)
1546 key = KEYC_MOUSEDOWN3_BORDER;
1547 break;
1548 case MOUSE_BUTTON_6:
1549 if (where == PANE)
1550 key = KEYC_MOUSEDOWN6_PANE;
1551 if (where == STATUS)
1552 key = KEYC_MOUSEDOWN6_STATUS;
1553 if (where == STATUS_LEFT)
1554 key = KEYC_MOUSEDOWN6_STATUS_LEFT;
1555 if (where == STATUS_RIGHT)
1556 key = KEYC_MOUSEDOWN6_STATUS_RIGHT;
1557 if (where == STATUS_DEFAULT)
1558 key = KEYC_MOUSEDOWN6_STATUS_DEFAULT;
1559 if (where == SCROLLBAR_UP)
1560 key = KEYC_MOUSEDOWN6_SCROLLBAR_UP;
1561 if (where == SCROLLBAR_SLIDER)
1562 key = KEYC_MOUSEDOWN6_SCROLLBAR_SLIDER;
1563 if (where == SCROLLBAR_DOWN)
1564 key = KEYC_MOUSEDOWN6_SCROLLBAR_DOWN;
1565 if (where == BORDER)
1566 key = KEYC_MOUSEDOWN6_BORDER;
1567 break;
1568 case MOUSE_BUTTON_7:
1569 if (where == PANE)
1570 key = KEYC_MOUSEDOWN7_PANE;
1571 if (where == STATUS)
1572 key = KEYC_MOUSEDOWN7_STATUS;
1573 if (where == STATUS_LEFT)
1574 key = KEYC_MOUSEDOWN7_STATUS_LEFT;
1575 if (where == STATUS_RIGHT)
1576 key = KEYC_MOUSEDOWN7_STATUS_RIGHT;
1577 if (where == STATUS_DEFAULT)
1578 key = KEYC_MOUSEDOWN7_STATUS_DEFAULT;
1579 if (where == SCROLLBAR_UP)
1580 key = KEYC_MOUSEDOWN7_SCROLLBAR_UP;
1581 if (where == SCROLLBAR_SLIDER)
1582 key = KEYC_MOUSEDOWN7_SCROLLBAR_SLIDER;
1583 if (where == SCROLLBAR_DOWN)
1584 key = KEYC_MOUSEDOWN7_SCROLLBAR_DOWN;
1585 if (where == BORDER)
1586 key = KEYC_MOUSEDOWN7_BORDER;
1587 break;
1588 case MOUSE_BUTTON_8:
1589 if (where == PANE)
1590 key = KEYC_MOUSEDOWN8_PANE;
1591 if (where == STATUS)
1592 key = KEYC_MOUSEDOWN8_STATUS;
1593 if (where == STATUS_LEFT)
1594 key = KEYC_MOUSEDOWN8_STATUS_LEFT;
1595 if (where == STATUS_RIGHT)
1596 key = KEYC_MOUSEDOWN8_STATUS_RIGHT;
1597 if (where == STATUS_DEFAULT)
1598 key = KEYC_MOUSEDOWN8_STATUS_DEFAULT;
1599 if (where == SCROLLBAR_UP)
1600 key = KEYC_MOUSEDOWN8_SCROLLBAR_UP;
1601 if (where == SCROLLBAR_SLIDER)
1602 key = KEYC_MOUSEDOWN8_SCROLLBAR_SLIDER;
1603 if (where == SCROLLBAR_DOWN)
1604 key = KEYC_MOUSEDOWN8_SCROLLBAR_DOWN;
1605 if (where == BORDER)
1606 key = KEYC_MOUSEDOWN8_BORDER;
1607 break;
1608 case MOUSE_BUTTON_9:
1609 if (where == PANE)
1610 key = KEYC_MOUSEDOWN9_PANE;
1611 if (where == STATUS)
1612 key = KEYC_MOUSEDOWN9_STATUS;
1613 if (where == STATUS_LEFT)
1614 key = KEYC_MOUSEDOWN9_STATUS_LEFT;
1615 if (where == STATUS_RIGHT)
1616 key = KEYC_MOUSEDOWN9_STATUS_RIGHT;
1617 if (where == STATUS_DEFAULT)
1618 key = KEYC_MOUSEDOWN9_STATUS_DEFAULT;
1619 if (where == SCROLLBAR_UP)
1620 key = KEYC_MOUSEDOWN9_SCROLLBAR_UP;
1621 if (where == SCROLLBAR_SLIDER)
1622 key = KEYC_MOUSEDOWN9_SCROLLBAR_SLIDER;
1623 if (where == SCROLLBAR_DOWN)
1624 key = KEYC_MOUSEDOWN9_SCROLLBAR_DOWN;
1625 if (where == BORDER)
1626 key = KEYC_MOUSEDOWN9_BORDER;
1627 break;
1628 case MOUSE_BUTTON_10:
1629 if (where == PANE)
1630 key = KEYC_MOUSEDOWN10_PANE;
1631 if (where == STATUS)
1632 key = KEYC_MOUSEDOWN10_STATUS;
1633 if (where == STATUS_LEFT)
1634 key = KEYC_MOUSEDOWN10_STATUS_LEFT;
1635 if (where == STATUS_RIGHT)
1636 key = KEYC_MOUSEDOWN10_STATUS_RIGHT;
1637 if (where == STATUS_DEFAULT)
1638 key = KEYC_MOUSEDOWN10_STATUS_DEFAULT;
1639 if (where == SCROLLBAR_UP)
1640 key = KEYC_MOUSEDOWN10_SCROLLBAR_UP;
1641 if (where == SCROLLBAR_SLIDER)
1642 key = KEYC_MOUSEDOWN10_SCROLLBAR_SLIDER;
1643 if (where == SCROLLBAR_DOWN)
1644 key = KEYC_MOUSEDOWN10_SCROLLBAR_DOWN;
1645 if (where == BORDER)
1646 key = KEYC_MOUSEDOWN10_BORDER;
1647 break;
1648 case MOUSE_BUTTON_11:
1649 if (where == PANE)
1650 key = KEYC_MOUSEDOWN11_PANE;
1651 if (where == STATUS)
1652 key = KEYC_MOUSEDOWN11_STATUS;
1653 if (where == STATUS_LEFT)
1654 key = KEYC_MOUSEDOWN11_STATUS_LEFT;
1655 if (where == STATUS_RIGHT)
1656 key = KEYC_MOUSEDOWN11_STATUS_RIGHT;
1657 if (where == STATUS_DEFAULT)
1658 key = KEYC_MOUSEDOWN11_STATUS_DEFAULT;
1659 if (where == SCROLLBAR_UP)
1660 key = KEYC_MOUSEDOWN11_SCROLLBAR_UP;
1661 if (where == SCROLLBAR_SLIDER)
1662 key = KEYC_MOUSEDOWN11_SCROLLBAR_SLIDER;
1663 if (where == SCROLLBAR_DOWN)
1664 key = KEYC_MOUSEDOWN11_SCROLLBAR_DOWN;
1665 if (where == BORDER)
1666 key = KEYC_MOUSEDOWN11_BORDER;
1667 break;
1669 break;
1670 case SECOND:
1671 switch (MOUSE_BUTTONS(b)) {
1672 case MOUSE_BUTTON_1:
1673 if (where == PANE)
1674 key = KEYC_SECONDCLICK1_PANE;
1675 if (where == STATUS)
1676 key = KEYC_SECONDCLICK1_STATUS;
1677 if (where == STATUS_LEFT)
1678 key = KEYC_SECONDCLICK1_STATUS_LEFT;
1679 if (where == STATUS_RIGHT)
1680 key = KEYC_SECONDCLICK1_STATUS_RIGHT;
1681 if (where == STATUS_DEFAULT)
1682 key = KEYC_SECONDCLICK1_STATUS_DEFAULT;
1683 if (where == SCROLLBAR_UP)
1684 key = KEYC_SECONDCLICK1_SCROLLBAR_UP;
1685 if (where == SCROLLBAR_SLIDER)
1686 key = KEYC_SECONDCLICK1_SCROLLBAR_SLIDER;
1687 if (where == SCROLLBAR_DOWN)
1688 key = KEYC_SECONDCLICK1_SCROLLBAR_DOWN;
1689 if (where == BORDER)
1690 key = KEYC_SECONDCLICK1_BORDER;
1691 break;
1692 case MOUSE_BUTTON_2:
1693 if (where == PANE)
1694 key = KEYC_SECONDCLICK2_PANE;
1695 if (where == STATUS)
1696 key = KEYC_SECONDCLICK2_STATUS;
1697 if (where == STATUS_LEFT)
1698 key = KEYC_SECONDCLICK2_STATUS_LEFT;
1699 if (where == STATUS_RIGHT)
1700 key = KEYC_SECONDCLICK2_STATUS_RIGHT;
1701 if (where == STATUS_DEFAULT)
1702 key = KEYC_SECONDCLICK2_STATUS_DEFAULT;
1703 if (where == SCROLLBAR_UP)
1704 key = KEYC_SECONDCLICK2_SCROLLBAR_UP;
1705 if (where == SCROLLBAR_SLIDER)
1706 key = KEYC_SECONDCLICK2_SCROLLBAR_SLIDER;
1707 if (where == SCROLLBAR_DOWN)
1708 key = KEYC_SECONDCLICK2_SCROLLBAR_DOWN;
1709 if (where == BORDER)
1710 key = KEYC_SECONDCLICK2_BORDER;
1711 break;
1712 case MOUSE_BUTTON_3:
1713 if (where == PANE)
1714 key = KEYC_SECONDCLICK3_PANE;
1715 if (where == STATUS)
1716 key = KEYC_SECONDCLICK3_STATUS;
1717 if (where == STATUS_LEFT)
1718 key = KEYC_SECONDCLICK3_STATUS_LEFT;
1719 if (where == STATUS_RIGHT)
1720 key = KEYC_SECONDCLICK3_STATUS_RIGHT;
1721 if (where == STATUS_DEFAULT)
1722 key = KEYC_SECONDCLICK3_STATUS_DEFAULT;
1723 if (where == SCROLLBAR_UP)
1724 key = KEYC_SECONDCLICK3_SCROLLBAR_UP;
1725 if (where == SCROLLBAR_SLIDER)
1726 key = KEYC_SECONDCLICK3_SCROLLBAR_SLIDER;
1727 if (where == SCROLLBAR_DOWN)
1728 key = KEYC_SECONDCLICK3_SCROLLBAR_DOWN;
1729 if (where == BORDER)
1730 key = KEYC_SECONDCLICK3_BORDER;
1731 break;
1732 case MOUSE_BUTTON_6:
1733 if (where == PANE)
1734 key = KEYC_SECONDCLICK6_PANE;
1735 if (where == STATUS)
1736 key = KEYC_SECONDCLICK6_STATUS;
1737 if (where == STATUS_LEFT)
1738 key = KEYC_SECONDCLICK6_STATUS_LEFT;
1739 if (where == STATUS_RIGHT)
1740 key = KEYC_SECONDCLICK6_STATUS_RIGHT;
1741 if (where == STATUS_DEFAULT)
1742 key = KEYC_SECONDCLICK6_STATUS_DEFAULT;
1743 if (where == SCROLLBAR_UP)
1744 key = KEYC_SECONDCLICK6_SCROLLBAR_UP;
1745 if (where == SCROLLBAR_SLIDER)
1746 key = KEYC_SECONDCLICK6_SCROLLBAR_SLIDER;
1747 if (where == SCROLLBAR_DOWN)
1748 key = KEYC_SECONDCLICK6_SCROLLBAR_DOWN;
1749 if (where == BORDER)
1750 key = KEYC_SECONDCLICK6_BORDER;
1751 break;
1752 case MOUSE_BUTTON_7:
1753 if (where == PANE)
1754 key = KEYC_SECONDCLICK7_PANE;
1755 if (where == STATUS)
1756 key = KEYC_SECONDCLICK7_STATUS;
1757 if (where == STATUS_LEFT)
1758 key = KEYC_SECONDCLICK7_STATUS_LEFT;
1759 if (where == STATUS_RIGHT)
1760 key = KEYC_SECONDCLICK7_STATUS_RIGHT;
1761 if (where == STATUS_DEFAULT)
1762 key = KEYC_SECONDCLICK7_STATUS_DEFAULT;
1763 if (where == SCROLLBAR_UP)
1764 key = KEYC_SECONDCLICK7_SCROLLBAR_UP;
1765 if (where == SCROLLBAR_SLIDER)
1766 key = KEYC_SECONDCLICK7_SCROLLBAR_SLIDER;
1767 if (where == SCROLLBAR_DOWN)
1768 key = KEYC_SECONDCLICK7_SCROLLBAR_DOWN;
1769 if (where == BORDER)
1770 key = KEYC_SECONDCLICK7_BORDER;
1771 break;
1772 case MOUSE_BUTTON_8:
1773 if (where == PANE)
1774 key = KEYC_SECONDCLICK8_PANE;
1775 if (where == STATUS)
1776 key = KEYC_SECONDCLICK8_STATUS;
1777 if (where == STATUS_LEFT)
1778 key = KEYC_SECONDCLICK8_STATUS_LEFT;
1779 if (where == STATUS_RIGHT)
1780 key = KEYC_SECONDCLICK8_STATUS_RIGHT;
1781 if (where == STATUS_DEFAULT)
1782 key = KEYC_SECONDCLICK8_STATUS_DEFAULT;
1783 if (where == SCROLLBAR_UP)
1784 key = KEYC_SECONDCLICK8_SCROLLBAR_UP;
1785 if (where == SCROLLBAR_SLIDER)
1786 key = KEYC_SECONDCLICK8_SCROLLBAR_SLIDER;
1787 if (where == SCROLLBAR_DOWN)
1788 key = KEYC_SECONDCLICK8_SCROLLBAR_DOWN;
1789 if (where == BORDER)
1790 key = KEYC_SECONDCLICK8_BORDER;
1791 break;
1792 case MOUSE_BUTTON_9:
1793 if (where == PANE)
1794 key = KEYC_SECONDCLICK9_PANE;
1795 if (where == STATUS)
1796 key = KEYC_SECONDCLICK9_STATUS;
1797 if (where == STATUS_LEFT)
1798 key = KEYC_SECONDCLICK9_STATUS_LEFT;
1799 if (where == STATUS_RIGHT)
1800 key = KEYC_SECONDCLICK9_STATUS_RIGHT;
1801 if (where == STATUS_DEFAULT)
1802 key = KEYC_SECONDCLICK9_STATUS_DEFAULT;
1803 if (where == SCROLLBAR_UP)
1804 key = KEYC_SECONDCLICK9_SCROLLBAR_UP;
1805 if (where == SCROLLBAR_SLIDER)
1806 key = KEYC_SECONDCLICK9_SCROLLBAR_SLIDER;
1807 if (where == SCROLLBAR_DOWN)
1808 key = KEYC_SECONDCLICK9_SCROLLBAR_DOWN;
1809 if (where == BORDER)
1810 key = KEYC_SECONDCLICK9_BORDER;
1811 break;
1812 case MOUSE_BUTTON_10:
1813 if (where == PANE)
1814 key = KEYC_SECONDCLICK10_PANE;
1815 if (where == STATUS)
1816 key = KEYC_SECONDCLICK10_STATUS;
1817 if (where == STATUS_LEFT)
1818 key = KEYC_SECONDCLICK10_STATUS_LEFT;
1819 if (where == STATUS_RIGHT)
1820 key = KEYC_SECONDCLICK10_STATUS_RIGHT;
1821 if (where == STATUS_DEFAULT)
1822 key = KEYC_SECONDCLICK10_STATUS_DEFAULT;
1823 if (where == SCROLLBAR_UP)
1824 key = KEYC_SECONDCLICK10_SCROLLBAR_UP;
1825 if (where == SCROLLBAR_SLIDER)
1826 key = KEYC_SECONDCLICK10_SCROLLBAR_SLIDER;
1827 if (where == SCROLLBAR_DOWN)
1828 key = KEYC_SECONDCLICK10_SCROLLBAR_DOWN;
1829 if (where == BORDER)
1830 key = KEYC_SECONDCLICK10_BORDER;
1831 break;
1832 case MOUSE_BUTTON_11:
1833 if (where == PANE)
1834 key = KEYC_SECONDCLICK11_PANE;
1835 if (where == STATUS)
1836 key = KEYC_SECONDCLICK11_STATUS;
1837 if (where == STATUS_LEFT)
1838 key = KEYC_SECONDCLICK11_STATUS_LEFT;
1839 if (where == STATUS_RIGHT)
1840 key = KEYC_SECONDCLICK11_STATUS_RIGHT;
1841 if (where == STATUS_DEFAULT)
1842 key = KEYC_SECONDCLICK11_STATUS_DEFAULT;
1843 if (where == SCROLLBAR_UP)
1844 key = KEYC_SECONDCLICK11_SCROLLBAR_UP;
1845 if (where == SCROLLBAR_SLIDER)
1846 key = KEYC_SECONDCLICK11_SCROLLBAR_SLIDER;
1847 if (where == SCROLLBAR_DOWN)
1848 key = KEYC_SECONDCLICK11_SCROLLBAR_DOWN;
1849 if (where == BORDER)
1850 key = KEYC_SECONDCLICK11_BORDER;
1851 break;
1853 break;
1854 case DOUBLE:
1855 switch (MOUSE_BUTTONS(b)) {
1856 case MOUSE_BUTTON_1:
1857 if (where == PANE)
1858 key = KEYC_DOUBLECLICK1_PANE;
1859 if (where == STATUS)
1860 key = KEYC_DOUBLECLICK1_STATUS;
1861 if (where == STATUS_LEFT)
1862 key = KEYC_DOUBLECLICK1_STATUS_LEFT;
1863 if (where == STATUS_RIGHT)
1864 key = KEYC_DOUBLECLICK1_STATUS_RIGHT;
1865 if (where == STATUS_DEFAULT)
1866 key = KEYC_DOUBLECLICK1_STATUS_DEFAULT;
1867 if (where == SCROLLBAR_UP)
1868 key = KEYC_DOUBLECLICK1_SCROLLBAR_UP;
1869 if (where == SCROLLBAR_SLIDER)
1870 key = KEYC_DOUBLECLICK1_SCROLLBAR_SLIDER;
1871 if (where == SCROLLBAR_DOWN)
1872 key = KEYC_DOUBLECLICK1_SCROLLBAR_DOWN;
1873 if (where == BORDER)
1874 key = KEYC_DOUBLECLICK1_BORDER;
1875 break;
1876 case MOUSE_BUTTON_2:
1877 if (where == PANE)
1878 key = KEYC_DOUBLECLICK2_PANE;
1879 if (where == STATUS)
1880 key = KEYC_DOUBLECLICK2_STATUS;
1881 if (where == STATUS_LEFT)
1882 key = KEYC_DOUBLECLICK2_STATUS_LEFT;
1883 if (where == STATUS_RIGHT)
1884 key = KEYC_DOUBLECLICK2_STATUS_RIGHT;
1885 if (where == STATUS_DEFAULT)
1886 key = KEYC_DOUBLECLICK2_STATUS_DEFAULT;
1887 if (where == SCROLLBAR_UP)
1888 key = KEYC_DOUBLECLICK2_SCROLLBAR_UP;
1889 if (where == SCROLLBAR_SLIDER)
1890 key = KEYC_DOUBLECLICK2_SCROLLBAR_SLIDER;
1891 if (where == SCROLLBAR_DOWN)
1892 key = KEYC_DOUBLECLICK2_SCROLLBAR_DOWN;
1893 if (where == BORDER)
1894 key = KEYC_DOUBLECLICK2_BORDER;
1895 break;
1896 case MOUSE_BUTTON_3:
1897 if (where == PANE)
1898 key = KEYC_DOUBLECLICK3_PANE;
1899 if (where == STATUS)
1900 key = KEYC_DOUBLECLICK3_STATUS;
1901 if (where == STATUS_LEFT)
1902 key = KEYC_DOUBLECLICK3_STATUS_LEFT;
1903 if (where == STATUS_RIGHT)
1904 key = KEYC_DOUBLECLICK3_STATUS_RIGHT;
1905 if (where == STATUS_DEFAULT)
1906 key = KEYC_DOUBLECLICK3_STATUS_DEFAULT;
1907 if (where == SCROLLBAR_UP)
1908 key = KEYC_DOUBLECLICK3_SCROLLBAR_UP;
1909 if (where == SCROLLBAR_SLIDER)
1910 key = KEYC_DOUBLECLICK3_SCROLLBAR_SLIDER;
1911 if (where == SCROLLBAR_DOWN)
1912 key = KEYC_DOUBLECLICK3_SCROLLBAR_DOWN;
1913 if (where == BORDER)
1914 key = KEYC_DOUBLECLICK3_BORDER;
1915 break;
1916 case MOUSE_BUTTON_6:
1917 if (where == PANE)
1918 key = KEYC_DOUBLECLICK6_PANE;
1919 if (where == STATUS)
1920 key = KEYC_DOUBLECLICK6_STATUS;
1921 if (where == STATUS_LEFT)
1922 key = KEYC_DOUBLECLICK6_STATUS_LEFT;
1923 if (where == STATUS_RIGHT)
1924 key = KEYC_DOUBLECLICK6_STATUS_RIGHT;
1925 if (where == STATUS_DEFAULT)
1926 key = KEYC_DOUBLECLICK6_STATUS_DEFAULT;
1927 if (where == SCROLLBAR_UP)
1928 key = KEYC_DOUBLECLICK6_SCROLLBAR_UP;
1929 if (where == SCROLLBAR_SLIDER)
1930 key = KEYC_DOUBLECLICK6_SCROLLBAR_SLIDER;
1931 if (where == SCROLLBAR_DOWN)
1932 key = KEYC_DOUBLECLICK6_SCROLLBAR_DOWN;
1933 if (where == BORDER)
1934 key = KEYC_DOUBLECLICK6_BORDER;
1935 break;
1936 case MOUSE_BUTTON_7:
1937 if (where == PANE)
1938 key = KEYC_DOUBLECLICK7_PANE;
1939 if (where == STATUS)
1940 key = KEYC_DOUBLECLICK7_STATUS;
1941 if (where == STATUS_LEFT)
1942 key = KEYC_DOUBLECLICK7_STATUS_LEFT;
1943 if (where == STATUS_RIGHT)
1944 key = KEYC_DOUBLECLICK7_STATUS_RIGHT;
1945 if (where == STATUS_DEFAULT)
1946 key = KEYC_DOUBLECLICK7_STATUS_DEFAULT;
1947 if (where == SCROLLBAR_UP)
1948 key = KEYC_DOUBLECLICK7_SCROLLBAR_UP;
1949 if (where == SCROLLBAR_SLIDER)
1950 key = KEYC_DOUBLECLICK7_SCROLLBAR_SLIDER;
1951 if (where == SCROLLBAR_DOWN)
1952 key = KEYC_DOUBLECLICK7_SCROLLBAR_DOWN;
1953 if (where == BORDER)
1954 key = KEYC_DOUBLECLICK7_BORDER;
1955 break;
1956 case MOUSE_BUTTON_8:
1957 if (where == PANE)
1958 key = KEYC_DOUBLECLICK8_PANE;
1959 if (where == STATUS)
1960 key = KEYC_DOUBLECLICK8_STATUS;
1961 if (where == STATUS_LEFT)
1962 key = KEYC_DOUBLECLICK8_STATUS_LEFT;
1963 if (where == STATUS_RIGHT)
1964 key = KEYC_DOUBLECLICK8_STATUS_RIGHT;
1965 if (where == STATUS_DEFAULT)
1966 key = KEYC_DOUBLECLICK8_STATUS_DEFAULT;
1967 if (where == SCROLLBAR_UP)
1968 key = KEYC_DOUBLECLICK8_SCROLLBAR_UP;
1969 if (where == SCROLLBAR_SLIDER)
1970 key = KEYC_DOUBLECLICK8_SCROLLBAR_SLIDER;
1971 if (where == SCROLLBAR_DOWN)
1972 key = KEYC_DOUBLECLICK8_SCROLLBAR_DOWN;
1973 if (where == BORDER)
1974 key = KEYC_DOUBLECLICK8_BORDER;
1975 break;
1976 case MOUSE_BUTTON_9:
1977 if (where == PANE)
1978 key = KEYC_DOUBLECLICK9_PANE;
1979 if (where == STATUS)
1980 key = KEYC_DOUBLECLICK9_STATUS;
1981 if (where == STATUS_LEFT)
1982 key = KEYC_DOUBLECLICK9_STATUS_LEFT;
1983 if (where == STATUS_RIGHT)
1984 key = KEYC_DOUBLECLICK9_STATUS_RIGHT;
1985 if (where == STATUS_DEFAULT)
1986 key = KEYC_DOUBLECLICK9_STATUS_DEFAULT;
1987 if (where == SCROLLBAR_UP)
1988 key = KEYC_DOUBLECLICK9_SCROLLBAR_UP;
1989 if (where == SCROLLBAR_SLIDER)
1990 key = KEYC_DOUBLECLICK9_SCROLLBAR_SLIDER;
1991 if (where == SCROLLBAR_DOWN)
1992 key = KEYC_DOUBLECLICK9_SCROLLBAR_DOWN;
1993 if (where == BORDER)
1994 key = KEYC_DOUBLECLICK9_BORDER;
1995 break;
1996 case MOUSE_BUTTON_10:
1997 if (where == PANE)
1998 key = KEYC_DOUBLECLICK10_PANE;
1999 if (where == STATUS)
2000 key = KEYC_DOUBLECLICK10_STATUS;
2001 if (where == STATUS_LEFT)
2002 key = KEYC_DOUBLECLICK10_STATUS_LEFT;
2003 if (where == STATUS_RIGHT)
2004 key = KEYC_DOUBLECLICK10_STATUS_RIGHT;
2005 if (where == STATUS_DEFAULT)
2006 key = KEYC_DOUBLECLICK10_STATUS_DEFAULT;
2007 if (where == SCROLLBAR_UP)
2008 key = KEYC_DOUBLECLICK10_SCROLLBAR_UP;
2009 if (where == SCROLLBAR_SLIDER)
2010 key = KEYC_DOUBLECLICK10_SCROLLBAR_SLIDER;
2011 if (where == SCROLLBAR_DOWN)
2012 key = KEYC_DOUBLECLICK10_SCROLLBAR_DOWN;
2013 if (where == BORDER)
2014 key = KEYC_DOUBLECLICK10_BORDER;
2015 break;
2016 case MOUSE_BUTTON_11:
2017 if (where == PANE)
2018 key = KEYC_DOUBLECLICK11_PANE;
2019 if (where == STATUS)
2020 key = KEYC_DOUBLECLICK11_STATUS;
2021 if (where == STATUS_LEFT)
2022 key = KEYC_DOUBLECLICK11_STATUS_LEFT;
2023 if (where == STATUS_RIGHT)
2024 key = KEYC_DOUBLECLICK11_STATUS_RIGHT;
2025 if (where == STATUS_DEFAULT)
2026 key = KEYC_DOUBLECLICK11_STATUS_DEFAULT;
2027 if (where == SCROLLBAR_UP)
2028 key = KEYC_DOUBLECLICK11_SCROLLBAR_UP;
2029 if (where == SCROLLBAR_SLIDER)
2030 key = KEYC_DOUBLECLICK11_SCROLLBAR_SLIDER;
2031 if (where == SCROLLBAR_DOWN)
2032 key = KEYC_DOUBLECLICK11_SCROLLBAR_DOWN;
2033 if (where == BORDER)
2034 key = KEYC_DOUBLECLICK11_BORDER;
2035 break;
2037 break;
2038 case TRIPLE:
2039 switch (MOUSE_BUTTONS(b)) {
2040 case MOUSE_BUTTON_1:
2041 if (where == PANE)
2042 key = KEYC_TRIPLECLICK1_PANE;
2043 if (where == STATUS)
2044 key = KEYC_TRIPLECLICK1_STATUS;
2045 if (where == STATUS_LEFT)
2046 key = KEYC_TRIPLECLICK1_STATUS_LEFT;
2047 if (where == STATUS_RIGHT)
2048 key = KEYC_TRIPLECLICK1_STATUS_RIGHT;
2049 if (where == STATUS_DEFAULT)
2050 key = KEYC_TRIPLECLICK1_STATUS_DEFAULT;
2051 if (where == SCROLLBAR_UP)
2052 key = KEYC_TRIPLECLICK1_SCROLLBAR_UP;
2053 if (where == SCROLLBAR_SLIDER)
2054 key = KEYC_TRIPLECLICK1_SCROLLBAR_SLIDER;
2055 if (where == SCROLLBAR_DOWN)
2056 key = KEYC_TRIPLECLICK1_SCROLLBAR_DOWN;
2057 if (where == BORDER)
2058 key = KEYC_TRIPLECLICK1_BORDER;
2059 break;
2060 case MOUSE_BUTTON_2:
2061 if (where == PANE)
2062 key = KEYC_TRIPLECLICK2_PANE;
2063 if (where == STATUS)
2064 key = KEYC_TRIPLECLICK2_STATUS;
2065 if (where == STATUS_LEFT)
2066 key = KEYC_TRIPLECLICK2_STATUS_LEFT;
2067 if (where == STATUS_RIGHT)
2068 key = KEYC_TRIPLECLICK2_STATUS_RIGHT;
2069 if (where == STATUS_DEFAULT)
2070 key = KEYC_TRIPLECLICK2_STATUS_DEFAULT;
2071 if (where == SCROLLBAR_UP)
2072 key = KEYC_TRIPLECLICK2_SCROLLBAR_UP;
2073 if (where == SCROLLBAR_SLIDER)
2074 key = KEYC_TRIPLECLICK2_SCROLLBAR_SLIDER;
2075 if (where == SCROLLBAR_DOWN)
2076 key = KEYC_TRIPLECLICK2_SCROLLBAR_DOWN;
2077 if (where == BORDER)
2078 key = KEYC_TRIPLECLICK2_BORDER;
2079 break;
2080 case MOUSE_BUTTON_3:
2081 if (where == PANE)
2082 key = KEYC_TRIPLECLICK3_PANE;
2083 if (where == STATUS)
2084 key = KEYC_TRIPLECLICK3_STATUS;
2085 if (where == STATUS_LEFT)
2086 key = KEYC_TRIPLECLICK3_STATUS_LEFT;
2087 if (where == STATUS_RIGHT)
2088 key = KEYC_TRIPLECLICK3_STATUS_RIGHT;
2089 if (where == STATUS_DEFAULT)
2090 key = KEYC_TRIPLECLICK3_STATUS_DEFAULT;
2091 if (where == SCROLLBAR_UP)
2092 key = KEYC_TRIPLECLICK3_SCROLLBAR_UP;
2093 if (where == SCROLLBAR_SLIDER)
2094 key = KEYC_TRIPLECLICK3_SCROLLBAR_SLIDER;
2095 if (where == SCROLLBAR_DOWN)
2096 key = KEYC_TRIPLECLICK3_SCROLLBAR_DOWN;
2097 if (where == BORDER)
2098 key = KEYC_TRIPLECLICK3_BORDER;
2099 break;
2100 case MOUSE_BUTTON_6:
2101 if (where == PANE)
2102 key = KEYC_TRIPLECLICK6_PANE;
2103 if (where == STATUS)
2104 key = KEYC_TRIPLECLICK6_STATUS;
2105 if (where == STATUS_LEFT)
2106 key = KEYC_TRIPLECLICK6_STATUS_LEFT;
2107 if (where == STATUS_RIGHT)
2108 key = KEYC_TRIPLECLICK6_STATUS_RIGHT;
2109 if (where == STATUS_DEFAULT)
2110 key = KEYC_TRIPLECLICK6_STATUS_DEFAULT;
2111 if (where == SCROLLBAR_UP)
2112 key = KEYC_TRIPLECLICK6_SCROLLBAR_UP;
2113 if (where == SCROLLBAR_SLIDER)
2114 key = KEYC_TRIPLECLICK6_SCROLLBAR_SLIDER;
2115 if (where == SCROLLBAR_DOWN)
2116 key = KEYC_TRIPLECLICK6_SCROLLBAR_DOWN;
2117 if (where == BORDER)
2118 key = KEYC_TRIPLECLICK6_BORDER;
2119 break;
2120 case MOUSE_BUTTON_7:
2121 if (where == PANE)
2122 key = KEYC_TRIPLECLICK7_PANE;
2123 if (where == STATUS)
2124 key = KEYC_TRIPLECLICK7_STATUS;
2125 if (where == STATUS_LEFT)
2126 key = KEYC_TRIPLECLICK7_STATUS_LEFT;
2127 if (where == STATUS_RIGHT)
2128 key = KEYC_TRIPLECLICK7_STATUS_RIGHT;
2129 if (where == STATUS_DEFAULT)
2130 key = KEYC_TRIPLECLICK7_STATUS_DEFAULT;
2131 if (where == SCROLLBAR_UP)
2132 key = KEYC_TRIPLECLICK7_SCROLLBAR_UP;
2133 if (where == SCROLLBAR_SLIDER)
2134 key = KEYC_TRIPLECLICK7_SCROLLBAR_SLIDER;
2135 if (where == SCROLLBAR_DOWN)
2136 key = KEYC_TRIPLECLICK7_SCROLLBAR_DOWN;
2137 if (where == BORDER)
2138 key = KEYC_TRIPLECLICK7_BORDER;
2139 break;
2140 case MOUSE_BUTTON_8:
2141 if (where == PANE)
2142 key = KEYC_TRIPLECLICK8_PANE;
2143 if (where == STATUS)
2144 key = KEYC_TRIPLECLICK8_STATUS;
2145 if (where == STATUS_LEFT)
2146 key = KEYC_TRIPLECLICK8_STATUS_LEFT;
2147 if (where == STATUS_RIGHT)
2148 key = KEYC_TRIPLECLICK8_STATUS_RIGHT;
2149 if (where == STATUS_DEFAULT)
2150 key = KEYC_TRIPLECLICK8_STATUS_DEFAULT;
2151 if (where == SCROLLBAR_UP)
2152 key = KEYC_TRIPLECLICK8_SCROLLBAR_UP;
2153 if (where == SCROLLBAR_SLIDER)
2154 key = KEYC_TRIPLECLICK8_SCROLLBAR_SLIDER;
2155 if (where == SCROLLBAR_DOWN)
2156 key = KEYC_TRIPLECLICK8_SCROLLBAR_DOWN;
2157 if (where == BORDER)
2158 key = KEYC_TRIPLECLICK8_BORDER;
2159 break;
2160 case MOUSE_BUTTON_9:
2161 if (where == PANE)
2162 key = KEYC_TRIPLECLICK9_PANE;
2163 if (where == STATUS)
2164 key = KEYC_TRIPLECLICK9_STATUS;
2165 if (where == STATUS_LEFT)
2166 key = KEYC_TRIPLECLICK9_STATUS_LEFT;
2167 if (where == STATUS_RIGHT)
2168 key = KEYC_TRIPLECLICK9_STATUS_RIGHT;
2169 if (where == STATUS_DEFAULT)
2170 key = KEYC_TRIPLECLICK9_STATUS_DEFAULT;
2171 if (where == SCROLLBAR_UP)
2172 key = KEYC_TRIPLECLICK9_SCROLLBAR_UP;
2173 if (where == SCROLLBAR_SLIDER)
2174 key = KEYC_TRIPLECLICK9_SCROLLBAR_SLIDER;
2175 if (where == SCROLLBAR_DOWN)
2176 key = KEYC_TRIPLECLICK9_SCROLLBAR_DOWN;
2177 if (where == BORDER)
2178 key = KEYC_TRIPLECLICK9_BORDER;
2179 break;
2180 case MOUSE_BUTTON_10:
2181 if (where == PANE)
2182 key = KEYC_TRIPLECLICK10_PANE;
2183 if (where == STATUS)
2184 key = KEYC_TRIPLECLICK10_STATUS;
2185 if (where == STATUS_LEFT)
2186 key = KEYC_TRIPLECLICK10_STATUS_LEFT;
2187 if (where == STATUS_RIGHT)
2188 key = KEYC_TRIPLECLICK10_STATUS_RIGHT;
2189 if (where == STATUS_DEFAULT)
2190 key = KEYC_TRIPLECLICK10_STATUS_DEFAULT;
2191 if (where == SCROLLBAR_UP)
2192 key = KEYC_TRIPLECLICK10_SCROLLBAR_UP;
2193 if (where == SCROLLBAR_SLIDER)
2194 key = KEYC_TRIPLECLICK10_SCROLLBAR_SLIDER;
2195 if (where == SCROLLBAR_DOWN)
2196 key = KEYC_TRIPLECLICK10_SCROLLBAR_DOWN;
2197 if (where == BORDER)
2198 key = KEYC_TRIPLECLICK10_BORDER;
2199 break;
2200 case MOUSE_BUTTON_11:
2201 if (where == PANE)
2202 key = KEYC_TRIPLECLICK11_PANE;
2203 if (where == STATUS)
2204 key = KEYC_TRIPLECLICK11_STATUS;
2205 if (where == STATUS_LEFT)
2206 key = KEYC_TRIPLECLICK11_STATUS_LEFT;
2207 if (where == STATUS_RIGHT)
2208 key = KEYC_TRIPLECLICK11_STATUS_RIGHT;
2209 if (where == STATUS_DEFAULT)
2210 key = KEYC_TRIPLECLICK11_STATUS_DEFAULT;
2211 if (where == SCROLLBAR_UP)
2212 key = KEYC_TRIPLECLICK11_SCROLLBAR_UP;
2213 if (where == SCROLLBAR_SLIDER)
2214 key = KEYC_TRIPLECLICK11_SCROLLBAR_SLIDER;
2215 if (where == SCROLLBAR_DOWN)
2216 key = KEYC_TRIPLECLICK11_SCROLLBAR_DOWN;
2217 if (where == BORDER)
2218 key = KEYC_TRIPLECLICK11_BORDER;
2219 break;
2221 break;
2223 if (key == KEYC_UNKNOWN)
2224 return (KEYC_UNKNOWN);
2226 out:
2227 /* Apply modifiers if any. */
2228 if (b & MOUSE_MASK_META)
2229 key |= KEYC_META;
2230 if (b & MOUSE_MASK_CTRL)
2231 key |= KEYC_CTRL;
2232 if (b & MOUSE_MASK_SHIFT)
2233 key |= KEYC_SHIFT;
2235 if (log_get_level() != 0)
2236 log_debug("mouse key is %s", key_string_lookup_key (key, 1));
2237 return (key);
2240 /* Is this a bracket paste key? */
2241 static int
2242 server_client_is_bracket_paste(struct client *c, key_code key)
2244 if (key == KEYC_PASTE_START) {
2245 c->flags |= CLIENT_BRACKETPASTING;
2246 log_debug("%s: bracket paste on", c->name);
2247 return (0);
2250 if (key == KEYC_PASTE_END) {
2251 c->flags &= ~CLIENT_BRACKETPASTING;
2252 log_debug("%s: bracket paste off", c->name);
2253 return (0);
2256 return !!(c->flags & CLIENT_BRACKETPASTING);
2259 /* Is this fast enough to probably be a paste? */
2260 static int
2261 server_client_is_assume_paste(struct client *c)
2263 struct session *s = c->session;
2264 struct timeval tv;
2265 int t;
2267 if (c->flags & CLIENT_BRACKETPASTING)
2268 return (0);
2269 if ((t = options_get_number(s->options, "assume-paste-time")) == 0)
2270 return (0);
2272 timersub(&c->activity_time, &c->last_activity_time, &tv);
2273 if (tv.tv_sec == 0 && tv.tv_usec < t * 1000) {
2274 if (c->flags & CLIENT_ASSUMEPASTING)
2275 return (1);
2276 c->flags |= CLIENT_ASSUMEPASTING;
2277 log_debug("%s: assume paste on", c->name);
2278 return (0);
2280 if (c->flags & CLIENT_ASSUMEPASTING) {
2281 c->flags &= ~CLIENT_ASSUMEPASTING;
2282 log_debug("%s: assume paste off", c->name);
2284 return (0);
2287 /* Has the latest client changed? */
2288 static void
2289 server_client_update_latest(struct client *c)
2291 struct window *w;
2293 if (c->session == NULL)
2294 return;
2295 w = c->session->curw->window;
2297 if (w->latest == c)
2298 return;
2299 w->latest = c;
2301 if (options_get_number(w->options, "window-size") == WINDOW_SIZE_LATEST)
2302 recalculate_size(w, 0);
2304 notify_client("client-active", c);
2307 /* Get repeat time. */
2308 static u_int
2309 server_client_repeat_time(struct client *c, struct key_binding *bd)
2311 struct session *s = c->session;
2312 u_int repeat, initial;
2314 if (~bd->flags & KEY_BINDING_REPEAT)
2315 return (0);
2316 repeat = options_get_number(s->options, "repeat-time");
2317 if (repeat == 0)
2318 return (0);
2319 if ((~c->flags & CLIENT_REPEAT) || bd->key != c->last_key) {
2320 initial = options_get_number(s->options, "initial-repeat-time");
2321 if (initial != 0)
2322 repeat = initial;
2324 return (repeat);
2328 * Handle data key input from client. This owns and can modify the key event it
2329 * is given and is responsible for freeing it.
2331 static enum cmd_retval
2332 server_client_key_callback(struct cmdq_item *item, void *data)
2334 struct client *c = cmdq_get_client(item);
2335 struct key_event *event = data;
2336 key_code key = event->key;
2337 struct mouse_event *m = &event->m;
2338 struct session *s = c->session;
2339 struct winlink *wl;
2340 struct window_pane *wp;
2341 struct window_mode_entry *wme;
2342 struct timeval tv;
2343 struct key_table *table, *first;
2344 struct key_binding *bd;
2345 u_int repeat;
2346 uint64_t flags, prefix_delay;
2347 struct cmd_find_state fs;
2348 key_code key0, prefix, prefix2;
2350 /* Check the client is good to accept input. */
2351 if (s == NULL || (c->flags & CLIENT_UNATTACHEDFLAGS))
2352 goto out;
2353 wl = s->curw;
2355 /* Update the activity timer. */
2356 memcpy(&c->last_activity_time, &c->activity_time,
2357 sizeof c->last_activity_time);
2358 if (gettimeofday(&c->activity_time, NULL) != 0)
2359 fatal("gettimeofday failed");
2360 session_update_activity(s, &c->activity_time);
2362 /* Check for mouse keys. */
2363 m->valid = 0;
2364 if (key == KEYC_MOUSE || key == KEYC_DOUBLECLICK) {
2365 if (c->flags & CLIENT_READONLY)
2366 goto out;
2367 key = server_client_check_mouse(c, event);
2368 if (key == KEYC_UNKNOWN)
2369 goto out;
2371 m->valid = 1;
2372 m->key = key;
2375 * Mouse drag is in progress, so fire the callback (now that
2376 * the mouse event is valid).
2378 if ((key & KEYC_MASK_KEY) == KEYC_DRAGGING) {
2379 c->tty.mouse_drag_update(c, m);
2380 goto out;
2382 event->key = key;
2385 /* Find affected pane. */
2386 if (!KEYC_IS_MOUSE(key) || cmd_find_from_mouse(&fs, m, 0) != 0)
2387 cmd_find_from_client(&fs, c, 0);
2388 wp = fs.wp;
2390 /* Forward mouse keys if disabled. */
2391 if (KEYC_IS_MOUSE(key) && !options_get_number(s->options, "mouse"))
2392 goto forward_key;
2394 /* Forward if bracket pasting. */
2395 if (server_client_is_bracket_paste (c, key))
2396 goto paste_key;
2398 /* Treat everything as a regular key when pasting is detected. */
2399 if (!KEYC_IS_MOUSE(key) &&
2400 key != KEYC_FOCUS_IN &&
2401 key != KEYC_FOCUS_OUT &&
2402 (~key & KEYC_SENT) &&
2403 server_client_is_assume_paste(c))
2404 goto paste_key;
2407 * Work out the current key table. If the pane is in a mode, use
2408 * the mode table instead of the default key table.
2410 if (server_client_is_default_key_table(c, c->keytable) &&
2411 wp != NULL &&
2412 (wme = TAILQ_FIRST(&wp->modes)) != NULL &&
2413 wme->mode->key_table != NULL)
2414 table = key_bindings_get_table(wme->mode->key_table(wme), 1);
2415 else
2416 table = c->keytable;
2417 first = table;
2419 table_changed:
2421 * The prefix always takes precedence and forces a switch to the prefix
2422 * table, unless we are already there.
2424 prefix = (key_code)options_get_number(s->options, "prefix");
2425 prefix2 = (key_code)options_get_number(s->options, "prefix2");
2426 key0 = (key & (KEYC_MASK_KEY|KEYC_MASK_MODIFIERS));
2427 if ((key0 == (prefix & (KEYC_MASK_KEY|KEYC_MASK_MODIFIERS)) ||
2428 key0 == (prefix2 & (KEYC_MASK_KEY|KEYC_MASK_MODIFIERS))) &&
2429 strcmp(table->name, "prefix") != 0) {
2430 server_client_set_key_table(c, "prefix");
2431 server_status_client(c);
2432 goto out;
2434 flags = c->flags;
2436 try_again:
2437 /* Log key table. */
2438 if (wp == NULL)
2439 log_debug("key table %s (no pane)", table->name);
2440 else
2441 log_debug("key table %s (pane %%%u)", table->name, wp->id);
2442 if (c->flags & CLIENT_REPEAT)
2443 log_debug("currently repeating");
2445 bd = key_bindings_get(table, key0);
2448 * If prefix-timeout is enabled and we're in the prefix table, see if
2449 * the timeout has been exceeded. Revert to the root table if so.
2451 prefix_delay = options_get_number(global_options, "prefix-timeout");
2452 if (prefix_delay > 0 &&
2453 strcmp(table->name, "prefix") == 0 &&
2454 server_client_key_table_activity_diff(c) > prefix_delay) {
2456 * If repeating is active and this is a repeating binding,
2457 * ignore the timeout.
2459 if (bd != NULL &&
2460 (c->flags & CLIENT_REPEAT) &&
2461 (bd->flags & KEY_BINDING_REPEAT)) {
2462 log_debug("prefix timeout ignored, repeat is active");
2463 } else {
2464 log_debug("prefix timeout exceeded");
2465 server_client_set_key_table(c, NULL);
2466 first = table = c->keytable;
2467 server_status_client(c);
2468 goto table_changed;
2472 /* Try to see if there is a key binding in the current table. */
2473 if (bd != NULL) {
2475 * Key was matched in this table. If currently repeating but a
2476 * non-repeating binding was found, stop repeating and try
2477 * again in the root table.
2479 if ((c->flags & CLIENT_REPEAT) &&
2480 (~bd->flags & KEY_BINDING_REPEAT)) {
2481 log_debug("found in key table %s (not repeating)",
2482 table->name);
2483 server_client_set_key_table(c, NULL);
2484 first = table = c->keytable;
2485 c->flags &= ~CLIENT_REPEAT;
2486 server_status_client(c);
2487 goto table_changed;
2489 log_debug("found in key table %s", table->name);
2492 * Take a reference to this table to make sure the key binding
2493 * doesn't disappear.
2495 table->references++;
2498 * If this is a repeating key, start the timer. Otherwise reset
2499 * the client back to the root table.
2501 repeat = server_client_repeat_time(c, bd);
2502 if (repeat != 0) {
2503 c->flags |= CLIENT_REPEAT;
2504 c->last_key = bd->key;
2506 tv.tv_sec = repeat / 1000;
2507 tv.tv_usec = (repeat % 1000) * 1000L;
2508 evtimer_del(&c->repeat_timer);
2509 evtimer_add(&c->repeat_timer, &tv);
2510 } else {
2511 c->flags &= ~CLIENT_REPEAT;
2512 server_client_set_key_table(c, NULL);
2514 server_status_client(c);
2516 /* Execute the key binding. */
2517 key_bindings_dispatch(bd, item, c, event, &fs);
2518 key_bindings_unref_table(table);
2519 goto out;
2523 * No match, try the ANY key.
2525 if (key0 != KEYC_ANY) {
2526 key0 = KEYC_ANY;
2527 goto try_again;
2531 * Binding movement keys is useless since we only turn them on when the
2532 * application requests, so don't let them exit the prefix table.
2534 if (key == KEYC_MOUSEMOVE_PANE ||
2535 key == KEYC_MOUSEMOVE_STATUS ||
2536 key == KEYC_MOUSEMOVE_STATUS_LEFT ||
2537 key == KEYC_MOUSEMOVE_STATUS_RIGHT ||
2538 key == KEYC_MOUSEMOVE_STATUS_DEFAULT ||
2539 key == KEYC_MOUSEMOVE_BORDER)
2540 goto forward_key;
2543 * No match in this table. If not in the root table or if repeating
2544 * switch the client back to the root table and try again.
2546 log_debug("not found in key table %s", table->name);
2547 if (!server_client_is_default_key_table(c, table) ||
2548 (c->flags & CLIENT_REPEAT)) {
2549 log_debug("trying in root table");
2550 server_client_set_key_table(c, NULL);
2551 table = c->keytable;
2552 if (c->flags & CLIENT_REPEAT)
2553 first = table;
2554 c->flags &= ~CLIENT_REPEAT;
2555 server_status_client(c);
2556 goto table_changed;
2560 * No match in the root table either. If this wasn't the first table
2561 * tried, don't pass the key to the pane.
2563 if (first != table && (~flags & CLIENT_REPEAT)) {
2564 server_client_set_key_table(c, NULL);
2565 server_status_client(c);
2566 goto out;
2569 forward_key:
2570 if (c->flags & CLIENT_READONLY)
2571 goto out;
2572 if (wp != NULL)
2573 window_pane_key(wp, c, s, wl, key, m);
2574 goto out;
2576 paste_key:
2577 if (c->flags & CLIENT_READONLY)
2578 goto out;
2579 if (event->buf != NULL)
2580 window_pane_paste(wp, key, event->buf, event->len);
2581 key = KEYC_NONE;
2582 goto out;
2584 out:
2585 if (s != NULL && key != KEYC_FOCUS_OUT)
2586 server_client_update_latest(c);
2587 free(event->buf);
2588 free(event);
2589 return (CMD_RETURN_NORMAL);
2592 /* Handle a key event. */
2594 server_client_handle_key(struct client *c, struct key_event *event)
2596 struct session *s = c->session;
2597 struct cmdq_item *item;
2599 /* Check the client is good to accept input. */
2600 if (s == NULL || (c->flags & CLIENT_UNATTACHEDFLAGS))
2601 return (0);
2604 * Key presses in overlay mode and the command prompt are a special
2605 * case. The queue might be blocked so they need to be processed
2606 * immediately rather than queued.
2608 if (~c->flags & CLIENT_READONLY) {
2609 if (c->message_string != NULL) {
2610 if (c->message_ignore_keys)
2611 return (0);
2612 status_message_clear(c);
2614 if (c->overlay_key != NULL) {
2615 switch (c->overlay_key(c, c->overlay_data, event)) {
2616 case 0:
2617 return (0);
2618 case 1:
2619 server_client_clear_overlay(c);
2620 return (0);
2623 server_client_clear_overlay(c);
2624 if (c->prompt_string != NULL) {
2625 if (status_prompt_key(c, event->key) == 0)
2626 return (0);
2631 * Add the key to the queue so it happens after any commands queued by
2632 * previous keys.
2634 item = cmdq_get_callback(server_client_key_callback, event);
2635 cmdq_append(c, item);
2636 return (1);
2639 /* Client functions that need to happen every loop. */
2640 void
2641 server_client_loop(void)
2643 struct client *c;
2644 struct window *w;
2645 struct window_pane *wp;
2647 /* Check for window resize. This is done before redrawing. */
2648 RB_FOREACH(w, windows, &windows)
2649 server_client_check_window_resize(w);
2651 /* Check clients. */
2652 TAILQ_FOREACH(c, &clients, entry) {
2653 server_client_check_exit(c);
2654 if (c->session != NULL) {
2655 server_client_check_modes(c);
2656 server_client_check_redraw(c);
2657 server_client_reset_state(c);
2662 * Any windows will have been redrawn as part of clients, so clear
2663 * their flags now.
2665 RB_FOREACH(w, windows, &windows) {
2666 TAILQ_FOREACH(wp, &w->panes, entry) {
2667 if (wp->fd != -1) {
2668 server_client_check_pane_resize(wp);
2669 server_client_check_pane_buffer(wp);
2671 wp->flags &= ~(PANE_REDRAW|PANE_REDRAWSCROLLBAR);
2673 check_window_name(w);
2677 /* Check if window needs to be resized. */
2678 static void
2679 server_client_check_window_resize(struct window *w)
2681 struct winlink *wl;
2683 if (~w->flags & WINDOW_RESIZE)
2684 return;
2686 TAILQ_FOREACH(wl, &w->winlinks, wentry) {
2687 if (wl->session->attached != 0 && wl->session->curw == wl)
2688 break;
2690 if (wl == NULL)
2691 return;
2693 log_debug("%s: resizing window @%u", __func__, w->id);
2694 resize_window(w, w->new_sx, w->new_sy, w->new_xpixel, w->new_ypixel);
2697 /* Resize timer event. */
2698 static void
2699 server_client_resize_timer(__unused int fd, __unused short events, void *data)
2701 struct window_pane *wp = data;
2703 log_debug("%s: %%%u resize timer expired", __func__, wp->id);
2704 evtimer_del(&wp->resize_timer);
2707 /* Check if pane should be resized. */
2708 static void
2709 server_client_check_pane_resize(struct window_pane *wp)
2711 struct window_pane_resize *r;
2712 struct window_pane_resize *r1;
2713 struct window_pane_resize *first;
2714 struct window_pane_resize *last;
2715 struct timeval tv = { .tv_usec = 250000 };
2717 if (TAILQ_EMPTY(&wp->resize_queue))
2718 return;
2720 if (!event_initialized(&wp->resize_timer))
2721 evtimer_set(&wp->resize_timer, server_client_resize_timer, wp);
2722 if (evtimer_pending(&wp->resize_timer, NULL))
2723 return;
2725 log_debug("%s: %%%u needs to be resized", __func__, wp->id);
2726 TAILQ_FOREACH(r, &wp->resize_queue, entry) {
2727 log_debug("queued resize: %ux%u -> %ux%u", r->osx, r->osy,
2728 r->sx, r->sy);
2732 * There are three cases that matter:
2734 * - Only one resize. It can just be applied.
2736 * - Multiple resizes and the ending size is different from the
2737 * starting size. We can discard all resizes except the most recent.
2739 * - Multiple resizes and the ending size is the same as the starting
2740 * size. We must resize at least twice to force the application to
2741 * redraw. So apply the first and leave the last on the queue for
2742 * next time.
2744 first = TAILQ_FIRST(&wp->resize_queue);
2745 last = TAILQ_LAST(&wp->resize_queue, window_pane_resizes);
2746 if (first == last) {
2747 /* Only one resize. */
2748 window_pane_send_resize(wp, first->sx, first->sy);
2749 TAILQ_REMOVE(&wp->resize_queue, first, entry);
2750 free(first);
2751 } else if (last->sx != first->osx || last->sy != first->osy) {
2752 /* Multiple resizes ending up with a different size. */
2753 window_pane_send_resize(wp, last->sx, last->sy);
2754 TAILQ_FOREACH_SAFE(r, &wp->resize_queue, entry, r1) {
2755 TAILQ_REMOVE(&wp->resize_queue, r, entry);
2756 free(r);
2758 } else {
2760 * Multiple resizes ending up with the same size. There will
2761 * not be more than one to the same size in succession so we
2762 * can just use the last-but-one on the list and leave the last
2763 * for later. We reduce the time until the next check to avoid
2764 * a long delay between the resizes.
2766 r = TAILQ_PREV(last, window_pane_resizes, entry);
2767 window_pane_send_resize(wp, r->sx, r->sy);
2768 TAILQ_FOREACH_SAFE(r, &wp->resize_queue, entry, r1) {
2769 if (r == last)
2770 break;
2771 TAILQ_REMOVE(&wp->resize_queue, r, entry);
2772 free(r);
2774 tv.tv_usec = 10000;
2776 evtimer_add(&wp->resize_timer, &tv);
2779 /* Check pane buffer size. */
2780 static void
2781 server_client_check_pane_buffer(struct window_pane *wp)
2783 struct evbuffer *evb = wp->event->input;
2784 size_t minimum;
2785 struct client *c;
2786 struct window_pane_offset *wpo;
2787 int off = 1, flag;
2788 u_int attached_clients = 0;
2789 size_t new_size;
2792 * Work out the minimum used size. This is the most that can be removed
2793 * from the buffer.
2795 minimum = wp->offset.used;
2796 if (wp->pipe_fd != -1 && wp->pipe_offset.used < minimum)
2797 minimum = wp->pipe_offset.used;
2798 TAILQ_FOREACH(c, &clients, entry) {
2799 if (c->session == NULL)
2800 continue;
2801 attached_clients++;
2803 if (~c->flags & CLIENT_CONTROL) {
2804 off = 0;
2805 continue;
2807 wpo = control_pane_offset(c, wp, &flag);
2808 if (wpo == NULL) {
2809 if (!flag)
2810 off = 0;
2811 continue;
2813 if (!flag)
2814 off = 0;
2816 window_pane_get_new_data(wp, wpo, &new_size);
2817 log_debug("%s: %s has %zu bytes used and %zu left for %%%u",
2818 __func__, c->name, wpo->used - wp->base_offset, new_size,
2819 wp->id);
2820 if (wpo->used < minimum)
2821 minimum = wpo->used;
2823 if (attached_clients == 0)
2824 off = 0;
2825 minimum -= wp->base_offset;
2826 if (minimum == 0)
2827 goto out;
2829 /* Drain the buffer. */
2830 log_debug("%s: %%%u has %zu minimum (of %zu) bytes used", __func__,
2831 wp->id, minimum, EVBUFFER_LENGTH(evb));
2832 evbuffer_drain(evb, minimum);
2835 * Adjust the base offset. If it would roll over, all the offsets into
2836 * the buffer need to be adjusted.
2838 if (wp->base_offset > SIZE_MAX - minimum) {
2839 log_debug("%s: %%%u base offset has wrapped", __func__, wp->id);
2840 wp->offset.used -= wp->base_offset;
2841 if (wp->pipe_fd != -1)
2842 wp->pipe_offset.used -= wp->base_offset;
2843 TAILQ_FOREACH(c, &clients, entry) {
2844 if (c->session == NULL || (~c->flags & CLIENT_CONTROL))
2845 continue;
2846 wpo = control_pane_offset(c, wp, &flag);
2847 if (wpo != NULL && !flag)
2848 wpo->used -= wp->base_offset;
2850 wp->base_offset = minimum;
2851 } else
2852 wp->base_offset += minimum;
2854 out:
2856 * If there is data remaining, and there are no clients able to consume
2857 * it, do not read any more. This is true when there are attached
2858 * clients, all of which are control clients which are not able to
2859 * accept any more data.
2861 log_debug("%s: pane %%%u is %s", __func__, wp->id, off ? "off" : "on");
2862 if (off)
2863 bufferevent_disable(wp->event, EV_READ);
2864 else
2865 bufferevent_enable(wp->event, EV_READ);
2869 * Update cursor position and mode settings. The scroll region and attributes
2870 * are cleared when idle (waiting for an event) as this is the most likely time
2871 * a user may interrupt tmux, for example with ~^Z in ssh(1). This is a
2872 * compromise between excessive resets and likelihood of an interrupt.
2874 * tty_region/tty_reset/tty_update_mode already take care of not resetting
2875 * things that are already in their default state.
2877 static void
2878 server_client_reset_state(struct client *c)
2880 struct tty *tty = &c->tty;
2881 struct window *w = c->session->curw->window;
2882 struct window_pane *wp = server_client_get_pane(c), *loop;
2883 struct screen *s = NULL;
2884 struct options *oo = c->session->options;
2885 int mode = 0, cursor, flags, n;
2886 u_int cx = 0, cy = 0, ox, oy, sx, sy;
2888 if (c->flags & (CLIENT_CONTROL|CLIENT_SUSPENDED))
2889 return;
2891 /* Disable the block flag. */
2892 flags = (tty->flags & TTY_BLOCK);
2893 tty->flags &= ~TTY_BLOCK;
2895 /* Get mode from overlay if any, else from screen. */
2896 if (c->overlay_draw != NULL) {
2897 if (c->overlay_mode != NULL)
2898 s = c->overlay_mode(c, c->overlay_data, &cx, &cy);
2899 } else if (c->prompt_string == NULL)
2900 s = wp->screen;
2901 else
2902 s = c->status.active;
2903 if (s != NULL)
2904 mode = s->mode;
2905 if (log_get_level() != 0) {
2906 log_debug("%s: client %s mode %s", __func__, c->name,
2907 screen_mode_to_string(mode));
2910 /* Reset region and margin. */
2911 tty_region_off(tty);
2912 tty_margin_off(tty);
2914 /* Move cursor to pane cursor and offset. */
2915 if (c->prompt_string != NULL) {
2916 n = options_get_number(oo, "status-position");
2917 if (n == 0)
2918 cy = 0;
2919 else {
2920 n = status_line_size(c);
2921 if (n == 0)
2922 cy = tty->sy - 1;
2923 else
2924 cy = tty->sy - n;
2926 cx = c->prompt_cursor;
2927 } else if (c->overlay_draw == NULL) {
2928 cursor = 0;
2929 tty_window_offset(tty, &ox, &oy, &sx, &sy);
2930 if (wp->xoff + s->cx >= ox && wp->xoff + s->cx <= ox + sx &&
2931 wp->yoff + s->cy >= oy && wp->yoff + s->cy <= oy + sy) {
2932 cursor = 1;
2934 cx = wp->xoff + s->cx - ox;
2935 cy = wp->yoff + s->cy - oy;
2937 if (status_at_line(c) == 0)
2938 cy += status_line_size(c);
2940 if (!cursor)
2941 mode &= ~MODE_CURSOR;
2943 log_debug("%s: cursor to %u,%u", __func__, cx, cy);
2944 tty_cursor(tty, cx, cy);
2947 * Set mouse mode if requested. To support dragging, always use button
2948 * mode.
2950 if (options_get_number(oo, "mouse")) {
2951 if (c->overlay_draw == NULL) {
2952 mode &= ~ALL_MOUSE_MODES;
2953 TAILQ_FOREACH(loop, &w->panes, entry) {
2954 if (loop->screen->mode & MODE_MOUSE_ALL)
2955 mode |= MODE_MOUSE_ALL;
2958 if (~mode & MODE_MOUSE_ALL)
2959 mode |= MODE_MOUSE_BUTTON;
2962 /* Clear bracketed paste mode if at the prompt. */
2963 if (c->overlay_draw == NULL && c->prompt_string != NULL)
2964 mode &= ~MODE_BRACKETPASTE;
2966 /* Set the terminal mode and reset attributes. */
2967 tty_update_mode(tty, mode, s);
2968 tty_reset(tty);
2970 /* All writing must be done, send a sync end (if it was started). */
2971 tty_sync_end(tty);
2972 tty->flags |= flags;
2975 /* Repeat time callback. */
2976 static void
2977 server_client_repeat_timer(__unused int fd, __unused short events, void *data)
2979 struct client *c = data;
2981 if (c->flags & CLIENT_REPEAT) {
2982 server_client_set_key_table(c, NULL);
2983 c->flags &= ~CLIENT_REPEAT;
2984 server_status_client(c);
2988 /* Double-click callback. */
2989 static void
2990 server_client_click_timer(__unused int fd, __unused short events, void *data)
2992 struct client *c = data;
2993 struct key_event *event;
2995 log_debug("click timer expired");
2997 if (c->flags & CLIENT_TRIPLECLICK) {
2999 * Waiting for a third click that hasn't happened, so this must
3000 * have been a double click.
3002 event = xcalloc(1, sizeof *event);
3003 event->key = KEYC_DOUBLECLICK;
3004 memcpy(&event->m, &c->click_event, sizeof event->m);
3005 if (!server_client_handle_key(c, event)) {
3006 free(event->buf);
3007 free(event);
3010 c->flags &= ~(CLIENT_DOUBLECLICK|CLIENT_TRIPLECLICK);
3013 /* Check if client should be exited. */
3014 static void
3015 server_client_check_exit(struct client *c)
3017 struct client_file *cf;
3018 const char *name = c->exit_session;
3019 char *data;
3020 size_t size, msize;
3022 if (c->flags & (CLIENT_DEAD|CLIENT_EXITED))
3023 return;
3024 if (~c->flags & CLIENT_EXIT)
3025 return;
3027 if (c->flags & CLIENT_CONTROL) {
3028 control_discard(c);
3029 if (!control_all_done(c))
3030 return;
3032 RB_FOREACH(cf, client_files, &c->files) {
3033 if (EVBUFFER_LENGTH(cf->buffer) != 0)
3034 return;
3036 c->flags |= CLIENT_EXITED;
3038 switch (c->exit_type) {
3039 case CLIENT_EXIT_RETURN:
3040 if (c->exit_message != NULL)
3041 msize = strlen(c->exit_message) + 1;
3042 else
3043 msize = 0;
3044 size = (sizeof c->retval) + msize;
3045 data = xmalloc(size);
3046 memcpy(data, &c->retval, sizeof c->retval);
3047 if (c->exit_message != NULL)
3048 memcpy(data + sizeof c->retval, c->exit_message, msize);
3049 proc_send(c->peer, MSG_EXIT, -1, data, size);
3050 free(data);
3051 break;
3052 case CLIENT_EXIT_SHUTDOWN:
3053 proc_send(c->peer, MSG_SHUTDOWN, -1, NULL, 0);
3054 break;
3055 case CLIENT_EXIT_DETACH:
3056 proc_send(c->peer, c->exit_msgtype, -1, name, strlen(name) + 1);
3057 break;
3059 free(c->exit_session);
3060 free(c->exit_message);
3063 /* Redraw timer callback. */
3064 static void
3065 server_client_redraw_timer(__unused int fd, __unused short events,
3066 __unused void *data)
3068 log_debug("redraw timer fired");
3072 * Check if modes need to be updated. Only modes in the current window are
3073 * updated and it is done when the status line is redrawn.
3075 static void
3076 server_client_check_modes(struct client *c)
3078 struct window *w = c->session->curw->window;
3079 struct window_pane *wp;
3080 struct window_mode_entry *wme;
3082 if (c->flags & (CLIENT_CONTROL|CLIENT_SUSPENDED))
3083 return;
3084 if (~c->flags & CLIENT_REDRAWSTATUS)
3085 return;
3086 TAILQ_FOREACH(wp, &w->panes, entry) {
3087 wme = TAILQ_FIRST(&wp->modes);
3088 if (wme != NULL && wme->mode->update != NULL)
3089 wme->mode->update(wme);
3093 /* Check for client redraws. */
3094 static void
3095 server_client_check_redraw(struct client *c)
3097 struct session *s = c->session;
3098 struct tty *tty = &c->tty;
3099 struct window *w = c->session->curw->window;
3100 struct window_pane *wp;
3101 int needed, tty_flags, mode = tty->mode;
3102 uint64_t client_flags = 0;
3103 int redraw_pane, redraw_scrollbar_only;
3104 u_int bit = 0;
3105 struct timeval tv = { .tv_usec = 1000 };
3106 static struct event ev;
3107 size_t left;
3109 if (c->flags & (CLIENT_CONTROL|CLIENT_SUSPENDED))
3110 return;
3111 if (c->flags & CLIENT_ALLREDRAWFLAGS) {
3112 log_debug("%s: redraw%s%s%s%s%s%s", c->name,
3113 (c->flags & CLIENT_REDRAWWINDOW) ? " window" : "",
3114 (c->flags & CLIENT_REDRAWSTATUS) ? " status" : "",
3115 (c->flags & CLIENT_REDRAWBORDERS) ? " borders" : "",
3116 (c->flags & CLIENT_REDRAWOVERLAY) ? " overlay" : "",
3117 (c->flags & CLIENT_REDRAWPANES) ? " panes" : "",
3118 (c->flags & CLIENT_REDRAWSCROLLBARS) ? " scrollbars" : "");
3122 * If there is outstanding data, defer the redraw until it has been
3123 * consumed. We can just add a timer to get out of the event loop and
3124 * end up back here.
3126 needed = 0;
3127 if (c->flags & CLIENT_ALLREDRAWFLAGS)
3128 needed = 1;
3129 else {
3130 TAILQ_FOREACH(wp, &w->panes, entry) {
3131 if (wp->flags & PANE_REDRAW) {
3132 needed = 1;
3133 client_flags |= CLIENT_REDRAWPANES;
3134 break;
3136 if (wp->flags & PANE_REDRAWSCROLLBAR) {
3137 needed = 1;
3138 client_flags |= CLIENT_REDRAWSCROLLBARS;
3139 /* no break - later panes may need redraw */
3143 if (needed && (left = EVBUFFER_LENGTH(tty->out)) != 0) {
3144 log_debug("%s: redraw deferred (%zu left)", c->name, left);
3145 if (!evtimer_initialized(&ev))
3146 evtimer_set(&ev, server_client_redraw_timer, NULL);
3147 if (!evtimer_pending(&ev, NULL)) {
3148 log_debug("redraw timer started");
3149 evtimer_add(&ev, &tv);
3152 if (~c->flags & CLIENT_REDRAWWINDOW) {
3153 TAILQ_FOREACH(wp, &w->panes, entry) {
3154 if (wp->flags & (PANE_REDRAW)) {
3155 log_debug("%s: pane %%%u needs redraw",
3156 c->name, wp->id);
3157 c->redraw_panes |= (1 << bit);
3158 } else if (wp->flags & PANE_REDRAWSCROLLBAR) {
3159 log_debug("%s: pane %%%u scrollbar "
3160 "needs redraw", c->name, wp->id);
3161 c->redraw_scrollbars |= (1 << bit);
3163 if (++bit == 64) {
3165 * If more that 64 panes, give up and
3166 * just redraw the window.
3168 client_flags &= ~(CLIENT_REDRAWPANES|
3169 CLIENT_REDRAWSCROLLBARS);
3170 client_flags |= CLIENT_REDRAWWINDOW;
3171 break;
3174 if (c->redraw_panes != 0)
3175 c->flags |= CLIENT_REDRAWPANES;
3176 if (c->redraw_scrollbars != 0)
3177 c->flags |= CLIENT_REDRAWSCROLLBARS;
3179 c->flags |= client_flags;
3180 return;
3181 } else if (needed)
3182 log_debug("%s: redraw needed", c->name);
3184 tty_flags = tty->flags & (TTY_BLOCK|TTY_FREEZE|TTY_NOCURSOR);
3185 tty->flags = (tty->flags & ~(TTY_BLOCK|TTY_FREEZE))|TTY_NOCURSOR;
3187 if (~c->flags & CLIENT_REDRAWWINDOW) {
3189 * If not redrawing the entire window, check whether each pane
3190 * needs to be redrawn.
3192 TAILQ_FOREACH(wp, &w->panes, entry) {
3193 redraw_pane = 0;
3194 redraw_scrollbar_only = 0;
3195 if (wp->flags & PANE_REDRAW)
3196 redraw_pane = 1;
3197 else if (c->flags & CLIENT_REDRAWPANES) {
3198 if (c->redraw_panes & (1 << bit))
3199 redraw_pane = 1;
3200 } else if (c->flags & CLIENT_REDRAWSCROLLBARS) {
3201 if (c->redraw_scrollbars & (1 << bit))
3202 redraw_scrollbar_only = 1;
3204 bit++;
3205 if (!redraw_pane && !redraw_scrollbar_only)
3206 continue;
3207 if (redraw_scrollbar_only) {
3208 log_debug("%s: redrawing (scrollbar only) pane "
3209 "%%%u", __func__, wp->id);
3210 } else {
3211 log_debug("%s: redrawing pane %%%u", __func__,
3212 wp->id);
3214 screen_redraw_pane(c, wp, redraw_scrollbar_only);
3216 c->redraw_panes = 0;
3217 c->redraw_scrollbars = 0;
3218 c->flags &= ~(CLIENT_REDRAWPANES|CLIENT_REDRAWSCROLLBARS);
3221 if (c->flags & CLIENT_ALLREDRAWFLAGS) {
3222 if (options_get_number(s->options, "set-titles")) {
3223 server_client_set_title(c);
3224 server_client_set_path(c);
3226 screen_redraw_screen(c);
3229 tty->flags = (tty->flags & ~TTY_NOCURSOR)|(tty_flags & TTY_NOCURSOR);
3230 tty_update_mode(tty, mode, NULL);
3231 tty->flags = (tty->flags & ~(TTY_BLOCK|TTY_FREEZE|TTY_NOCURSOR))|
3232 tty_flags;
3234 c->flags &= ~(CLIENT_ALLREDRAWFLAGS|CLIENT_STATUSFORCE);
3236 if (needed) {
3238 * We would have deferred the redraw unless the output buffer
3239 * was empty, so we can record how many bytes the redraw
3240 * generated.
3242 c->redraw = EVBUFFER_LENGTH(tty->out);
3243 log_debug("%s: redraw added %zu bytes", c->name, c->redraw);
3247 /* Set client title. */
3248 static void
3249 server_client_set_title(struct client *c)
3251 struct session *s = c->session;
3252 const char *template;
3253 char *title;
3254 struct format_tree *ft;
3256 template = options_get_string(s->options, "set-titles-string");
3258 ft = format_create(c, NULL, FORMAT_NONE, 0);
3259 format_defaults(ft, c, NULL, NULL, NULL);
3261 title = format_expand_time(ft, template);
3262 if (c->title == NULL || strcmp(title, c->title) != 0) {
3263 free(c->title);
3264 c->title = xstrdup(title);
3265 tty_set_title(&c->tty, c->title);
3267 free(title);
3269 format_free(ft);
3272 /* Set client path. */
3273 static void
3274 server_client_set_path(struct client *c)
3276 struct session *s = c->session;
3277 const char *path;
3279 if (s->curw == NULL)
3280 return;
3281 if (s->curw->window->active->base.path == NULL)
3282 path = "";
3283 else
3284 path = s->curw->window->active->base.path;
3285 if (c->path == NULL || strcmp(path, c->path) != 0) {
3286 free(c->path);
3287 c->path = xstrdup(path);
3288 tty_set_path(&c->tty, c->path);
3292 /* Dispatch message from client. */
3293 static void
3294 server_client_dispatch(struct imsg *imsg, void *arg)
3296 struct client *c = arg;
3297 ssize_t datalen;
3298 struct session *s;
3300 if (c->flags & CLIENT_DEAD)
3301 return;
3303 if (imsg == NULL) {
3304 server_client_lost(c);
3305 return;
3308 datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
3310 switch (imsg->hdr.type) {
3311 case MSG_IDENTIFY_CLIENTPID:
3312 case MSG_IDENTIFY_CWD:
3313 case MSG_IDENTIFY_ENVIRON:
3314 case MSG_IDENTIFY_FEATURES:
3315 case MSG_IDENTIFY_FLAGS:
3316 case MSG_IDENTIFY_LONGFLAGS:
3317 case MSG_IDENTIFY_STDIN:
3318 case MSG_IDENTIFY_STDOUT:
3319 case MSG_IDENTIFY_TERM:
3320 case MSG_IDENTIFY_TERMINFO:
3321 case MSG_IDENTIFY_TTYNAME:
3322 case MSG_IDENTIFY_DONE:
3323 server_client_dispatch_identify(c, imsg);
3324 break;
3325 case MSG_COMMAND:
3326 server_client_dispatch_command(c, imsg);
3327 break;
3328 case MSG_RESIZE:
3329 if (datalen != 0)
3330 fatalx("bad MSG_RESIZE size");
3332 if (c->flags & CLIENT_CONTROL)
3333 break;
3334 server_client_update_latest(c);
3335 tty_resize(&c->tty);
3336 tty_repeat_requests(&c->tty);
3337 recalculate_sizes();
3338 if (c->overlay_resize == NULL)
3339 server_client_clear_overlay(c);
3340 else
3341 c->overlay_resize(c, c->overlay_data);
3342 server_redraw_client(c);
3343 if (c->session != NULL)
3344 notify_client("client-resized", c);
3345 break;
3346 case MSG_EXITING:
3347 if (datalen != 0)
3348 fatalx("bad MSG_EXITING size");
3349 server_client_set_session(c, NULL);
3350 recalculate_sizes();
3351 tty_close(&c->tty);
3352 proc_send(c->peer, MSG_EXITED, -1, NULL, 0);
3353 break;
3354 case MSG_WAKEUP:
3355 case MSG_UNLOCK:
3356 if (datalen != 0)
3357 fatalx("bad MSG_WAKEUP size");
3359 if (!(c->flags & CLIENT_SUSPENDED))
3360 break;
3361 c->flags &= ~CLIENT_SUSPENDED;
3363 if (c->fd == -1 || c->session == NULL) /* exited already */
3364 break;
3365 s = c->session;
3367 if (gettimeofday(&c->activity_time, NULL) != 0)
3368 fatal("gettimeofday failed");
3370 tty_start_tty(&c->tty);
3371 server_redraw_client(c);
3372 recalculate_sizes();
3374 if (s != NULL)
3375 session_update_activity(s, &c->activity_time);
3376 break;
3377 case MSG_SHELL:
3378 if (datalen != 0)
3379 fatalx("bad MSG_SHELL size");
3381 server_client_dispatch_shell(c);
3382 break;
3383 case MSG_WRITE_READY:
3384 file_write_ready(&c->files, imsg);
3385 break;
3386 case MSG_READ:
3387 file_read_data(&c->files, imsg);
3388 break;
3389 case MSG_READ_DONE:
3390 file_read_done(&c->files, imsg);
3391 break;
3395 /* Callback when command is not allowed. */
3396 static enum cmd_retval
3397 server_client_read_only(struct cmdq_item *item, __unused void *data)
3399 cmdq_error(item, "client is read-only");
3400 return (CMD_RETURN_ERROR);
3403 /* Callback when command is done. */
3404 static enum cmd_retval
3405 server_client_command_done(struct cmdq_item *item, __unused void *data)
3407 struct client *c = cmdq_get_client(item);
3409 if (~c->flags & CLIENT_ATTACHED)
3410 c->flags |= CLIENT_EXIT;
3411 else if (~c->flags & CLIENT_EXIT) {
3412 if (c->flags & CLIENT_CONTROL)
3413 control_ready(c);
3414 tty_send_requests(&c->tty);
3416 return (CMD_RETURN_NORMAL);
3419 /* Handle command message. */
3420 static void
3421 server_client_dispatch_command(struct client *c, struct imsg *imsg)
3423 struct msg_command data;
3424 char *buf;
3425 size_t len;
3426 int argc;
3427 char **argv, *cause;
3428 struct cmd_parse_result *pr;
3429 struct args_value *values;
3430 struct cmdq_item *new_item;
3432 if (c->flags & CLIENT_EXIT)
3433 return;
3435 if (imsg->hdr.len - IMSG_HEADER_SIZE < sizeof data)
3436 fatalx("bad MSG_COMMAND size");
3437 memcpy(&data, imsg->data, sizeof data);
3439 buf = (char *)imsg->data + sizeof data;
3440 len = imsg->hdr.len - IMSG_HEADER_SIZE - sizeof data;
3441 if (len > 0 && buf[len - 1] != '\0')
3442 fatalx("bad MSG_COMMAND string");
3444 argc = data.argc;
3445 if (cmd_unpack_argv(buf, len, argc, &argv) != 0) {
3446 cause = xstrdup("command too long");
3447 goto error;
3450 if (argc == 0) {
3451 argc = 1;
3452 argv = xcalloc(1, sizeof *argv);
3453 *argv = xstrdup("new-session");
3456 values = args_from_vector(argc, argv);
3457 pr = cmd_parse_from_arguments(values, argc, NULL);
3458 switch (pr->status) {
3459 case CMD_PARSE_ERROR:
3460 cause = pr->error;
3461 goto error;
3462 case CMD_PARSE_SUCCESS:
3463 break;
3465 args_free_values(values, argc);
3466 free(values);
3467 cmd_free_argv(argc, argv);
3469 if ((c->flags & CLIENT_READONLY) &&
3470 !cmd_list_all_have(pr->cmdlist, CMD_READONLY))
3471 new_item = cmdq_get_callback(server_client_read_only, NULL);
3472 else
3473 new_item = cmdq_get_command(pr->cmdlist, NULL);
3474 cmdq_append(c, new_item);
3475 cmdq_append(c, cmdq_get_callback(server_client_command_done, NULL));
3477 cmd_list_free(pr->cmdlist);
3478 return;
3480 error:
3481 cmd_free_argv(argc, argv);
3483 cmdq_append(c, cmdq_get_error(cause));
3484 free(cause);
3486 c->flags |= CLIENT_EXIT;
3489 /* Handle identify message. */
3490 static void
3491 server_client_dispatch_identify(struct client *c, struct imsg *imsg)
3493 const char *data, *home;
3494 size_t datalen;
3495 int flags, feat;
3496 uint64_t longflags;
3497 char *name;
3499 if (c->flags & CLIENT_IDENTIFIED)
3500 fatalx("out-of-order identify message");
3502 data = imsg->data;
3503 datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
3505 switch (imsg->hdr.type) {
3506 case MSG_IDENTIFY_FEATURES:
3507 if (datalen != sizeof feat)
3508 fatalx("bad MSG_IDENTIFY_FEATURES size");
3509 memcpy(&feat, data, sizeof feat);
3510 c->term_features |= feat;
3511 log_debug("client %p IDENTIFY_FEATURES %s", c,
3512 tty_get_features(feat));
3513 break;
3514 case MSG_IDENTIFY_FLAGS:
3515 if (datalen != sizeof flags)
3516 fatalx("bad MSG_IDENTIFY_FLAGS size");
3517 memcpy(&flags, data, sizeof flags);
3518 c->flags |= flags;
3519 log_debug("client %p IDENTIFY_FLAGS %#x", c, flags);
3520 break;
3521 case MSG_IDENTIFY_LONGFLAGS:
3522 if (datalen != sizeof longflags)
3523 fatalx("bad MSG_IDENTIFY_LONGFLAGS size");
3524 memcpy(&longflags, data, sizeof longflags);
3525 c->flags |= longflags;
3526 log_debug("client %p IDENTIFY_LONGFLAGS %#llx", c,
3527 (unsigned long long)longflags);
3528 break;
3529 case MSG_IDENTIFY_TERM:
3530 if (datalen == 0 || data[datalen - 1] != '\0')
3531 fatalx("bad MSG_IDENTIFY_TERM string");
3532 if (*data == '\0')
3533 c->term_name = xstrdup("unknown");
3534 else
3535 c->term_name = xstrdup(data);
3536 log_debug("client %p IDENTIFY_TERM %s", c, data);
3537 break;
3538 case MSG_IDENTIFY_TERMINFO:
3539 if (datalen == 0 || data[datalen - 1] != '\0')
3540 fatalx("bad MSG_IDENTIFY_TERMINFO string");
3541 c->term_caps = xreallocarray(c->term_caps, c->term_ncaps + 1,
3542 sizeof *c->term_caps);
3543 c->term_caps[c->term_ncaps++] = xstrdup(data);
3544 log_debug("client %p IDENTIFY_TERMINFO %s", c, data);
3545 break;
3546 case MSG_IDENTIFY_TTYNAME:
3547 if (datalen == 0 || data[datalen - 1] != '\0')
3548 fatalx("bad MSG_IDENTIFY_TTYNAME string");
3549 c->ttyname = xstrdup(data);
3550 log_debug("client %p IDENTIFY_TTYNAME %s", c, data);
3551 break;
3552 case MSG_IDENTIFY_CWD:
3553 if (datalen == 0 || data[datalen - 1] != '\0')
3554 fatalx("bad MSG_IDENTIFY_CWD string");
3555 if (access(data, X_OK) == 0)
3556 c->cwd = xstrdup(data);
3557 else if ((home = find_home()) != NULL)
3558 c->cwd = xstrdup(home);
3559 else
3560 c->cwd = xstrdup("/");
3561 log_debug("client %p IDENTIFY_CWD %s", c, data);
3562 break;
3563 case MSG_IDENTIFY_STDIN:
3564 if (datalen != 0)
3565 fatalx("bad MSG_IDENTIFY_STDIN size");
3566 c->fd = imsg_get_fd(imsg);
3567 log_debug("client %p IDENTIFY_STDIN %d", c, c->fd);
3568 break;
3569 case MSG_IDENTIFY_STDOUT:
3570 if (datalen != 0)
3571 fatalx("bad MSG_IDENTIFY_STDOUT size");
3572 c->out_fd = imsg_get_fd(imsg);
3573 log_debug("client %p IDENTIFY_STDOUT %d", c, c->out_fd);
3574 break;
3575 case MSG_IDENTIFY_ENVIRON:
3576 if (datalen == 0 || data[datalen - 1] != '\0')
3577 fatalx("bad MSG_IDENTIFY_ENVIRON string");
3578 if (strchr(data, '=') != NULL)
3579 environ_put(c->environ, data, 0);
3580 log_debug("client %p IDENTIFY_ENVIRON %s", c, data);
3581 break;
3582 case MSG_IDENTIFY_CLIENTPID:
3583 if (datalen != sizeof c->pid)
3584 fatalx("bad MSG_IDENTIFY_CLIENTPID size");
3585 memcpy(&c->pid, data, sizeof c->pid);
3586 log_debug("client %p IDENTIFY_CLIENTPID %ld", c, (long)c->pid);
3587 break;
3588 default:
3589 break;
3592 if (imsg->hdr.type != MSG_IDENTIFY_DONE)
3593 return;
3594 c->flags |= CLIENT_IDENTIFIED;
3596 if (*c->ttyname != '\0')
3597 name = xstrdup(c->ttyname);
3598 else
3599 xasprintf(&name, "client-%ld", (long)c->pid);
3600 c->name = name;
3601 log_debug("client %p name is %s", c, c->name);
3603 #ifdef __CYGWIN__
3604 c->fd = open(c->ttyname, O_RDWR|O_NOCTTY);
3605 c->out_fd = dup(c->fd);
3606 #endif
3608 if (c->flags & CLIENT_CONTROL)
3609 control_start(c);
3610 else if (c->fd != -1) {
3611 if (tty_init(&c->tty, c) != 0) {
3612 close(c->fd);
3613 c->fd = -1;
3614 } else {
3615 tty_resize(&c->tty);
3616 c->flags |= CLIENT_TERMINAL;
3618 close(c->out_fd);
3619 c->out_fd = -1;
3623 * If this is the first client, load configuration files. Any later
3624 * clients are allowed to continue with their command even if the
3625 * config has not been loaded - they might have been run from inside it
3627 if ((~c->flags & CLIENT_EXIT) &&
3628 !cfg_finished &&
3629 c == TAILQ_FIRST(&clients))
3630 start_cfg();
3633 /* Handle shell message. */
3634 static void
3635 server_client_dispatch_shell(struct client *c)
3637 const char *shell;
3639 shell = options_get_string(global_s_options, "default-shell");
3640 if (!checkshell(shell))
3641 shell = _PATH_BSHELL;
3642 proc_send(c->peer, MSG_SHELL, -1, shell, strlen(shell) + 1);
3644 proc_kill_peer(c->peer);
3647 /* Get client working directory. */
3648 const char *
3649 server_client_get_cwd(struct client *c, struct session *s)
3651 const char *home;
3653 if (!cfg_finished && cfg_client != NULL)
3654 return (cfg_client->cwd);
3655 if (c != NULL && c->session == NULL && c->cwd != NULL)
3656 return (c->cwd);
3657 if (s != NULL && s->cwd != NULL)
3658 return (s->cwd);
3659 if (c != NULL && (s = c->session) != NULL && s->cwd != NULL)
3660 return (s->cwd);
3661 if ((home = find_home()) != NULL)
3662 return (home);
3663 return ("/");
3666 /* Get control client flags. */
3667 static uint64_t
3668 server_client_control_flags(struct client *c, const char *next)
3670 if (strcmp(next, "pause-after") == 0) {
3671 c->pause_age = 0;
3672 return (CLIENT_CONTROL_PAUSEAFTER);
3674 if (sscanf(next, "pause-after=%u", &c->pause_age) == 1) {
3675 c->pause_age *= 1000;
3676 return (CLIENT_CONTROL_PAUSEAFTER);
3678 if (strcmp(next, "no-output") == 0)
3679 return (CLIENT_CONTROL_NOOUTPUT);
3680 if (strcmp(next, "wait-exit") == 0)
3681 return (CLIENT_CONTROL_WAITEXIT);
3682 return (0);
3685 /* Set client flags. */
3686 void
3687 server_client_set_flags(struct client *c, const char *flags)
3689 char *s, *copy, *next;
3690 uint64_t flag;
3691 int not;
3693 s = copy = xstrdup(flags);
3694 while ((next = strsep(&s, ",")) != NULL) {
3695 not = (*next == '!');
3696 if (not)
3697 next++;
3699 if (c->flags & CLIENT_CONTROL)
3700 flag = server_client_control_flags(c, next);
3701 else
3702 flag = 0;
3703 if (strcmp(next, "read-only") == 0)
3704 flag = CLIENT_READONLY;
3705 else if (strcmp(next, "ignore-size") == 0)
3706 flag = CLIENT_IGNORESIZE;
3707 else if (strcmp(next, "active-pane") == 0)
3708 flag = CLIENT_ACTIVEPANE;
3709 else if (strcmp(next, "no-detach-on-destroy") == 0)
3710 flag = CLIENT_NO_DETACH_ON_DESTROY;
3711 if (flag == 0)
3712 continue;
3714 log_debug("client %s set flag %s", c->name, next);
3715 if (not) {
3716 if (c->flags & CLIENT_READONLY)
3717 flag &= ~CLIENT_READONLY;
3718 c->flags &= ~flag;
3719 } else
3720 c->flags |= flag;
3721 if (flag == CLIENT_CONTROL_NOOUTPUT)
3722 control_reset_offsets(c);
3724 free(copy);
3725 proc_send(c->peer, MSG_FLAGS, -1, &c->flags, sizeof c->flags);
3728 /* Get client flags. This is only flags useful to show to users. */
3729 const char *
3730 server_client_get_flags(struct client *c)
3732 static char s[256];
3733 char tmp[32];
3735 *s = '\0';
3736 if (c->flags & CLIENT_ATTACHED)
3737 strlcat(s, "attached,", sizeof s);
3738 if (c->flags & CLIENT_FOCUSED)
3739 strlcat(s, "focused,", sizeof s);
3740 if (c->flags & CLIENT_CONTROL)
3741 strlcat(s, "control-mode,", sizeof s);
3742 if (c->flags & CLIENT_IGNORESIZE)
3743 strlcat(s, "ignore-size,", sizeof s);
3744 if (c->flags & CLIENT_NO_DETACH_ON_DESTROY)
3745 strlcat(s, "no-detach-on-destroy,", sizeof s);
3746 if (c->flags & CLIENT_CONTROL_NOOUTPUT)
3747 strlcat(s, "no-output,", sizeof s);
3748 if (c->flags & CLIENT_CONTROL_WAITEXIT)
3749 strlcat(s, "wait-exit,", sizeof s);
3750 if (c->flags & CLIENT_CONTROL_PAUSEAFTER) {
3751 xsnprintf(tmp, sizeof tmp, "pause-after=%u,",
3752 c->pause_age / 1000);
3753 strlcat(s, tmp, sizeof s);
3755 if (c->flags & CLIENT_READONLY)
3756 strlcat(s, "read-only,", sizeof s);
3757 if (c->flags & CLIENT_ACTIVEPANE)
3758 strlcat(s, "active-pane,", sizeof s);
3759 if (c->flags & CLIENT_SUSPENDED)
3760 strlcat(s, "suspended,", sizeof s);
3761 if (c->flags & CLIENT_UTF8)
3762 strlcat(s, "UTF-8,", sizeof s);
3763 if (*s != '\0')
3764 s[strlen(s) - 1] = '\0';
3765 return (s);
3768 /* Get client window. */
3769 struct client_window *
3770 server_client_get_client_window(struct client *c, u_int id)
3772 struct client_window cw = { .window = id };
3774 return (RB_FIND(client_windows, &c->windows, &cw));
3777 /* Add client window. */
3778 struct client_window *
3779 server_client_add_client_window(struct client *c, u_int id)
3781 struct client_window *cw;
3783 cw = server_client_get_client_window(c, id);
3784 if (cw == NULL) {
3785 cw = xcalloc(1, sizeof *cw);
3786 cw->window = id;
3787 RB_INSERT(client_windows, &c->windows, cw);
3789 return (cw);
3792 /* Get client active pane. */
3793 struct window_pane *
3794 server_client_get_pane(struct client *c)
3796 struct session *s = c->session;
3797 struct client_window *cw;
3799 if (s == NULL)
3800 return (NULL);
3802 if (~c->flags & CLIENT_ACTIVEPANE)
3803 return (s->curw->window->active);
3804 cw = server_client_get_client_window(c, s->curw->window->id);
3805 if (cw == NULL)
3806 return (s->curw->window->active);
3807 return (cw->pane);
3810 /* Set client active pane. */
3811 void
3812 server_client_set_pane(struct client *c, struct window_pane *wp)
3814 struct session *s = c->session;
3815 struct client_window *cw;
3817 if (s == NULL)
3818 return;
3820 cw = server_client_add_client_window(c, s->curw->window->id);
3821 cw->pane = wp;
3822 log_debug("%s pane now %%%u", c->name, wp->id);
3825 /* Remove pane from client lists. */
3826 void
3827 server_client_remove_pane(struct window_pane *wp)
3829 struct client *c;
3830 struct window *w = wp->window;
3831 struct client_window *cw;
3833 TAILQ_FOREACH(c, &clients, entry) {
3834 cw = server_client_get_client_window(c, w->id);
3835 if (cw != NULL && cw->pane == wp) {
3836 RB_REMOVE(client_windows, &c->windows, cw);
3837 free(cw);
3842 /* Print to a client. */
3843 void
3844 server_client_print(struct client *c, int parse, struct evbuffer *evb)
3846 void *data = EVBUFFER_DATA(evb);
3847 size_t size = EVBUFFER_LENGTH(evb);
3848 struct window_pane *wp;
3849 struct window_mode_entry *wme;
3850 char *sanitized, *msg, *line, empty = '\0';
3852 if (!parse) {
3853 utf8_stravisx(&msg, data, size,
3854 VIS_OCTAL|VIS_CSTYLE|VIS_NOSLASH);
3855 } else {
3856 if (size == 0)
3857 msg = &empty;
3858 else {
3859 msg = EVBUFFER_DATA(evb);
3860 if (msg[size - 1] != '\0')
3861 evbuffer_add(evb, "", 1);
3864 log_debug("%s: %s", __func__, msg);
3866 if (c == NULL)
3867 goto out;
3869 if (c->session == NULL || (c->flags & CLIENT_CONTROL)) {
3870 if (~c->flags & CLIENT_UTF8) {
3871 sanitized = utf8_sanitize(msg);
3872 if (c->flags & CLIENT_CONTROL)
3873 control_write(c, "%s", sanitized);
3874 else
3875 file_print(c, "%s\n", sanitized);
3876 free(sanitized);
3877 } else {
3878 if (c->flags & CLIENT_CONTROL)
3879 control_write(c, "%s", msg);
3880 else
3881 file_print(c, "%s\n", msg);
3883 goto out;
3886 wp = server_client_get_pane(c);
3887 wme = TAILQ_FIRST(&wp->modes);
3888 if (wme == NULL || wme->mode != &window_view_mode)
3889 window_pane_set_mode(wp, NULL, &window_view_mode, NULL, NULL);
3890 if (parse) {
3891 do {
3892 line = evbuffer_readln(evb, NULL, EVBUFFER_EOL_LF);
3893 if (line != NULL) {
3894 window_copy_add(wp, 1, "%s", line);
3895 free(line);
3897 } while (line != NULL);
3899 size = EVBUFFER_LENGTH(evb);
3900 if (size != 0) {
3901 line = EVBUFFER_DATA(evb);
3902 window_copy_add(wp, 1, "%.*s", (int)size, line);
3904 } else
3905 window_copy_add(wp, 0, "%s", msg);
3907 out:
3908 if (!parse)
3909 free(msg);