Rewrite of tmux mouse support which was a mess. Instead of having

options for "mouse-this" and "mouse-that", mouse events may be bound as
keys and there is one option "mouse" that turns on mouse support
entirely (set -g mouse on).

See the new MOUSE SUPPORT section of the man page for description of the
key names and new flags (-t= to specify the pane or window under mouse
as a target, and send-keys -M to pass through a mouse event).

The default builtin bindings for the mouse are:

    bind -n   MouseDown1Pane select-pane -t=; send-keys -M
    bind -n MouseDown1Status select-window -t=
    bind -n   MouseDrag1Pane copy-mode -M
    bind -n MouseDrag1Border resize-pane -M

To get the effect of turning mode-mouse off, do:

    unbind -n MouseDrag1Pane
    unbind -temacs-copy MouseDrag1Pane

The old mouse options are now gone, set-option -q may be used to
suppress warnings if mixing configuration files.
This commit is contained in:
nicm
2015-04-19 21:34:21 +00:00
parent ee123c2489
commit bf635e7741
27 changed files with 879 additions and 584 deletions

View File

@ -32,7 +32,7 @@
void server_client_check_focus(struct window_pane *);
void server_client_check_resize(struct window_pane *);
void server_client_check_mouse(struct client *, struct window_pane *);
int server_client_check_mouse(struct client *);
void server_client_repeat_timer(int, short, void *);
void server_client_check_exit(struct client *);
void server_client_check_redraw(struct client *);
@ -91,13 +91,6 @@ server_client_create(int fd)
c->prompt_buffer = NULL;
c->prompt_index = 0;
c->tty.mouse.xb = c->tty.mouse.button = 3;
c->tty.mouse.x = c->tty.mouse.y = -1;
c->tty.mouse.lx = c->tty.mouse.ly = -1;
c->tty.mouse.sx = c->tty.mouse.sy = -1;
c->tty.mouse.event = MOUSE_EVENT_UP;
c->tty.mouse.flags = 0;
c->flags |= CLIENT_FOCUSED;
evtimer_set(&c->repeat_timer, server_client_repeat_timer, c);
@ -289,56 +282,228 @@ server_client_status_timer(void)
}
/* Check for mouse keys. */
void
server_client_check_mouse(struct client *c, struct window_pane *wp)
int
server_client_check_mouse(struct client *c)
{
struct session *s = c->session;
struct options *oo = &s->options;
struct mouse_event *m = &c->tty.mouse;
int statusat;
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;
int key;
statusat = status_at_line(c);
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);
/* Is this a window selection click on the status line? */
if (statusat != -1 && m->y == (u_int)statusat &&
options_get_number(oo, "mouse-select-window")) {
if (m->event & MOUSE_EVENT_CLICK) {
status_set_window_at(c, m->x);
} else if (m->event == MOUSE_EVENT_WHEEL) {
if (m->wheel == MOUSE_WHEEL_UP)
session_previous(c->session, 0);
else if (m->wheel == MOUSE_WHEEL_DOWN)
session_next(c->session, 0);
server_redraw_session(s);
/* What type of event is this? */
if (MOUSE_DRAG(m->b)) {
type = DRAG;
if (c->tty.mouse_drag_flag) {
x = m->x, y = m->y, b = m->b;
log_debug("drag update at %u,%u", x, y);
} else {
x = m->lx, y = m->ly, b = m->lb;
log_debug("drag start at %u,%u", x, y);
}
recalculate_sizes();
return;
} else if (MOUSE_WHEEL(m->b)) {
type = WHEEL;
x = m->x, y = m->y, b = m->b;
log_debug("wheel at %u,%u", x, y);
} else if (MOUSE_BUTTONS(m->b) == 3) {
type = UP;
x = m->x, y = m->y, b = m->lb;
log_debug("up at %u,%u", x, y);
} else {
type = DOWN;
x = m->x, y = m->y, b = m->b;
log_debug("down at %u,%u", x, y);
}
if (type == NOTYPE)
return (KEYC_NONE);
/* Always save the session. */
m->s = s->id;
/* Is this on the status line? */
m->statusat = status_at_line(c);
if (m->statusat != -1 && y == (u_int)m->statusat) {
w = status_get_window_at(c, x);
if (w == NULL)
return (KEYC_NONE);
m->w = w->id;
where = STATUS;
} else
m->w = -1;
/* Not on status line. Adjust position and check for border or pane. */
if (where == NOWHERE) {
if (m->statusat == 0 && y > 0)
y--;
else if (m->statusat > 0 && y >= (u_int)m->statusat)
y = m->statusat - 1;
TAILQ_FOREACH(wp, &s->curw->window->panes, entry) {
if ((wp->xoff + wp->sx == x &&
wp->yoff <= 1 + y &&
wp->yoff + wp->sy >= y) ||
(wp->yoff + wp->sy == y &&
wp->xoff <= 1 + x &&
wp->xoff + wp->sx >= x))
break;
}
if (wp != NULL)
where = BORDER;
else {
wp = window_get_active_at(s->curw->window, x, y);
if (wp != NULL)
where = PANE;
}
if (where == NOWHERE)
return (KEYC_NONE);
m->wp = wp->id;
m->w = wp->window->id;
} else
m->wp = -1;
/* Stop dragging if needed. */
if (type != DRAG && c->tty.mouse_drag_flag) {
if (c->tty.mouse_drag_release != NULL)
c->tty.mouse_drag_release(c, m);
c->tty.mouse_drag_update = NULL;
c->tty.mouse_drag_release = NULL;
c->tty.mouse_drag_flag = 0;
return (KEYC_NONE);
}
/*
* Not on status line - adjust mouse position if status line is at the
* top and limit if at the bottom. From here on a struct mouse
* represents the offset onto the window itself.
*/
if (statusat == 0 && m->y > 0)
m->y--;
else if (statusat > 0 && m->y >= (u_int)statusat)
m->y = statusat - 1;
/* Convert to a key binding. */
key = KEYC_NONE;
switch (type) {
case NOTYPE:
break;
case DRAG:
if (c->tty.mouse_drag_update != NULL)
c->tty.mouse_drag_update(c, m);
else {
switch (MOUSE_BUTTONS(b)) {
case 0:
if (where == PANE)
key = KEYC_MOUSEDRAG1_PANE;
if (where == STATUS)
key = KEYC_MOUSEDRAG1_STATUS;
if (where == BORDER)
key = KEYC_MOUSEDRAG1_BORDER;
break;
case 1:
if (where == PANE)
key = KEYC_MOUSEDRAG2_PANE;
if (where == STATUS)
key = KEYC_MOUSEDRAG2_STATUS;
if (where == BORDER)
key = KEYC_MOUSEDRAG2_BORDER;
break;
case 2:
if (where == PANE)
key = KEYC_MOUSEDRAG3_PANE;
if (where == STATUS)
key = KEYC_MOUSEDRAG3_STATUS;
if (where == BORDER)
key = KEYC_MOUSEDRAG3_BORDER;
break;
}
}
/* Is this a pane selection? */
if (options_get_number(oo, "mouse-select-pane") &&
(m->event == MOUSE_EVENT_DOWN || m->event == MOUSE_EVENT_WHEEL)) {
window_set_active_at(wp->window, m->x, m->y);
server_redraw_window(wp->window);
wp = wp->window->active; /* may have changed */
c->tty.mouse_drag_flag = 1;
break;
case WHEEL:
if (MOUSE_BUTTONS(b) == MOUSE_WHEEL_UP) {
if (where == PANE)
key = KEYC_WHEELUP_PANE;
if (where == STATUS)
key = KEYC_WHEELUP_STATUS;
if (where == BORDER)
key = KEYC_WHEELUP_BORDER;
} else {
if (where == PANE)
key = KEYC_WHEELDOWN_PANE;
if (where == STATUS)
key = KEYC_WHEELDOWN_STATUS;
if (where == BORDER)
key = KEYC_WHEELDOWN_BORDER;
}
break;
case UP:
switch (MOUSE_BUTTONS(b)) {
case 0:
if (where == PANE)
key = KEYC_MOUSEUP1_PANE;
if (where == STATUS)
key = KEYC_MOUSEUP1_STATUS;
if (where == BORDER)
key = KEYC_MOUSEUP1_BORDER;
break;
case 1:
if (where == PANE)
key = KEYC_MOUSEUP2_PANE;
if (where == STATUS)
key = KEYC_MOUSEUP2_STATUS;
if (where == BORDER)
key = KEYC_MOUSEUP2_BORDER;
break;
case 2:
if (where == PANE)
key = KEYC_MOUSEUP3_PANE;
if (where == STATUS)
key = KEYC_MOUSEUP3_STATUS;
if (where == BORDER)
key = KEYC_MOUSEUP3_BORDER;
break;
}
break;
case DOWN:
switch (MOUSE_BUTTONS(b)) {
case 0:
if (where == PANE)
key = KEYC_MOUSEDOWN1_PANE;
if (where == STATUS)
key = KEYC_MOUSEDOWN1_STATUS;
if (where == BORDER)
key = KEYC_MOUSEDOWN1_BORDER;
break;
case 1:
if (where == PANE)
key = KEYC_MOUSEDOWN2_PANE;
if (where == STATUS)
key = KEYC_MOUSEDOWN2_STATUS;
if (where == BORDER)
key = KEYC_MOUSEDOWN2_BORDER;
break;
case 2:
if (where == PANE)
key = KEYC_MOUSEDOWN3_PANE;
if (where == STATUS)
key = KEYC_MOUSEDOWN3_STATUS;
if (where == BORDER)
key = KEYC_MOUSEDOWN3_BORDER;
break;
}
break;
}
if (key == KEYC_NONE)
return (KEYC_NONE);
/* Check if trying to resize pane. */
if (options_get_number(oo, "mouse-resize-pane"))
layout_resize_pane_mouse(c);
/* Apply modifiers if any. */
if (b & MOUSE_MASK_META)
key |= KEYC_ESCAPE;
if (b & MOUSE_MASK_CTRL)
key |= KEYC_CTRL;
if (b & MOUSE_MASK_SHIFT)
key |= KEYC_SHIFT;
/* Update last and pass through to client. */
window_pane_mouse(wp, c->session, m);
return (key);
}
/* Is this fast enough to probably be a paste? */
@ -361,6 +526,7 @@ server_client_assume_paste(struct session *s)
void
server_client_handle_key(struct client *c, int key)
{
struct mouse_event *m = &c->tty.mouse;
struct session *s;
struct window *w;
struct window_pane *wp;
@ -372,21 +538,20 @@ server_client_handle_key(struct client *c, int key)
if ((c->flags & (CLIENT_DEAD|CLIENT_SUSPENDED)) != 0)
return;
/* No session, do nothing. */
if (c->session == NULL)
return;
s = c->session;
w = c->session->curw->window;
wp = w->active;
/* Update the activity timer. */
if (gettimeofday(&c->activity_time, NULL) != 0)
fatal("gettimeofday failed");
memcpy(&s->last_activity_time, &s->activity_time,
sizeof s->last_activity_time);
memcpy(&s->activity_time, &c->activity_time, sizeof s->activity_time);
w = c->session->curw->window;
wp = w->active;
/* Special case: number keys jump to pane in identify mode. */
if (c->flags & CLIENT_IDENTIFY && key >= '0' && key <= '9') {
if (c->flags & CLIENT_READONLY)
@ -414,9 +579,19 @@ server_client_handle_key(struct client *c, int key)
if (key == KEYC_MOUSE) {
if (c->flags & CLIENT_READONLY)
return;
server_client_check_mouse(c, wp);
return;
}
key = server_client_check_mouse(c);
if (key == KEYC_NONE)
return;
m->valid = 1;
m->key = key;
if (!options_get_number(&s->options, "mouse")) {
window_pane_key(wp, c, s, key, m);
return;
}
} else
m->valid = 0;
/* Is this a prefix key? */
if (key == options_get_number(&s->options, "prefix"))
@ -442,9 +617,9 @@ server_client_handle_key(struct client *c, int key)
/* Try as a non-prefix key binding. */
if (ispaste || (bd = key_bindings_lookup(key)) == NULL) {
if (!(c->flags & CLIENT_READONLY))
window_pane_key(wp, s, key);
window_pane_key(wp, c, s, key, m);
} else
key_bindings_dispatch(bd, c);
key_bindings_dispatch(bd, c, m);
return;
}
@ -458,7 +633,7 @@ server_client_handle_key(struct client *c, int key)
if (isprefix)
c->flags |= CLIENT_PREFIX;
else if (!(c->flags & CLIENT_READONLY))
window_pane_key(wp, s, key);
window_pane_key(wp, c, s, key, m);
}
return;
}
@ -469,7 +644,7 @@ server_client_handle_key(struct client *c, int key)
if (isprefix)
c->flags |= CLIENT_PREFIX;
else if (!(c->flags & CLIENT_READONLY))
window_pane_key(wp, s, key);
window_pane_key(wp, c, s, key, m);
return;
}
@ -485,7 +660,7 @@ server_client_handle_key(struct client *c, int key)
}
/* Dispatch the command. */
key_bindings_dispatch(bd, c);
key_bindings_dispatch(bd, c, m);
}
/* Client functions that need to happen every loop. */
@ -622,7 +797,6 @@ server_client_reset_state(struct client *c)
struct window_pane *wp = w->active;
struct screen *s = wp->screen;
struct options *oo = &c->session->options;
struct options *wo = &w->options;
int status, mode, o;
if (c->flags & CLIENT_SUSPENDED)
@ -642,29 +816,12 @@ server_client_reset_state(struct client *c)
}
/*
* Resizing panes with the mouse requires at least button mode to give
* a smooth appearance.
* Set mouse mode if requested. To support dragging, always use button
* mode.
*/
mode = s->mode;
if ((c->tty.mouse.flags & MOUSE_RESIZE_PANE) &&
!(mode & MODE_MOUSE_BUTTON))
mode |= MODE_MOUSE_BUTTON;
/*
* Any mode will do for mouse-select-pane, but set standard mode if
* none.
*/
if ((mode & ALL_MOUSE_MODES) == 0) {
if (TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry) != NULL &&
options_get_number(oo, "mouse-select-pane"))
mode |= MODE_MOUSE_STANDARD;
else if (options_get_number(oo, "mouse-resize-pane"))
mode |= MODE_MOUSE_STANDARD;
else if (options_get_number(oo, "mouse-select-window"))
mode |= MODE_MOUSE_STANDARD;
else if (options_get_number(wo, "mode-mouse"))
mode |= MODE_MOUSE_STANDARD;
}
if (options_get_number(oo, "mouse"))
mode = (mode & ~ALL_MOUSE_MODES) | MODE_MOUSE_BUTTON;
/*
* Set UTF-8 mouse input if required. If the terminal is UTF-8, the
@ -945,9 +1102,9 @@ server_client_msg_command(struct client *c, struct imsg *imsg)
cmd_free_argv(argc, argv);
if (c != cfg_client || cfg_finished)
cmdq_run(c->cmdq, cmdlist);
cmdq_run(c->cmdq, cmdlist, NULL);
else
cmdq_append(c->cmdq, cmdlist);
cmdq_append(c->cmdq, cmdlist, NULL);
cmd_list_free(cmdlist);
return;