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.
pull/1/head
nicm 2015-04-19 21:34:21 +00:00
parent ee123c2489
commit bf635e7741
27 changed files with 879 additions and 584 deletions

2
cfg.c
View File

@ -75,7 +75,7 @@ load_cfg(const char *path, struct cmd_q *cmdq, char **cause)
if (cmdlist == NULL)
continue;
cmdq_append(cmdq, cmdlist);
cmdq_append(cmdq, cmdlist, NULL);
cmd_list_free(cmdlist);
found++;
}

View File

@ -151,7 +151,7 @@ cmd_command_prompt_callback(void *data, const char *s)
return (0);
}
cmdq_run(c->cmdq, cmdlist);
cmdq_run(c->cmdq, cmdlist, NULL);
cmd_list_free(cmdlist);
if (c->prompt_callbackfn != (void *) &cmd_command_prompt_callback)

View File

@ -105,7 +105,7 @@ cmd_confirm_before_callback(void *data, const char *s)
return (0);
}
cmdq_run(c->cmdq, cmdlist);
cmdq_run(c->cmdq, cmdlist, NULL);
cmd_list_free(cmdlist);
return (0);

View File

@ -28,8 +28,8 @@ enum cmd_retval cmd_copy_mode_exec(struct cmd *, struct cmd_q *);
const struct cmd_entry cmd_copy_mode_entry = {
"copy-mode", NULL,
"t:u", 0, 0,
"[-u] " CMD_TARGET_PANE_USAGE,
"Mt:u", 0, 0,
"[-Mu] " CMD_TARGET_PANE_USAGE,
0,
cmd_copy_mode_exec
};
@ -46,9 +46,16 @@ enum cmd_retval
cmd_copy_mode_exec(struct cmd *self, struct cmd_q *cmdq)
{
struct args *args = self->args;
struct client *c = cmdq->client;
struct session *s;
struct window_pane *wp;
if (cmd_find_pane(cmdq, args_get(args, 't'), NULL, &wp) == NULL)
if (args_has(args, 'M')) {
if ((wp = cmd_mouse_pane(&cmdq->item->mouse, &s, NULL)) == NULL)
return (CMD_RETURN_NORMAL);
if (c == NULL || c->session != s)
return (CMD_RETURN_NORMAL);
} else if (cmd_find_pane(cmdq, args_get(args, 't'), NULL, &wp) == NULL)
return (CMD_RETURN_ERROR);
if (self->entry == &cmd_clock_mode_entry) {
@ -61,6 +68,8 @@ cmd_copy_mode_exec(struct cmd *self, struct cmd_q *cmdq)
return (CMD_RETURN_NORMAL);
window_copy_init_from_pane(wp);
}
if (args_has(args, 'M'))
window_copy_start_drag(c, &cmdq->item->mouse);
if (wp->mode == &window_copy_mode && args_has(self->args, 'u'))
window_copy_pageup(wp);

View File

@ -95,7 +95,7 @@ cmd_if_shell_exec(struct cmd *self, struct cmd_q *cmdq)
}
return (CMD_RETURN_ERROR);
}
cmdq_run(cmdq, cmdlist);
cmdq_run(cmdq, cmdlist, NULL);
cmd_list_free(cmdlist);
return (CMD_RETURN_NORMAL);
}
@ -152,7 +152,7 @@ cmd_if_shell_callback(struct job *job)
cmdq1->emptyfn = cmd_if_shell_done;
cmdq1->data = cdata;
cmdq_run(cmdq1, cmdlist);
cmdq_run(cmdq1, cmdlist, NULL);
cmd_list_free(cmdlist);
}

View File

@ -20,6 +20,7 @@
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "tmux.h"
@ -132,9 +133,9 @@ cmdq_guard(struct cmd_q *cmdq, const char *guard, int flags)
/* Add command list to queue and begin processing if needed. */
void
cmdq_run(struct cmd_q *cmdq, struct cmd_list *cmdlist)
cmdq_run(struct cmd_q *cmdq, struct cmd_list *cmdlist, struct mouse_event *m)
{
cmdq_append(cmdq, cmdlist);
cmdq_append(cmdq, cmdlist, m);
if (cmdq->item == NULL) {
cmdq->cmd = NULL;
@ -144,7 +145,7 @@ cmdq_run(struct cmd_q *cmdq, struct cmd_list *cmdlist)
/* Add command list to queue. */
void
cmdq_append(struct cmd_q *cmdq, struct cmd_list *cmdlist)
cmdq_append(struct cmd_q *cmdq, struct cmd_list *cmdlist, struct mouse_event *m)
{
struct cmd_q_item *item;
@ -152,6 +153,11 @@ cmdq_append(struct cmd_q *cmdq, struct cmd_list *cmdlist)
item->cmdlist = cmdlist;
TAILQ_INSERT_TAIL(&cmdq->queue, item, qentry);
cmdlist->references++;
if (m != NULL)
memcpy(&item->mouse, m, sizeof item->mouse);
else
item->mouse.valid = 0;
}
/* Continue processing command queue. Returns 1 if finishes empty. */

View File

@ -28,10 +28,13 @@
enum cmd_retval cmd_resize_pane_exec(struct cmd *, struct cmd_q *);
void cmd_resize_pane_mouse_update(struct client *, struct mouse_event *);
const struct cmd_entry cmd_resize_pane_entry = {
"resize-pane", "resizep",
"DLRt:Ux:y:Z", 0, 1,
"[-DLRUZ] [-x width] [-y height] " CMD_TARGET_PANE_USAGE " [adjustment]",
"DLMRt:Ux:y:Z", 0, 1,
"[-DLMRUZ] [-x width] [-y height] " CMD_TARGET_PANE_USAGE
" [adjustment]",
0,
cmd_resize_pane_exec
};
@ -40,6 +43,8 @@ enum cmd_retval
cmd_resize_pane_exec(struct cmd *self, struct cmd_q *cmdq)
{
struct args *args = self->args;
struct client *c = cmdq->client;
struct session *s;
struct winlink *wl;
struct window *w;
const char *errstr;
@ -48,6 +53,16 @@ cmd_resize_pane_exec(struct cmd *self, struct cmd_q *cmdq)
u_int adjust;
int x, y;
if (args_has(args, 'M')) {
if (cmd_mouse_window(&cmdq->item->mouse, &s) == NULL)
return (CMD_RETURN_NORMAL);
if (c == NULL || c->session != s)
return (CMD_RETURN_NORMAL);
c->tty.mouse_drag_update = cmd_resize_pane_mouse_update;
cmd_resize_pane_mouse_update(c, &cmdq->item->mouse);
return (CMD_RETURN_NORMAL);
}
if ((wl = cmd_find_pane(cmdq, args_get(args, 't'), NULL, &wp)) == NULL)
return (CMD_RETURN_ERROR);
w = wl->window;
@ -106,3 +121,50 @@ cmd_resize_pane_exec(struct cmd *self, struct cmd_q *cmdq)
return (CMD_RETURN_NORMAL);
}
void
cmd_resize_pane_mouse_update(struct client *c, struct mouse_event *m)
{
struct winlink *wl;
struct window_pane *wp;
int found;
u_int y, ly;
wl = cmd_mouse_window(m, NULL);
if (wl == NULL) {
c->tty.mouse_drag_update = NULL;
return;
}
y = m->y;
if (m->statusat == 0 && y > 0)
y--;
else if (m->statusat > 0 && y >= (u_int)m->statusat)
y = m->statusat - 1;
ly = m->ly;
if (m->statusat == 0 && ly > 0)
ly--;
else if (m->statusat > 0 && ly >= (u_int)m->statusat)
ly = m->statusat - 1;
found = 0;
TAILQ_FOREACH(wp, &wl->window->panes, entry) {
if (!window_pane_visible(wp))
continue;
if (wp->xoff + wp->sx == m->lx &&
wp->yoff <= 1 + ly && wp->yoff + wp->sy >= ly) {
layout_resize_pane(wp, LAYOUT_LEFTRIGHT, m->x - m->lx);
found = 1;
}
if (wp->yoff + wp->sy == ly &&
wp->xoff <= 1 + m->lx && wp->xoff + wp->sx >= m->lx) {
layout_resize_pane(wp, LAYOUT_TOPBOTTOM, y - ly);
found = 1;
}
}
if (found)
server_redraw_window(wl->window);
else
c->tty.mouse_drag_update = NULL;
}

View File

@ -31,8 +31,8 @@ enum cmd_retval cmd_send_keys_exec(struct cmd *, struct cmd_q *);
const struct cmd_entry cmd_send_keys_entry = {
"send-keys", "send",
"lRt:", 0, -1,
"[-lR] " CMD_TARGET_PANE_USAGE " key ...",
"lRMt:", 0, -1,
"[-lRM] " CMD_TARGET_PANE_USAGE " key ...",
0,
cmd_send_keys_exec
};
@ -49,12 +49,23 @@ enum cmd_retval
cmd_send_keys_exec(struct cmd *self, struct cmd_q *cmdq)
{
struct args *args = self->args;
struct mouse_event *m = &cmdq->item->mouse;
struct window_pane *wp;
struct session *s;
struct input_ctx *ictx;
const u_char *str;
int i, key;
if (args_has(args, 'M')) {
wp = cmd_mouse_pane(m, &s, NULL);
if (wp == NULL) {
cmdq_error(cmdq, "no mouse target");
return (CMD_RETURN_ERROR);
}
window_pane_key(wp, NULL, s, m->key, m);
return (CMD_RETURN_NORMAL);
}
if (cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp) == NULL)
return (CMD_RETURN_ERROR);
@ -63,7 +74,7 @@ cmd_send_keys_exec(struct cmd *self, struct cmd_q *cmdq)
key = options_get_number(&s->options, "prefix2");
else
key = options_get_number(&s->options, "prefix");
window_pane_key(wp, s, key);
window_pane_key(wp, NULL, s, key, NULL);
return (CMD_RETURN_NORMAL);
}
@ -88,10 +99,10 @@ cmd_send_keys_exec(struct cmd *self, struct cmd_q *cmdq)
if (!args_has(args, 'l') &&
(key = key_string_lookup_string(str)) != KEYC_NONE) {
window_pane_key(wp, s, key);
window_pane_key(wp, NULL, s, key, NULL);
} else {
for (; *str != '\0'; str++)
window_pane_key(wp, s, *str);
window_pane_key(wp, NULL, s, *str, NULL);
}
}

