mirror of
https://github.com/tmux/tmux.git
synced 2025-09-05 08:07:03 +00:00
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:
325
server-client.c
325
server-client.c
@ -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;
|
||||
|
||||
|
Reference in New Issue
Block a user