diff --git a/key-bindings.c b/key-bindings.c index 384a9f8f..f6b17c3c 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -263,6 +263,8 @@ key_bindings_init(void) "bind -Tcopy-mode MouseDragEnd1Pane send -X copy-selection-and-cancel", "bind -Tcopy-mode WheelUpPane send -N5 -X scroll-up", "bind -Tcopy-mode WheelDownPane send -N5 -X scroll-down", + "bind -Tcopy-mode DoubleClick1Pane send -X select-word", + "bind -Tcopy-mode TripleClick1Pane send -X select-line", "bind -Tcopy-mode NPage send -X page-down", "bind -Tcopy-mode PPage send -X page-up", "bind -Tcopy-mode Up send -X cursor-up", @@ -359,6 +361,8 @@ key_bindings_init(void) "bind -Tcopy-mode-vi MouseDragEnd1Pane send -X copy-selection-and-cancel", "bind -Tcopy-mode-vi WheelUpPane send -N5 -X scroll-up", "bind -Tcopy-mode-vi WheelDownPane send -N5 -X scroll-down", + "bind -Tcopy-mode-vi DoubleClick1Pane send -X select-word", + "bind -Tcopy-mode-vi TripleClick1Pane send -X select-line", "bind -Tcopy-mode-vi BSpace send -X cursor-left", "bind -Tcopy-mode-vi NPage send -X page-down", "bind -Tcopy-mode-vi PPage send -X page-up", diff --git a/key-string.c b/key-string.c index 00ebb386..d513ec6f 100644 --- a/key-string.c +++ b/key-string.c @@ -98,6 +98,12 @@ static const struct { KEYC_MOUSE_STRING(MOUSEDRAGEND3, MouseDragEnd3), KEYC_MOUSE_STRING(WHEELUP, WheelUp), KEYC_MOUSE_STRING(WHEELDOWN, WheelDown), + KEYC_MOUSE_STRING(DOUBLECLICK1, DoubleClick1), + KEYC_MOUSE_STRING(DOUBLECLICK2, DoubleClick2), + KEYC_MOUSE_STRING(DOUBLECLICK3, DoubleClick3), + KEYC_MOUSE_STRING(TRIPLECLICK1, TripleClick1), + KEYC_MOUSE_STRING(TRIPLECLICK2, TripleClick2), + KEYC_MOUSE_STRING(TRIPLECLICK3, TripleClick3), }; /* Find key string in table. */ diff --git a/server-client.c b/server-client.c index 70fdd51a..d5c0cf23 100644 --- a/server-client.c +++ b/server-client.c @@ -37,6 +37,7 @@ static void server_client_check_focus(struct window_pane *); static void server_client_check_resize(struct window_pane *); static key_code server_client_check_mouse(struct client *); static void server_client_repeat_timer(int, short, void *); +static void server_client_click_timer(int, short, void *); static void server_client_check_exit(struct client *); static void server_client_check_redraw(struct client *); static void server_client_set_title(struct client *); @@ -155,6 +156,7 @@ server_client_create(int fd) c->keytable->references++; evtimer_set(&c->repeat_timer, server_client_repeat_timer, c); + evtimer_set(&c->click_timer, server_client_click_timer, c); TAILQ_INSERT_TAIL(&clients, c, entry); log_debug("new client %p", c); @@ -223,6 +225,7 @@ server_client_lost(struct client *c) free((void *)c->cwd); evtimer_del(&c->repeat_timer); + evtimer_del(&c->click_timer); key_bindings_unref_table(c->keytable); @@ -299,14 +302,16 @@ server_client_detach(struct client *c, enum msgtype msgtype) static key_code server_client_check_mouse(struct client *c) { - struct session *s = c->session; - struct mouse_event *m = &c->tty.mouse; - struct window *w; - struct window_pane *wp; - enum { NOTYPE, DOWN, UP, DRAG, WHEEL } type = NOTYPE; - enum { NOWHERE, PANE, STATUS, BORDER } where = NOWHERE; - u_int x, y, b; - key_code key; + struct session *s = c->session; + struct mouse_event *m = &c->tty.mouse; + struct window *w; + struct window_pane *wp; + u_int x, y, b; + int flag; + key_code key; + struct timeval tv; + enum { NOTYPE, DOWN, UP, DRAG, WHEEL, DOUBLE, TRIPLE } type = NOTYPE; + enum { NOWHERE, PANE, STATUS, BORDER } where = NOWHERE; log_debug("mouse %02x at %u,%u (last %u,%u) (%d)", m->b, m->x, m->y, m->lx, m->ly, c->tty.mouse_drag_flag); @@ -330,10 +335,45 @@ server_client_check_mouse(struct client *c) x = m->x, y = m->y, b = m->lb; log_debug("up at %u,%u", x, y); } else { + if (c->flags & CLIENT_DOUBLECLICK) { + evtimer_del(&c->click_timer); + c->flags &= ~CLIENT_DOUBLECLICK; + if (m->b == c->click_button) { + type = DOUBLE; + x = m->x, y = m->y, b = m->b; + log_debug("double-click at %u,%u", x, y); + flag = CLIENT_TRIPLECLICK; + goto add_timer; + } + } else if (c->flags & CLIENT_TRIPLECLICK) { + evtimer_del(&c->click_timer); + c->flags &= ~CLIENT_TRIPLECLICK; + if (m->b == c->click_button) { + type = TRIPLE; + x = m->x, y = m->y, b = m->b; + log_debug("triple-click at %u,%u", x, y); + goto have_event; + } + } + type = DOWN; x = m->x, y = m->y, b = m->b; log_debug("down at %u,%u", x, y); + flag = CLIENT_DOUBLECLICK; + + add_timer: + if (KEYC_CLICK_TIMEOUT != 0) { + c->flags |= flag; + c->click_button = m->b; + + tv.tv_sec = KEYC_CLICK_TIMEOUT / 1000; + tv.tv_usec = (KEYC_CLICK_TIMEOUT % 1000) * 1000L; + evtimer_del(&c->click_timer); + evtimer_add(&c->click_timer, &tv); + } } + +have_event: if (type == NOTYPE) return (KEYC_UNKNOWN); @@ -546,6 +586,62 @@ server_client_check_mouse(struct client *c) break; } break; + case DOUBLE: + switch (MOUSE_BUTTONS(b)) { + case 0: + if (where == PANE) + key = KEYC_DOUBLECLICK1_PANE; + if (where == STATUS) + key = KEYC_DOUBLECLICK1_STATUS; + if (where == BORDER) + key = KEYC_DOUBLECLICK1_BORDER; + break; + case 1: + if (where == PANE) + key = KEYC_DOUBLECLICK2_PANE; + if (where == STATUS) + key = KEYC_DOUBLECLICK2_STATUS; + if (where == BORDER) + key = KEYC_DOUBLECLICK2_BORDER; + break; + case 2: + if (where == PANE) + key = KEYC_DOUBLECLICK3_PANE; + if (where == STATUS) + key = KEYC_DOUBLECLICK3_STATUS; + if (where == BORDER) + key = KEYC_DOUBLECLICK3_BORDER; + break; + } + break; + case TRIPLE: + switch (MOUSE_BUTTONS(b)) { + case 0: + if (where == PANE) + key = KEYC_TRIPLECLICK1_PANE; + if (where == STATUS) + key = KEYC_TRIPLECLICK1_STATUS; + if (where == BORDER) + key = KEYC_TRIPLECLICK1_BORDER; + break; + case 1: + if (where == PANE) + key = KEYC_TRIPLECLICK2_PANE; + if (where == STATUS) + key = KEYC_TRIPLECLICK2_STATUS; + if (where == BORDER) + key = KEYC_TRIPLECLICK2_BORDER; + break; + case 2: + if (where == PANE) + key = KEYC_TRIPLECLICK3_PANE; + if (where == STATUS) + key = KEYC_TRIPLECLICK3_STATUS; + if (where == BORDER) + key = KEYC_TRIPLECLICK3_BORDER; + break; + } + break; } if (key == KEYC_UNKNOWN) return (KEYC_UNKNOWN); @@ -945,6 +1041,15 @@ server_client_repeat_timer(__unused int fd, __unused short events, void *data) } } +/* Double-click callback. */ +static void +server_client_click_timer(__unused int fd, __unused short events, void *data) +{ + struct client *c = data; + + c->flags &= ~(CLIENT_DOUBLECLICK|CLIENT_TRIPLECLICK); +} + /* Check if client should be exited. */ static void server_client_check_exit(struct client *c) diff --git a/tmux.1 b/tmux.1 index ec6b02cd..f375a215 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3334,10 +3334,12 @@ for a pane border or for the status line). The following mouse events are available: .Bl -column "MouseDown1" "MouseDrag1" "WheelDown" -offset indent +.It Li "WheelUp" Ta "WheelDown" Ta "" .It Li "MouseDown1" Ta "MouseUp1" Ta "MouseDrag1" Ta "MouseDragEnd1" .It Li "MouseDown2" Ta "MouseUp2" Ta "MouseDrag2" Ta "MouseDragEnd2" .It Li "MouseDown3" Ta "MouseUp3" Ta "MouseDrag3" Ta "MouseDragEnd3" -.It Li "WheelUp" Ta "WheelDown" Ta "" Ta "" +.It Li "DoubleClick1" Ta "DoubleClick2" Ta "DoubleClick3" Ta "WheelUp" +.It Li "TripleClick1" Ta "TripleClick2" Ta "TripleClick3" Ta "WheelDown" .El .Pp Each should be suffixed with a location, for example diff --git a/tmux.h b/tmux.h index 2a020c33..f18276c5 100644 --- a/tmux.h +++ b/tmux.h @@ -105,6 +105,9 @@ struct tmuxproc; #define KEYC_IS_MOUSE(key) (((key) & KEYC_MASK_KEY) >= KEYC_MOUSE && \ ((key) & KEYC_MASK_KEY) < KEYC_BSPACE) +/* Multiple click timeout. */ +#define KEYC_CLICK_TIMEOUT 300 + /* Mouse key codes. */ #define KEYC_MOUSE_KEY(name) \ KEYC_ ## name ## _PANE, \ @@ -143,6 +146,12 @@ enum { KEYC_MOUSE_KEY(MOUSEDRAGEND3), KEYC_MOUSE_KEY(WHEELUP), KEYC_MOUSE_KEY(WHEELDOWN), + KEYC_MOUSE_KEY(DOUBLECLICK1), + KEYC_MOUSE_KEY(DOUBLECLICK2), + KEYC_MOUSE_KEY(DOUBLECLICK3), + KEYC_MOUSE_KEY(TRIPLECLICK1), + KEYC_MOUSE_KEY(TRIPLECLICK2), + KEYC_MOUSE_KEY(TRIPLECLICK3), /* Backspace key. */ KEYC_BSPACE, @@ -1216,6 +1225,9 @@ struct client { struct event repeat_timer; + struct event click_timer; + u_int click_button; + struct event status_timer; struct screen status; @@ -1239,6 +1251,8 @@ struct client { #define CLIENT_256COLOURS 0x20000 #define CLIENT_IDENTIFIED 0x40000 #define CLIENT_STATUSFORCE 0x80000 +#define CLIENT_DOUBLECLICK 0x100000 +#define CLIENT_TRIPLECLICK 0x200000 int flags; struct key_table *keytable; diff --git a/window-copy.c b/window-copy.c index d5140fc7..5c1f5f81 100644 --- a/window-copy.c +++ b/window-copy.c @@ -91,6 +91,7 @@ void window_copy_cursor_previous_word(struct window_pane *, const char *); void window_copy_scroll_up(struct window_pane *, u_int); void window_copy_scroll_down(struct window_pane *, u_int); void window_copy_rectangle_toggle(struct window_pane *); +void window_copy_move_mouse(struct mouse_event *); void window_copy_drag_update(struct client *, struct mouse_event *); void window_copy_drag_release(struct client *, struct mouse_event *); @@ -479,6 +480,9 @@ window_copy_command(struct window_pane *wp, struct client *c, struct session *s, return; command = args->argv[0]; + if (m != NULL && m->valid) + window_copy_move_mouse(m); + if (args->argc == 1) { if (strcmp(command, "append-selection") == 0) { if (s != NULL) @@ -731,9 +735,17 @@ window_copy_command(struct window_pane *wp, struct client *c, struct session *s, window_copy_cursor_end_of_line(wp); window_copy_redraw_screen(wp); } - if (strcmp(command, "start-of-line") == 0) { - window_copy_cursor_start_of_line(wp); + if (strcmp(command, "select-word") == 0) { + sn->sel.lineflag = LINE_SEL_LEFT_RIGHT; + data->rectflag = 0; + ws = options_get_string(s->options, "word-separators"); + window_copy_cursor_previous_word(wp, ws); + window_copy_start_selection(wp); + window_copy_cursor_next_word_end(wp, ws); + window_copy_redraw_screen(wp); } + if (strcmp(command, "start-of-line") == 0) + window_copy_cursor_start_of_line(wp); if (strcmp(command, "top-line") == 0) { data->cx = 0; data->cy = 0; @@ -2105,6 +2117,22 @@ window_copy_rectangle_toggle(struct window_pane *wp) window_copy_redraw_screen(wp); } +void +window_copy_move_mouse(struct mouse_event *m) +{ + struct window_pane *wp; + u_int x, y; + + wp = cmd_mouse_pane(m, NULL, NULL); + if (wp == NULL || wp->mode != &window_copy_mode) + return; + + if (cmd_mouse_at(wp, m, &x, &y, 1) != 0) + return; + + window_copy_update_cursor(wp, x, y); +} + void window_copy_start_drag(struct client *c, struct mouse_event *m) {