94
cmd.c
View File

@ -348,6 +348,7 @@ cmd_current_session(struct cmd_q *cmdq, int prefer_unattached)
const char *path;
int found;
/* Try the queue session. */
if (c != NULL && c->session != NULL)
return (c->session);
@ -504,6 +505,74 @@ cmd_choose_client(struct clients *cc)
return (cbest);
}
/* Adjust current mouse position for a pane. */
int
cmd_mouse_at(struct window_pane *wp, struct mouse_event *m, u_int *xp,
u_int *yp, int last)
{
u_int x, y;
if (last) {
x = m->lx;
y = m->ly;
} else {
x = m->x;
y = m->y;
}
if (m->statusat == 0 && y > 0)
y--;
else if (m->statusat > 0 && y >= (u_int)m->statusat)
y = m->statusat - 1;
if (x < wp->xoff || x >= wp->xoff + wp->sx)
return (-1);
if (y < wp->yoff || y >= wp->yoff + wp->sy)
return (-1);
*xp = x - wp->xoff;
*yp = y - wp->yoff;
return (0);
}
/* Get current mouse window if any. */
struct winlink *
cmd_mouse_window(struct mouse_event *m, struct session **sp)
{
struct session *s;
struct window *w;
if (!m->valid || m->s == -1 || m->w == -1)
return (NULL);
if ((s = session_find_by_id(m->s)) == NULL)
return (NULL);
if ((w = window_find_by_id(m->w)) == NULL)
return (NULL);
if (sp != NULL)
*sp = s;
return (winlink_find_by_window(&s->windows, w));
}
/* Get current mouse pane if any. */
struct window_pane *
cmd_mouse_pane(struct mouse_event *m, struct session **sp, struct winlink **wlp)
{
struct winlink *wl;
struct window_pane *wp;
if ((wl = cmd_mouse_window(m, sp)) == NULL)
return (NULL);
if ((wp = window_pane_find_by_id(m->wp)) == NULL)
return (NULL);
if (!window_has_pane(wl->window, wp))
return (NULL);
if (wlp != NULL)
*wlp = wl;
return (wp);
}
/* Find the target client or report an error and return NULL. */
struct client *
cmd_find_client(struct cmd_q *cmdq, const char *arg, int quiet)
@ -928,7 +997,12 @@ no_colon:
* No colon in the string, first try special cases, then as a window
* and lastly as a session.
*/
if (arg[0] == '!' && arg[1] == '\0') {
if (arg[0] == '=' && arg[1] == '\0') {
if ((wl = cmd_mouse_window(&cmdq->item->mouse, &s)) == NULL) {
cmdq_error(cmdq, "no mouse target");
goto error;
}
} else if (arg[0] == '!' && arg[1] == '\0') {
if ((wl = TAILQ_FIRST(&s->lastw)) == NULL)
goto not_found;
} else if (arg[0] == '+' || arg[0] == '-') {
@ -959,14 +1033,16 @@ no_session:
cmdq_error(cmdq, "multiple sessions: %s", arg);
else
cmdq_error(cmdq, "session not found: %s", arg);
free(sessptr);
return (NULL);
goto error;
not_found:
if (ambiguous)
cmdq_error(cmdq, "multiple windows: %s", arg);
else
cmdq_error(cmdq, "window not found: %s", arg);
goto error;
error:
free(sessptr);
return (NULL);
}
@ -1228,6 +1304,18 @@ lookup_string:
return (wl);
no_period:
/* Check mouse event. */
if (arg[0] == '=' && arg[1] == '\0') {
*wpp = cmd_mouse_pane(&cmdq->item->mouse, &s, &wl);
if (*wpp == NULL) {
cmdq_error(cmdq, "no mouse target");
return (NULL);
}
if (sp != NULL)
*sp = s;
return (wl);
}
/* Try as a pane number alone. */
idx = strtonum(arg, 0, INT_MAX, &errstr);
if (errstr != NULL)

View File

@ -81,7 +81,7 @@ control_callback(struct client *c, int closed, unused void *data)
} else {
TAILQ_FOREACH(cmd, &cmdlist->list, qentry)
cmd->flags |= CMD_CONTROL;
cmdq_run(c->cmdq, cmdlist);
cmdq_run(c->cmdq, cmdlist, NULL);
cmd_list_free(cmdlist);
}

View File

