Support double and triple clicks (they are cumulative, so double is

fired then triple), and use for select-word and select-line in copy
mode. Inspired by a different solution from Omar Sandoval.
pull/581/head
nicm 2016-10-11 09:30:36 +00:00
parent 76d6d3641f
commit 85d7afaefc
6 changed files with 170 additions and 11 deletions

View File

@ -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",

View File

@ -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. */

View File

@ -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)

4
tmux.1
View File

@ -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

14
tmux.h
View File

@ -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;

View File

@ -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)
{