@ -31,6 +31,8 @@
* direction with output).
*/
void input_key_mouse(struct window_pane *, struct mouse_event *);
struct input_key_ent {
int key;
const char *data;
@ -135,7 +137,7 @@ const struct input_key_ent input_keys[] = {
/* Translate a key code into an output key sequence. */
void
input_key(struct window_pane *wp, int key)
input_key(struct window_pane *wp, int key, struct mouse_event *m)
{
const struct input_key_ent *ike;
u_int i;
@ -143,7 +145,14 @@ input_key(struct window_pane *wp, int key)
char *out;
u_char ch;
log_debug("writing key 0x%x", key);
log_debug("writing key 0x%x (%s)", key, key_string_lookup_key(key));
/* If this is a mouse key, pass off to mouse function. */
if (KEYC_IS_MOUSE(key)) {
if (m != NULL && m->wp != -1 && (u_int)m->wp == wp->id)
input_key_mouse(wp, m);
return;
}
/*
* If this is a normal 7-bit key, just send it, with a leading escape
@ -200,55 +209,47 @@ input_key(struct window_pane *wp, int key)
/* Translate mouse and output. */
void
input_mouse(struct window_pane *wp, struct session *s, struct mouse_event *m)
input_key_mouse(struct window_pane *wp, struct mouse_event *m)
{
char buf[40];
size_t len;
struct paste_buffer *pb;
int event;
char buf[40];
size_t len;
u_int x, y;
if (wp->screen->mode & ALL_MOUSE_MODES) {
/*
* Use the SGR (1006) extension only if the application
* requested it and the underlying terminal also sent the event
* in this format (this is because an old style mouse release
* event cannot be converted into the new SGR format, since the
* released button is unknown). Otherwise pretend that tmux
* doesn't speak this extension, and fall back to the UTF-8
* (1005) extension if the application requested, or to the
* legacy format.
*/
if (m->sgr && (wp->screen->mode & MODE_MOUSE_SGR)) {
len = xsnprintf(buf, sizeof buf, "\033[<%u;%u;%u%c",
m->sgr_xb, m->x + 1, m->y + 1,
m->sgr_rel ? 'm' : 'M');
} else if (wp->screen->mode & MODE_MOUSE_UTF8) {
len = xsnprintf(buf, sizeof buf, "\033[M");
len += utf8_split2(m->xb + 32, &buf[len]);
len += utf8_split2(m->x + 33, &buf[len]);
len += utf8_split2(m->y + 33, &buf[len]);
} else {
if (m->xb > 223)
return;
len = xsnprintf(buf, sizeof buf, "\033[M");
buf[len++] = m->xb + 32;
buf[len++] = m->x + 33;
buf[len++] = m->y + 33;
}
bufferevent_write(wp->event, buf, len);
if ((wp->screen->mode & ALL_MOUSE_MODES) == 0)
return;
if (!window_pane_visible(wp))
return;
if (cmd_mouse_at(wp, m, &x, &y, 0) != 0)
return;
}
if (options_get_number(&wp->window->options, "mode-mouse") != 1)
/* If this pane is not in button mode, discard motion events. */
if (!(wp->screen->mode & MODE_MOUSE_BUTTON) && (m->b & MOUSE_MASK_DRAG))
return;
event = m->event & (MOUSE_EVENT_CLICK|MOUSE_EVENT_WHEEL);
if (wp->mode == NULL && m->button == 1 && event == MOUSE_EVENT_CLICK) {
pb = paste_get_top();
if (pb != NULL)
paste_send_pane(pb, wp, "\r", 1);
} else if (window_pane_set_mode(wp, &window_copy_mode) == 0) {
window_copy_init_from_pane(wp);
if (wp->mode->mouse != NULL)
wp->mode->mouse(wp, s, m);
/*
* Use the SGR (1006) extension only if the application requested it
* and the underlying terminal also sent the event in this format (this
* is because an old style mouse release event cannot be converted into
* the new SGR format, since the released button is unknown). Otherwise
* pretend that tmux doesn't speak this extension, and fall back to the
* UTF-8 (1005) extension if the application requested, or to the
* legacy format.
*/
if (m->sgr_type != ' ' && (wp->screen->mode & MODE_MOUSE_SGR)) {
len = xsnprintf(buf, sizeof buf, "\033[<%u;%u;%u%c",
m->sgr_b, x + 1, y + 1, m->sgr_type);
} else if (wp->screen->mode & MODE_MOUSE_UTF8) {
len = xsnprintf(buf, sizeof buf, "\033[M");
len += utf8_split2(m->b + 32, &buf[len]);
len += utf8_split2(x + 33, &buf[len]);
len += utf8_split2(y + 33, &buf[len]);
} else {
if (m->b > 223)
return;
len = xsnprintf(buf, sizeof buf, "\033[M");
buf[len++] = m->b + 32;
buf[len++] = x + 33;
buf[len++] = y + 33;
}
bufferevent_write(wp->event, buf, len);
}

View File

@ -158,6 +158,10 @@ key_bindings_init(void)
"bind -r C-Down resize-pane -D",
"bind -r C-Left resize-pane -L",
"bind -r C-Right resize-pane -R",
"bind -n MouseDown1Pane select-pane -t=\\; send-keys -M",
"bind -n MouseDrag1Border resize-pane -M",
"bind -n MouseDown1Status select-window -t=",
"bind -n MouseDrag1Pane copy-mode -M",
};
u_int i;
struct cmd_list *cmdlist;
@ -173,14 +177,15 @@ key_bindings_init(void)
"<default-keys>", i, &cause);
if (error != 0)
fatalx("bad default key");
cmdq_run(cmdq, cmdlist);
cmd_list_free(cmdlist);
cmdq_run(cmdq, cmdlist, NULL);
cmd_list_free (cmdlist);
}
cmdq_free(cmdq);
}
void
key_bindings_dispatch(struct key_binding *bd, struct client *c)
key_bindings_dispatch(struct key_binding *bd, struct client *c,
struct mouse_event *m)
{
struct cmd *cmd;
int readonly;
@ -195,5 +200,5 @@ key_bindings_dispatch(struct key_binding *bd, struct client *c)
return;
}
cmdq_run(c->cmdq, bd->cmdlist);
cmdq_run(c->cmdq, bd->cmdlist, m);
}

View File

@ -82,6 +82,19 @@ const struct {
{ "KPEnter", KEYC_KP_ENTER },
{ "KP0", KEYC_KP_ZERO },
{ "KP.", KEYC_KP_PERIOD },
/* Mouse keys. */
KEYC_MOUSE_STRING(MOUSEDOWN1, MouseDown1),
KEYC_MOUSE_STRING(MOUSEDOWN2, MouseDown2),
KEYC_MOUSE_STRING(MOUSEDOWN3, MouseDown3),
KEYC_MOUSE_STRING(MOUSEUP1, MouseUp1),
KEYC_MOUSE_STRING(MOUSEUP2, MouseUp2),
KEYC_MOUSE_STRING(MOUSEUP3, MouseUp3),
KEYC_MOUSE_STRING(MOUSEDRAG1, MouseDrag1),
KEYC_MOUSE_STRING(MOUSEDRAG2, MouseDrag2),
KEYC_MOUSE_STRING(MOUSEDRAG3, MouseDrag3),
KEYC_MOUSE_STRING(WHEELUP, WheelUp),
KEYC_MOUSE_STRING(WHEELDOWN, WheelDown),
};
/* Find key string in table. */
@ -192,7 +205,9 @@ key_string_lookup_key(int key)
/* Handle no key. */
if (key == KEYC_NONE)
return ("none");
return ("<NONE>");
if (key == KEYC_MOUSE)
return ("<MOUSE>");
/*
* Special case: display C-@ as C-Space. Could do this below in

View File

@ -519,58 +519,6 @@ layout_resize_pane(struct window_pane *wp, enum layout_type type, int change)
notify_window_layout_changed(wp->window);
}
/* Resize pane based on mouse events. */
void
layout_resize_pane_mouse(struct client *c)
{
struct window *w;
struct window_pane *wp;
struct mouse_event *m = &c->tty.mouse;
int pane_border;
w = c->session->curw->window;
pane_border = 0;
if (m->event & MOUSE_EVENT_DRAG && m->flags & MOUSE_RESIZE_PANE) {
TAILQ_FOREACH(wp, &w->panes, entry) {
if (!window_pane_visible(wp))
continue;
if (wp->xoff + wp->sx == m->lx &&
wp->yoff <= 1 + m->ly &&
wp->yoff + wp->sy >= m->ly) {
layout_resize_pane(wp, LAYOUT_LEFTRIGHT,
m->x - m->lx);
pane_border = 1;
}
if (wp->yoff + wp->sy == m->ly &&
wp->xoff <= 1 + m->lx &&
wp->xoff + wp->sx >= m->lx) {
layout_resize_pane(wp, LAYOUT_TOPBOTTOM,
m->y - m->ly);
pane_border = 1;
}
}
if (pane_border)
server_redraw_window(w);
} else if (m->event & MOUSE_EVENT_DOWN) {
TAILQ_FOREACH(wp, &w->panes, entry) {
if ((wp->xoff + wp->sx == m->x &&
wp->yoff <= 1 + m->y &&
wp->yoff + wp->sy >= m->y) ||
(wp->yoff + wp->sy == m->y &&
wp->xoff <= 1 + m->x &&
wp->xoff + wp->sx >= m->x)) {
pane_border = 1;
}
}
}
if (pane_border)
m->flags |= MOUSE_RESIZE_PANE;
else
m->flags &= ~MOUSE_RESIZE_PANE;
}
/* Helper function to grow pane. */
int
layout_resize_pane_grow(

View File

@ -251,6 +251,10 @@ const struct mode_key_entry mode_key_vi_choice[] = {
{ KEYC_RIGHT, 0, MODEKEYCHOICE_TREE_EXPAND },
{ KEYC_LEFT | KEYC_CTRL, 0, MODEKEYCHOICE_TREE_COLLAPSE_ALL },
{ KEYC_RIGHT | KEYC_CTRL, 0, MODEKEYCHOICE_TREE_EXPAND_ALL },
{ KEYC_MOUSEDOWN1_PANE, 0, MODEKEYCHOICE_CHOOSE },
{ KEYC_MOUSEDOWN3_PANE, 0, MODEKEYCHOICE_TREE_TOGGLE },
{ KEYC_WHEELUP_PANE, 0, MODEKEYCHOICE_UP },
{ KEYC_WHEELDOWN_PANE, 0, MODEKEYCHOICE_DOWN },
{ 0, -1, 0 }
};
@ -326,6 +330,9 @@ const struct mode_key_entry mode_key_vi_copy[] = {
{ KEYC_RIGHT, 0, MODEKEYCOPY_RIGHT },
{ KEYC_UP | KEYC_CTRL, 0, MODEKEYCOPY_SCROLLUP },
{ KEYC_UP, 0, MODEKEYCOPY_UP },
{ KEYC_WHEELUP_PANE, 0, MODEKEYCOPY_SCROLLUP },
{ KEYC_WHEELDOWN_PANE, 0, MODEKEYCOPY_SCROLLDOWN },
{ KEYC_MOUSEDRAG1_PANE, 0, MODEKEYCOPY_STARTSELECTION },
{ 0, -1, 0 }
};
@ -405,6 +412,10 @@ const struct mode_key_entry mode_key_emacs_choice[] = {
{ KEYC_RIGHT, 0, MODEKEYCHOICE_TREE_EXPAND },
{ KEYC_LEFT | KEYC_CTRL, 0, MODEKEYCHOICE_TREE_COLLAPSE_ALL },
{ KEYC_RIGHT | KEYC_CTRL, 0, MODEKEYCHOICE_TREE_EXPAND_ALL },
{ KEYC_MOUSEDOWN1_PANE, 0, MODEKEYCHOICE_CHOOSE },
{ KEYC_MOUSEDOWN3_PANE, 0, MODEKEYCHOICE_TREE_TOGGLE },
{ KEYC_WHEELUP_PANE, 0, MODEKEYCHOICE_UP },
{ KEYC_WHEELDOWN_PANE, 0, MODEKEYCHOICE_DOWN },
{ 0, -1, 0 }
};
@ -467,6 +478,9 @@ const struct mode_key_entry mode_key_emacs_copy[] = {
{ KEYC_UP | KEYC_CTRL, 0, MODEKEYCOPY_SCROLLUP },
{ KEYC_UP | KEYC_ESCAPE, 0, MODEKEYCOPY_HALFPAGEUP },
{ KEYC_UP, 0, MODEKEYCOPY_UP },
{ KEYC_WHEELUP_PANE, 0, MODEKEYCOPY_SCROLLUP },
{ KEYC_WHEELDOWN_PANE, 0, MODEKEYCOPY_SCROLLDOWN },
{ KEYC_MOUSEDRAG1_PANE, 0, MODEKEYCOPY_STARTSELECTION },
{ 0, -1, 0 }
};

View File

@ -36,9 +36,6 @@
const char *options_table_mode_keys_list[] = {
"emacs", "vi", NULL
};
const char *options_table_mode_mouse_list[] = {
"off", "on", "copy-mode", NULL
};
const char *options_table_clock_mode_style_list[] = {
"12", "24", NULL
};
@ -255,17 +252,7 @@ const struct options_table_entry session_options_table[] = {
.default_str = "bg=yellow,fg=black"
},
{ .name = "mouse-resize-pane",
.type = OPTIONS_TABLE_FLAG,
.default_num = 0
},
{ .name = "mouse-select-pane",
.type = OPTIONS_TABLE_FLAG,
.default_num = 0
},
{ .name = "mouse-select-window",
{ .name = "mouse",
.type = OPTIONS_TABLE_FLAG,
.default_num = 0
},
@ -575,12 +562,6 @@ const struct options_table_entry window_options_table[] = {
.default_num = MODEKEY_EMACS
},
{ .name = "mode-mouse",
.type = OPTIONS_TABLE_CHOICE,
.choices = options_table_mode_mouse_list,
.default_num = 0
},
{ .name = "mode-style",
.type = OPTIONS_TABLE_STYLE,
.default_str = "bg=yellow,fg=black"

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;

View File

@ -604,7 +604,8 @@ server_set_stdin_callback(struct client *c, void (*cb)(struct client *, int,
void
server_unzoom_window(struct window *w)
{
window_unzoom(w);
server_redraw_window(w);
server_status_window(w);
if (window_unzoom(w) == 0) {
server_redraw_window(w);
server_status_window(w);
}
}

View File

@ -118,9 +118,9 @@ status_redraw_get_right(struct client *c, time_t t, int utf8flag,
return (right);
}
/* Set window at window list position. */
void
status_set_window_at(struct client *c, u_int x)
/* Get window at window list position. */
struct window *
status_get_window_at(struct client *c, u_int x)
{
struct session *s = c->session;
struct winlink *wl;
@ -130,12 +130,13 @@ status_set_window_at(struct client *c, u_int x)
x += c->wlmouse;
RB_FOREACH(wl, winlinks, &s->windows) {
oo = &wl->window->options;
len = strlen(options_get_string(oo, "window-status-separator"));
if (x < wl->status_width && session_select(s, wl->idx) == 0)
server_redraw_session(s);
if (x < wl->status_width)
return (wl->window);
x -= wl->status_width + len;
}
return (NULL);
}
/* Draw status for client on the last lines of given context. */

99
tmux.1
View File

@ -1019,13 +1019,16 @@ The synopsis for the
command is:
.Bl -tag -width Ds
.It Xo Ic copy-mode
.Op Fl u
.Op Fl Mu
.Op Fl t Ar target-pane
.Xc
Enter copy mode.
The
.Fl u
option scrolls one page up.
.Fl M
begins a mouse drag (only valid if bound to a mouse key binding, see
.Sx MOUSE SUPPORT Ns ).
.El
.Pp
Each window displayed by
@ -1643,7 +1646,7 @@ Rename the current window, or the window at
if specified, to
.Ar new-name .
.It Xo Ic resize-pane
.Op Fl DLRUZ
.Op Fl DLMRUZ
.Op Fl t Ar target-pane
.Op Fl x Ar width
.Op Fl y Ar height
@ -1672,6 +1675,10 @@ With
.Fl Z ,
the active pane is toggled between zoomed (occupying the whole of the window)
and unzoomed (its normal position in the layout).
.Pp
.Fl M
begins mouse resizing (only valid if bound to a mouse key binding, see
.Sx MOUSE SUPPORT Ns ).
.It Xo Ic respawn-pane
.Op Fl k
.Op Fl t Ar target-pane
@ -1980,7 +1987,7 @@ are listed; this may be one of:
or
.Em emacs-copy .
.It Xo Ic send-keys
.Op Fl lR
.Op Fl lMR
.Op Fl t Ar target-pane
.Ar key Ar ...
.Xc
@ -2001,6 +2008,10 @@ All arguments are sent sequentially from first to last.
The
.Fl R
flag causes the terminal state to be reset.
.Pp
.Fl M
passes through a mouse event (only valid if bound to a mouse key binding, see
.Sx MOUSE SUPPORT Ns ).
.It Xo Ic send-prefix
.Op Fl 2
.Op Fl t Ar target-pane
@ -2449,25 +2460,15 @@ For how to specify
see the
.Ic message-command-style
option.
.It Xo Ic mouse-resize-pane
.It Xo Ic mouse
.Op Ic on | off
.Xc
If on,
.Nm
captures the mouse and allows panes to be resized by dragging on their borders.
.It Xo Ic mouse-select-pane
.Op Ic on | off
.Xc
If on,
.Nm
captures the mouse and when a window is split into multiple panes the mouse may
be used to select the current pane.
The mouse click is also passed through to the application as normal.
.It Xo Ic mouse-select-window
.Op Ic on | off
.Xc
If on, clicking the mouse on a window name in the status line will select that
window.
captures the mouse and allows mouse events to be bound as key bindings.
See the
.Sx MOUSE SUPPORT
section for details.
.It Xo Ic mouse-utf8
.Op Ic on | off
.Xc
@ -2855,18 +2856,6 @@ or
contains
.Ql vi .
.Pp
.It Xo Ic mode-mouse
.Op Ic on | off | copy-mode
.Xc
Mouse state in modes.
If on, the mouse may be used to enter copy mode and copy a selection by
dragging, to enter copy mode and scroll with the mouse wheel, or to select an
option in choice mode.
If set to
.Em copy-mode ,
the mouse behaves as set to on, but cannot be used to enter copy
mode.
.Pp
.It Ic mode-style Ar style
Set window modes style.
For how to specify
@ -3083,6 +3072,56 @@ is used.
.Fl v
shows only the option value, not the name.
.El
.Sh MOUSE SUPPORT
If the
.Ic mouse
option is on (the default is off),
.Nm
allows mouse events to be bound as keys.
The name of each key is made up of a mouse event (such as
.Ql MouseUp1 )
and a location suffix (one of
.Ql Pane
for the contents of a pane,
.Ql Border
for a pane border or
.Ql Status
for the status line).
The following mouse events are available:
.Bl -column "MouseDown1" "MouseDrag1" "WheelDown" -offset indent
.It Li "MouseDown1" Ta "MouseUp1" Ta "MouseDrag1"
.It Li "MouseDown2" Ta "MouseUp2" Ta "MouseDrag2"
.It Li "MouseDown3" Ta "MouseUp3" Ta "MouseDrag3"
.It Li "WheelUp" Ta "WheelDown"
.El
.Pp
Each should be suffixed with a location, for example
.Ql MouseDown1Status .
.Pp
The special character
.Ql =
may be used as
.Ar target-window
or
.Ar target-pane
in commands bound to mouse key bindings.
It resolves to the window or pane over which the mouse event took place
(for example, the window in the status line over which button 1 was released for a
.Ql MouseUp1Status
binding, or the pane over which the wheel was scrolled for a
.Ql WheelDownPane
binding).
.Pp
The
.Ic send-keys
.Fl M
flag may be used to forward a mouse event to a pane.
.Pp
The default key bindings allow the mouse to be used to select and resize panes,
to copy text and to change window using the status line.
These take effect if the
.Ic mouse
option is turned on.
.Sh FORMATS
Certain commands accept the
.Fl F

140
tmux.h
View File

@ -95,10 +95,39 @@ extern char **environ;
#define KEYC_MASK_MOD (KEYC_ESCAPE|KEYC_CTRL|KEYC_SHIFT|KEYC_PREFIX)
#define KEYC_MASK_KEY (~KEYC_MASK_MOD)
/* Other key codes. */
/* Is this a mouse key? */
#define KEYC_IS_MOUSE(key) (((key) & KEYC_MASK_KEY) >= KEYC_MOUSE && \
((key) & KEYC_MASK_KEY) < KEYC_BSPACE)
/* Mouse key codes. */
#define KEYC_MOUSE_KEY(name) \
KEYC_ ## name ## _PANE, \
KEYC_ ## name ## _STATUS, \
KEYC_ ## name ## _BORDER
#define KEYC_MOUSE_STRING(name, s) \
{ #s "Pane", KEYC_ ## name ## _PANE }, \
{ #s "Status", KEYC_ ## name ## _STATUS }, \
{ #s "Border", KEYC_ ## name ## _BORDER }
/* Special key codes. */
enum key_code {
/* Mouse key. */
KEYC_MOUSE = KEYC_BASE,
/* Focus events. */
KEYC_FOCUS_IN = KEYC_BASE,
KEYC_FOCUS_OUT,
/* Mouse keys. */
KEYC_MOUSE, /* unclassified mouse event */
KEYC_MOUSE_KEY(MOUSEDOWN1),
KEYC_MOUSE_KEY(MOUSEDOWN2),
KEYC_MOUSE_KEY(MOUSEDOWN3),
KEYC_MOUSE_KEY(MOUSEUP1),
KEYC_MOUSE_KEY(MOUSEUP2),
KEYC_MOUSE_KEY(MOUSEUP3),
KEYC_MOUSE_KEY(MOUSEDRAG1),
KEYC_MOUSE_KEY(MOUSEDRAG2),
KEYC_MOUSE_KEY(MOUSEDRAG3),
KEYC_MOUSE_KEY(WHEELUP),
KEYC_MOUSE_KEY(WHEELDOWN),
/* Backspace key. */
KEYC_BSPACE,
@ -147,9 +176,6 @@ enum key_code {
KEYC_KP_ENTER,
KEYC_KP_ZERO,
KEYC_KP_PERIOD,
KEYC_FOCUS_IN,
KEYC_FOCUS_OUT,
};
/* Termcap codes. */
@ -818,16 +844,15 @@ struct input_ctx {
* Window mode. Windows can be in several modes and this is used to call the
* right function to handle input and output.
*/
struct client;
struct session;
struct window;
struct mouse_event;
struct window_mode {
struct screen *(*init)(struct window_pane *);
void (*free)(struct window_pane *);
void (*resize)(struct window_pane *, u_int, u_int);
void (*key)(struct window_pane *, struct session *, int);
void (*mouse)(struct window_pane *,
struct session *, struct mouse_event *);
void (*key)(struct window_pane *, struct client *, struct session *,
int, struct mouse_event *);
void (*timer)(struct window_pane *);
};
@ -1114,54 +1139,35 @@ LIST_HEAD(tty_terms, tty_term);
/* Mouse wheel states. */
#define MOUSE_WHEEL_UP 0
#define MOUSE_WHEEL_DOWN 1
#define MOUSE_WHEEL_DOWN 64
/* Mouse wheel multipler. */
#define MOUSE_WHEEL_SCALE 3
/* Mouse helpers. */
#define MOUSE_BUTTONS(b) ((b) & MOUSE_MASK_BUTTONS)
#define MOUSE_WHEEL(b) ((b) & MOUSE_MASK_WHEEL)
#define MOUSE_DRAG(b) ((b) & MOUSE_MASK_DRAG)
#define MOUSE_RELEASE(b) (((b) & MOUSE_MASK_BUTTONS) == 3)
/* Mouse event bits. */
#define MOUSE_EVENT_DOWN 0x1
#define MOUSE_EVENT_DRAG 0x2
#define MOUSE_EVENT_UP 0x4
#define MOUSE_EVENT_CLICK 0x8
#define MOUSE_EVENT_WHEEL 0x10
/* Mouse flag bits. */
#define MOUSE_RESIZE_PANE 0x1
/*
* Mouse input. When sent by xterm:
*
* - buttons are in the bottom two bits: 0 = b1; 1 = b2; 2 = b3; 3 = released
* - bits 3, 4 and 5 are for keys
* - bit 6 is set for dragging
* - bit 7 for buttons 4 and 5
*
* With the SGR 1006 extension the released button becomes known. Store these
* in separate fields and store the value converted to the old format in xb.
*/
/* Mouse input. */
struct mouse_event {
u_int xb;
int valid;
int key;
int statusat;
u_int x;
u_int lx;
u_int sx;
u_int y;
u_int b;
u_int lx;
u_int ly;
u_int sy;
u_int lb;
u_int sgr; /* whether the input arrived in SGR format */
u_int sgr_xb; /* only for SGR: the unmangled button */
u_int sgr_rel; /* only for SGR: if it is a release event */
int s;
int w;
int wp;
u_int button;
u_int clicks;
u_int scroll;
int wheel;
int event;
int flags;
u_int sgr_type;
u_int sgr_b;
};
struct tty {
@ -1207,6 +1213,11 @@ struct tty {
int term_flags;
struct mouse_event mouse;
int mouse_drag_flag;
void (*mouse_drag_update)(struct client *,
struct mouse_event *);
void (*mouse_drag_release)(struct client *,
struct mouse_event *);
struct event key_timer;
struct tty_key *key_tree;
@ -1382,6 +1393,9 @@ enum cmd_retval {
/* Command queue entry. */
struct cmd_q_item {
struct cmd_list *cmdlist;
struct mouse_event mouse;
TAILQ_ENTRY(cmd_q_item) qentry;
};
TAILQ_HEAD(cmd_q_items, cmd_q_item);
@ -1723,6 +1737,11 @@ void cmd_free_argv(int, char **);
char *cmd_stringify_argv(int, char **);
struct cmd *cmd_parse(int, char **, const char *, u_int, char **);
size_t cmd_print(struct cmd *, char *, size_t);
int cmd_mouse_at(struct window_pane *, struct mouse_event *,
u_int *, u_int *, int);
struct winlink *cmd_mouse_window(struct mouse_event *, struct session **);
struct window_pane *cmd_mouse_pane(struct mouse_event *, struct session **,
struct winlink **);
struct session *cmd_current_session(struct cmd_q *, int);
struct client *cmd_current_client(struct cmd_q *);
struct client *cmd_find_client(struct cmd_q *, const char *, int);
@ -1839,8 +1858,10 @@ int cmdq_free(struct cmd_q *);
void printflike(2, 3) cmdq_print(struct cmd_q *, const char *, ...);
void printflike(2, 3) cmdq_error(struct cmd_q *, const char *, ...);
void cmdq_guard(struct cmd_q *, const char *, int);
void cmdq_run(struct cmd_q *, struct cmd_list *);
void cmdq_append(struct cmd_q *, struct cmd_list *);
void cmdq_run(struct cmd_q *, struct cmd_list *,
struct mouse_event *);
void cmdq_append(struct cmd_q *, struct cmd_list *,
struct mouse_event *);
int cmdq_continue(struct cmd_q *);
void cmdq_flush(struct cmd_q *);
@ -1862,7 +1883,8 @@ struct key_binding *key_bindings_lookup(int);
void key_bindings_add(int, int, struct cmd_list *);
void key_bindings_remove(int);
void key_bindings_init(void);
void key_bindings_dispatch(struct key_binding *, struct client *);
void key_bindings_dispatch(struct key_binding *, struct client *,
struct mouse_event *);
/* key-string.c */
int key_string_lookup_string(const char *);
@ -1930,7 +1952,7 @@ RB_PROTOTYPE(status_out_tree, status_out, entry, status_out_cmp);
int status_at_line(struct client *);
void status_free_jobs(struct status_out_tree *);
void status_update_jobs(struct client *);
void status_set_window_at(struct client *, u_int);
struct window *status_get_window_at(struct client *, u_int);
int status_redraw(struct client *);
void printflike(2, 3) status_message_set(struct client *, const char *, ...);
void status_message_clear(struct client *);
@ -1951,9 +1973,7 @@ void input_free(struct window_pane *);
void input_parse(struct window_pane *);
/* input-key.c */
void input_key(struct window_pane *, int);
void input_mouse(struct window_pane *, struct session *,
struct mouse_event *);
void input_key(struct window_pane *, int, struct mouse_event *);
/* xterm-keys.c */
char *xterm_keys_lookup(int);
@ -2118,6 +2138,7 @@ void window_destroy(struct window *);
struct window_pane *window_get_active_at(struct window *, u_int, u_int);
void window_set_active_at(struct window *, u_int, u_int);
struct window_pane *window_find_string(struct window *, const char *);
int window_has_pane(struct window *, struct window_pane *);
int window_set_active_pane(struct window *, struct window_pane *);
struct window_pane *window_add_pane(struct window *, u_int);
void window_resize(struct window *, u_int, u_int);
@ -2148,9 +2169,8 @@ void window_pane_alternate_off(struct window_pane *,
int window_pane_set_mode(
struct window_pane *, const struct window_mode *);
void window_pane_reset_mode(struct window_pane *);
void window_pane_key(struct window_pane *, struct session *, int);
void window_pane_mouse(struct window_pane *,
struct session *, struct mouse_event *);
void window_pane_key(struct window_pane *, struct client *,
struct session *, int, struct mouse_event *);
int window_pane_visible(struct window_pane *);
char *window_pane_search(
struct window_pane *, const char *, u_int *);
@ -2186,7 +2206,6 @@ void layout_resize_pane(struct window_pane *, enum layout_type,
int);
void layout_resize_pane_to(struct window_pane *, enum layout_type,
u_int);
void layout_resize_pane_mouse(struct client *);
void layout_assign_pane(struct layout_cell *, struct window_pane *);
struct layout_cell *layout_split_pane(
struct window_pane *, enum layout_type, int, int);
@ -2215,6 +2234,7 @@ void window_copy_init_for_output(struct window_pane *);
void printflike(2, 3) window_copy_add(struct window_pane *, const char *, ...);
void window_copy_vadd(struct window_pane *, const char *, va_list);
void window_copy_pageup(struct window_pane *);
void window_copy_start_drag(struct client *, struct mouse_event *);
/* window-choose.c */
extern const struct window_mode window_choose_mode;

View File

@ -644,8 +644,8 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size)
{
struct mouse_event *m = &tty->mouse;
struct utf8_data utf8data;
u_int i, value, x, y, b, sgr, sgr_b, sgr_rel;
unsigned char c;
u_int i, value, x, y, b, sgr_b;
u_char sgr_type, c;
/*
* Standard mouse sequences are \033[M followed by three characters
@ -661,7 +661,8 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size)
*/
*size = 0;
x = y = b = sgr = sgr_b = sgr_rel = 0;
x = y = b = sgr_b = 0;
sgr_type = ' ';
/* First two bytes are always \033[. */
if (buf[0] != '\033')
@ -708,7 +709,7 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size)
else
y = value;
}
log_debug("mouse input: %.*s", (int) *size, buf);
log_debug("mouse input: %.*s", (int)*size, buf);
/* Check and return the mouse input. */
if (b < 32)
@ -748,22 +749,26 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size)
while (1) {
if (len <= *size)
return (1);
c = (u_char) buf[(*size)++];
c = (u_char)buf[(*size)++];
if (c == 'M' || c == 'm')
break;
if (c < '0' || c > '9')
return (-1);
y = 10 * y + (c - '0');
}
log_debug("mouse input (sgr): %.*s", (int) *size, buf);
log_debug("mouse input (SGR): %.*s", (int)*size, buf);
/* Check and return the mouse input. */
if (x < 1 || y < 1)
return (-1);
x--;
y--;
sgr = 1;
sgr_rel = (c == 'm');
b = sgr_b;
/* Type is M for press, m for release. */
sgr_type = c;
if (sgr_type == 'm')
b |= 3;
/*
* Some terminals (like PuTTY 0.63) mistakenly send
@ -771,64 +776,20 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size)
* Discard it before it reaches any program running inside
* tmux.
*/
if (sgr_rel && (sgr_b & 64))
if (sgr_type == 'm' && (sgr_b & 64))
return (-2);
/* Figure out what b would be in old format. */
b = sgr_b;
if (sgr_rel)
b |= 3;
} else
return (-1);
/* Fill in mouse structure. */
if (~m->event & MOUSE_EVENT_WHEEL) {
m->lx = m->x;
m->ly = m->y;
}
m->xb = b;
m->sgr = sgr;
m->sgr_xb = sgr_b;
m->sgr_rel = sgr_rel;
/* Fill mouse event. */
m->lx = m->x;
m->x = x;
m->ly = m->y;
m->y = y;
if (b & MOUSE_MASK_WHEEL) {
if (b & MOUSE_MASK_SHIFT)
m->scroll = 1;
else
m->scroll = MOUSE_WHEEL_SCALE;
if (b & MOUSE_MASK_META)
m->scroll *= MOUSE_WHEEL_SCALE;
if (b & MOUSE_MASK_CTRL)
m->scroll *= MOUSE_WHEEL_SCALE;
b &= MOUSE_MASK_BUTTONS;
if (b == 0)
m->wheel = MOUSE_WHEEL_UP;
else if (b == 1)
m->wheel = MOUSE_WHEEL_DOWN;
m->event = MOUSE_EVENT_WHEEL;
m->button = 3;
} else if ((b & MOUSE_MASK_BUTTONS) == 3) {
if (~m->event & MOUSE_EVENT_DRAG && x == m->sx && y == m->sy) {
m->event = MOUSE_EVENT_CLICK;
m->clicks = (m->clicks + 1) % 3;
} else
m->event = MOUSE_EVENT_DRAG;
m->event |= MOUSE_EVENT_UP;
} else {
if (b & MOUSE_MASK_DRAG)
m->event = MOUSE_EVENT_DRAG;
else {
m->event = MOUSE_EVENT_DOWN;
if (x != m->sx || y != m->sy)
m->clicks = 0;
}
m->button = (b & MOUSE_MASK_BUTTONS);
}
m->sx = x;
m->sy = y;
m->lb = m->b;
m->b = b;
m->sgr_type = sgr_type;
m->sgr_b = sgr_b;
return (0);
}

4
tty.c
View File

@ -241,6 +241,10 @@ tty_start_tty(struct tty *tty)
tty->flags |= TTY_STARTED;
tty_force_cursor_colour(tty, "");
tty->mouse_drag_flag = 0;
tty->mouse_drag_update = NULL;
tty->mouse_drag_release = NULL;
}
void

View File

@ -27,11 +27,12 @@
struct screen *window_choose_init(struct window_pane *);
void window_choose_free(struct window_pane *);
void window_choose_resize(struct window_pane *, u_int, u_int);
void window_choose_key(struct window_pane *, struct session *, int);
void window_choose_mouse(
struct window_pane *, struct session *, struct mouse_event *);
void window_choose_key(struct window_pane *, struct client *,
struct session *, int, struct mouse_event *);
void window_choose_default_callback(struct window_choose_data *);
struct window_choose_mode_item *window_choose_get_item(struct window_pane *,
int, struct mouse_event *);
void window_choose_fire_callback(
struct window_pane *, struct window_choose_data *);
@ -42,7 +43,7 @@ void window_choose_write_line(
void window_choose_scroll_up(struct window_pane *);
void window_choose_scroll_down(struct window_pane *);
void window_choose_collapse(struct window_pane *, struct session *);
void window_choose_collapse(struct window_pane *, struct session *, u_int);
void window_choose_expand(struct window_pane *, struct session *, u_int);
enum window_choose_input_type {
@ -55,7 +56,6 @@ const struct window_mode window_choose_mode = {
window_choose_free,
window_choose_resize,
window_choose_key,
window_choose_mouse,
NULL,
};
@ -160,8 +160,6 @@ window_choose_init(struct window_pane *wp)
s = &data->screen;
screen_init(s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0);
s->mode &= ~MODE_CURSOR;
if (options_get_number(&wp->window->options, "mode-mouse"))
s->mode |= MODE_MOUSE_STANDARD;
keys = options_get_number(&wp->window->options, "mode-keys");
if (keys == MODEKEY_EMACS)
@ -237,7 +235,7 @@ window_choose_data_run(struct window_choose_data *cdata)
return;
}
cmdq_run(cdata->start_client->cmdq, cmdlist);
cmdq_run(cdata->start_client->cmdq, cmdlist, NULL);
cmd_list_free(cmdlist);
}
@ -325,7 +323,7 @@ window_choose_prompt_input(enum window_choose_input_type input_type,
}
void
window_choose_collapse(struct window_pane *wp, struct session *s)
window_choose_collapse(struct window_pane *wp, struct session *s, u_int pos)
{
struct window_choose_mode_data *data = wp->modedata;
struct window_choose_mode_item *item, *chosen;
@ -335,7 +333,7 @@ window_choose_collapse(struct window_pane *wp, struct session *s)
ARRAY_DECL(, struct window_choose_mode_item) list_copy;
ARRAY_INIT(&list_copy);
chosen = &ARRAY_ITEM(&data->list, data->selected);
chosen = &ARRAY_ITEM(&data->list, pos);
chosen->state &= ~TREE_EXPANDED;
/*
@ -383,7 +381,7 @@ window_choose_collapse_all(struct window_pane *wp)
chosen = ARRAY_ITEM(&data->list, data->selected).wcd->start_session;
RB_FOREACH(s, sessions, &sessions)
window_choose_collapse(wp, s);
window_choose_collapse(wp, s, data->selected);
/* Reset the selection back to the starting session. */
for (i = 0; i < ARRAY_LENGTH(&data->list); i++) {
@ -483,8 +481,27 @@ window_choose_expand(struct window_pane *wp, struct session *s, u_int pos)
}
}
struct window_choose_mode_item *
window_choose_get_item(struct window_pane *wp, int key, struct mouse_event *m)
{
struct window_choose_mode_data *data = wp->modedata;
u_int x, y, idx;
if (!KEYC_IS_MOUSE(key))
return (&ARRAY_ITEM(&data->list, data->selected));
if (cmd_mouse_at(wp, m, &x, &y, 0) != 0)
return (NULL);
idx = data->top + y;
if (idx >= ARRAY_LENGTH(&data->list))
return (NULL);
return (&ARRAY_ITEM(&data->list, idx));
}
void
window_choose_key(struct window_pane *wp, unused struct session *sess, int key)
window_choose_key(struct window_pane *wp, unused struct client *c,
unused struct session *sess, int key, struct mouse_event *m)
{
struct window_choose_mode_data *data = wp->modedata;
struct screen *s = &data->screen;
@ -533,23 +550,28 @@ window_choose_key(struct window_pane *wp, unused struct session *sess, int key)
window_choose_fire_callback(wp, NULL);
break;
case MODEKEYCHOICE_CHOOSE:
item = &ARRAY_ITEM(&data->list, data->selected);
if ((item = window_choose_get_item(wp, key, m)) == NULL)
break;
window_choose_fire_callback(wp, item->wcd);
break;
case MODEKEYCHOICE_TREE_TOGGLE:
item = &ARRAY_ITEM(&data->list, data->selected);
if (item->state & TREE_EXPANDED)
window_choose_collapse(wp, item->wcd->tree_session);
else {
if ((item = window_choose_get_item(wp, key, m)) == NULL)
break;
if (item->state & TREE_EXPANDED) {
window_choose_collapse(wp, item->wcd->tree_session,
item->wcd->idx);
} else {
window_choose_expand(wp, item->wcd->tree_session,
data->selected);
item->wcd->idx);
}
window_choose_redraw_screen(wp);
break;
case MODEKEYCHOICE_TREE_COLLAPSE:
item = &ARRAY_ITEM(&data->list, data->selected);
if ((item = window_choose_get_item(wp, key, m)) == NULL)
break;
if (item->state & TREE_EXPANDED) {
window_choose_collapse(wp, item->wcd->tree_session);
window_choose_collapse(wp, item->wcd->tree_session,
data->selected);
window_choose_redraw_screen(wp);
}
break;
@ -557,7 +579,8 @@ window_choose_key(struct window_pane *wp, unused struct session *sess, int key)
window_choose_collapse_all(wp);
break;
case MODEKEYCHOICE_TREE_EXPAND:
item = &ARRAY_ITEM(&data->list, data->selected);
if ((item = window_choose_get_item(wp, key, m)) == NULL)
break;
if (!(item->state & TREE_EXPANDED)) {
window_choose_expand(wp, item->wcd->tree_session,
data->selected);
@ -711,48 +734,6 @@ window_choose_key(struct window_pane *wp, unused struct session *sess, int key)
}
}
void
window_choose_mouse(struct window_pane *wp, struct session *sess,
struct mouse_event *m)
{
struct window_choose_mode_data *data = wp->modedata;
struct screen *s = &data->screen;
struct window_choose_mode_item *item;
u_int idx, i, n;
if (m->event == MOUSE_EVENT_WHEEL) {
/*
* Multiple line scrolling by default is annoying, so scale
* m->scroll back down.
*/
n = m->scroll;
if (n >= MOUSE_WHEEL_SCALE)
n /= MOUSE_WHEEL_SCALE;
for (i = 0; i < n; i++) {
if (m->wheel == MOUSE_WHEEL_UP)
window_choose_key(wp, sess, KEYC_UP);
else
window_choose_key(wp, sess, KEYC_DOWN);
}
return;
}
if (~m->event & MOUSE_EVENT_CLICK)
return;
if (m->x >= screen_size_x(s))
return;
if (m->y >= screen_size_y(s))
return;
idx = data->top + m->y;
if (idx >= ARRAY_LENGTH(&data->list))
return;
data->selected = idx;
item = &ARRAY_ITEM(&data->list, data->selected);
window_choose_fire_callback(wp, item->wcd);
}
void
window_choose_write_line(
struct window_pane *wp, struct screen_write_ctx *ctx, u_int py)

View File

@ -27,7 +27,8 @@
struct screen *window_clock_init(struct window_pane *);
void window_clock_free(struct window_pane *);
void window_clock_resize(struct window_pane *, u_int, u_int);
void window_clock_key(struct window_pane *, struct session *, int);
void window_clock_key(struct window_pane *, struct client *,
struct session *, int, struct mouse_event *);
void window_clock_timer(struct window_pane *);
void window_clock_draw_screen(struct window_pane *);
@ -37,7 +38,6 @@ const struct window_mode window_clock_mode = {
window_clock_free,
window_clock_resize,
window_clock_key,
NULL,
window_clock_timer,
};
@ -157,8 +157,8 @@ window_clock_resize(struct window_pane *wp, u_int sx, u_int sy)
}
void
window_clock_key(
struct window_pane *wp, unused struct session *sess, unused int key)
window_clock_key(struct window_pane *wp, unused struct client *c,
unused struct session *sess, unused int key, unused struct mouse_event *m)
{
window_pane_reset_mode(wp);
}

View File

@ -27,11 +27,10 @@
struct screen *window_copy_init(struct window_pane *);
void window_copy_free(struct window_pane *);
void window_copy_resize(struct window_pane *, u_int, u_int);
void window_copy_key(struct window_pane *, struct session *, int);
void window_copy_key(struct window_pane *, struct client *, struct session *,
int, struct mouse_event *);
int window_copy_key_input(struct window_pane *, int);
int window_copy_key_numeric_prefix(struct window_pane *, int);
void window_copy_mouse(struct window_pane *, struct session *,
struct mouse_event *);
void window_copy_redraw_selection(struct window_pane *, u_int);
void window_copy_redraw_lines(struct window_pane *, u_int, u_int);
@ -84,13 +83,14 @@ 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_drag_update(struct client *, struct mouse_event *);
void window_copy_drag_release(struct client *, struct mouse_event *);
const struct window_mode window_copy_mode = {
window_copy_init,
window_copy_free,
window_copy_resize,
window_copy_key,
window_copy_mouse,
NULL,
};
@ -124,38 +124,38 @@ enum window_copy_input_type {
* mode ends).
*/
struct window_copy_mode_data {
struct screen screen;
struct screen screen;
struct screen *backing;
int backing_written; /* backing display has started */
struct screen *backing;
int backing_written; /* backing display started */
struct mode_key_data mdata;
struct mode_key_data mdata;
u_int oy;
u_int oy;
u_int selx;
u_int sely;
u_int selx;
u_int sely;
u_int rectflag; /* are we in rectangle copy mode? */
u_int rectflag; /* are we in rectangle copy mode? */
u_int cx;
u_int cy;
u_int cx;
u_int cy;
u_int lastcx; /* position in last line with content */
u_int lastsx; /* size of last line with content */
u_int lastcx; /* position in last line w/ content */
u_int lastsx; /* size of last line w/ content */
enum window_copy_input_type inputtype;
const char *inputprompt;
char *inputstr;
int inputexit;
const char *inputprompt;
char *inputstr;
int inputexit;
int numprefix;
int numprefix;
enum window_copy_input_type searchtype;
char *searchstr;
char *searchstr;
enum window_copy_input_type jumptype;
char jumpchar;
char jumpchar;
};
struct screen *
@ -193,8 +193,6 @@ window_copy_init(struct window_pane *wp)
s = &data->screen;
screen_init(s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0);
if (options_get_number(&wp->window->options, "mode-mouse"))
s->mode |= MODE_MOUSE_STANDARD;
keys = options_get_number(&wp->window->options, "mode-keys");
if (keys == MODEKEY_EMACS)
@ -367,19 +365,20 @@ window_copy_resize(struct window_pane *wp, u_int sx, u_int sy)
}
void
window_copy_key(struct window_pane *wp, struct session *sess, int key)
window_copy_key(struct window_pane *wp, struct client *c, struct session *sess,
int key, struct mouse_event *m)
{
const char *word_separators;
struct window_copy_mode_data *data = wp->modedata;
struct screen *s = &data->screen;
u_int n;
int np, keys;
u_int n, np;
int keys;
enum mode_key_cmd cmd;
const char *arg, *ss;
np = data->numprefix;
if (np <= 0)
np = 1;
np = 1;
if (data->numprefix > 0)
np = data->numprefix;
if (data->inputtype == WINDOW_COPY_JUMPFORWARD ||
data->inputtype == WINDOW_COPY_JUMPBACK ||
@ -536,9 +535,14 @@ window_copy_key(struct window_pane *wp, struct session *sess, int key)
window_copy_redraw_screen(wp);
break;
case MODEKEYCOPY_STARTSELECTION:
s->sel.lineflag = LINE_SEL_NONE;
window_copy_start_selection(wp);
window_copy_redraw_screen(wp);
if (KEYC_IS_MOUSE(key)) {
if (c != NULL)
window_copy_start_drag(c, m);
} else {
s->sel.lineflag = LINE_SEL_NONE;
window_copy_start_selection(wp);
window_copy_redraw_screen(wp);
}
break;
case MODEKEYCOPY_SELECTLINE:
s->sel.lineflag = LINE_SEL_LEFT_RIGHT;
@ -887,75 +891,6 @@ window_copy_key_numeric_prefix(struct window_pane *wp, int key)
return (0);
}
void
window_copy_mouse(struct window_pane *wp, struct session *sess,
struct mouse_event *m)
{
struct window_copy_mode_data *data = wp->modedata;
struct screen *s = &data->screen;
u_int i, old_cy;
if (m->x >= screen_size_x(s))
return;
if (m->y >= screen_size_y(s))
return;
/* If mouse wheel (buttons 4 and 5), scroll. */
if (m->event == MOUSE_EVENT_WHEEL) {
for (i = 0; i < m->scroll; i++) {
if (m->wheel == MOUSE_WHEEL_UP)
window_copy_cursor_up(wp, 1);
else {
window_copy_cursor_down(wp, 1);
/*
* We reached the bottom, leave copy mode, but
* only if no selection is in progress.
*/
if (data->oy == 0 && !s->sel.flag &&
s->sel.lineflag == LINE_SEL_NONE)
goto reset_mode;
}
}
return;
}
/*
* If already reading motion, move the cursor while buttons are still
* pressed, or stop the selection on their release.
*/
if (s->mode & MODE_MOUSE_BUTTON) {
if (~m->event & MOUSE_EVENT_UP) {
old_cy = data->cy;
window_copy_update_cursor(wp, m->x, m->y);
if (window_copy_update_selection(wp, 1))
window_copy_redraw_selection(wp, old_cy);
return;
}
goto reset_mode;
}
/* Otherwise if other buttons pressed, start selection and motion. */
if (~m->event & MOUSE_EVENT_UP) {
s->mode &= ~MODE_MOUSE_STANDARD;
s->mode |= MODE_MOUSE_BUTTON;
window_copy_update_cursor(wp, m->x, m->y);
window_copy_start_selection(wp);
window_copy_redraw_screen(wp);
}
return;
reset_mode:
s->mode &= ~MODE_MOUSE_BUTTON;
s->mode |= MODE_MOUSE_STANDARD;
if (sess != NULL) {
window_copy_copy_selection(wp, NULL);
window_pane_reset_mode(wp);
}
}
void
window_copy_scroll_to(struct window_pane *wp, u_int px, u_int py)
{
@ -2274,3 +2209,62 @@ window_copy_rectangle_toggle(struct window_pane *wp)
window_copy_update_selection(wp, 1);
window_copy_redraw_screen(wp);
}
void
window_copy_start_drag(struct client *c, unused struct mouse_event *m)
{
struct window_pane *wp;
struct window_copy_mode_data *data;
u_int x, y;
wp = cmd_mouse_pane(m, NULL, NULL);
if (wp->mode != &window_copy_mode)
return;
data = wp->modedata;
if (cmd_mouse_at(wp, m, &x, &y, 1) != 0)
return;
c->tty.mouse_drag_update = window_copy_drag_update;
c->tty.mouse_drag_release = window_copy_drag_release;
window_copy_update_cursor(wp, x, y);
window_copy_start_selection(wp);
window_copy_redraw_screen(wp);
}
void
window_copy_drag_update(unused struct client *c, struct mouse_event *m)
{
struct window_pane *wp;
struct window_copy_mode_data *data;
u_int x, y, old_cy;
wp = cmd_mouse_pane(m, NULL, NULL);
if (wp->mode != &window_copy_mode)
return;
data = wp->modedata;
if (cmd_mouse_at(wp, m, &x, &y, 0) != 0)
return;
old_cy = data->cy;
window_copy_update_cursor(wp, x, y);
if (window_copy_update_selection(wp, 1))
window_copy_redraw_selection(wp, old_cy);
}
void
window_copy_drag_release(unused struct client *c, struct mouse_event *m)
{
struct window_pane *wp;
struct window_copy_mode_data *data;
wp = cmd_mouse_pane(m, NULL, NULL);
if (wp->mode != &window_copy_mode)
return;
data = wp->modedata;
window_copy_copy_selection(wp, NULL);
window_pane_reset_mode(wp);
}

View File

@ -386,6 +386,18 @@ window_resize(struct window *w, u_int sx, u_int sy)
w->sy = sy;
}
int
window_has_pane(struct window *w, struct window_pane *wp)
{
struct window_pane *wp1;
TAILQ_FOREACH(wp1, &w->panes, entry) {
if (wp1 == wp)
return (1);
}
return (0);
}
int
window_set_active_pane(struct window *w, struct window_pane *wp)
{
@ -1052,52 +1064,37 @@ window_pane_reset_mode(struct window_pane *wp)
}
void
window_pane_key(struct window_pane *wp, struct session *sess, int key)
window_pane_key(struct window_pane *wp, struct client *c, struct session *s,
int key, struct mouse_event *m)
{
struct window_pane *wp2;
if (KEYC_IS_MOUSE(key) && m == NULL)
return;
if (wp->mode != NULL) {
if (wp->mode->key != NULL)
wp->mode->key(wp, sess, key);
wp->mode->key(wp, c, s, key, m);
return;
}
if (wp->fd == -1 || wp->flags & PANE_INPUTOFF)
return;
input_key(wp, key);
input_key(wp, key, m);
if (KEYC_IS_MOUSE(key))
return;
if (options_get_number(&wp->window->options, "synchronize-panes")) {
TAILQ_FOREACH(wp2, &wp->window->panes, entry) {
if (wp2 == wp || wp2->mode != NULL)
continue;
if (wp2->fd != -1 && window_pane_visible(wp2))
input_key(wp2, key);
input_key(wp2, key, NULL);
}
}
}
void
window_pane_mouse(struct window_pane *wp, struct session *sess,
struct mouse_event *m)
{
if (!window_pane_visible(wp))
return;
if (m->x < wp->xoff || m->x >= wp->xoff + wp->sx)
return;
if (m->y < wp->yoff || m->y >= wp->yoff + wp->sy)
return;
m->x -= wp->xoff;
m->y -= wp->yoff;
if (wp->mode != NULL) {
if (wp->mode->mouse != NULL &&
options_get_number(&wp->window->options, "mode-mouse"))
wp->mode->mouse(wp, sess, m);
} else if (wp->fd != -1)
input_mouse(wp, sess, m);
}
int
window_pane_visible(struct window_pane *wp)
{