From ee123c248951450100475717f5bd45f292d9bb4d Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 19 Apr 2015 21:05:27 +0000 Subject: [PATCH 1/6] Support setting the default window and pane background colours (window and active pane via window-style and window-active-style options, an individual pane by a new select-pane -P flag). From J Raynor. --- cmd-select-pane.c | 20 +++++- options-table.c | 10 +++ screen-redraw.c | 20 +++--- server-client.c | 3 +- tmux.1 | 41 ++++++++++- tmux.h | 11 ++- tty-term.c | 1 + tty.c | 170 +++++++++++++++++++++++++++++++++------------- window.c | 2 + 9 files changed, 212 insertions(+), 66 deletions(-) diff --git a/cmd-select-pane.c b/cmd-select-pane.c index 5810eeab..c84b4149 100644 --- a/cmd-select-pane.c +++ b/cmd-select-pane.c @@ -28,8 +28,8 @@ enum cmd_retval cmd_select_pane_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_select_pane_entry = { "select-pane", "selectp", - "DdeLlRt:U", 0, 0, - "[-DdeLlRU] " CMD_TARGET_PANE_USAGE, + "DdegLlP:Rt:U", 0, 0, + "[-DdegLlRU] [-P style] " CMD_TARGET_PANE_USAGE, 0, cmd_select_pane_exec }; @@ -48,6 +48,7 @@ cmd_select_pane_exec(struct cmd *self, struct cmd_q *cmdq) struct args *args = self->args; struct winlink *wl; struct window_pane *wp; + const char *style; if (self->entry == &cmd_last_pane_entry || args_has(args, 'l')) { wl = cmd_find_window(cmdq, args_get(args, 't'), NULL); @@ -82,6 +83,21 @@ cmd_select_pane_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_ERROR); } + if (args_has(self->args, 'P') || args_has(self->args, 'g')) { + if (args_has(args, 'P')) { + style = args_get(args, 'P'); + if (style_parse(&grid_default_cell, &wp->colgc, + style) == -1) { + cmdq_error(cmdq, "bad style: %s", style); + return (CMD_RETURN_ERROR); + } + wp->flags |= PANE_REDRAW; + } + if (args_has(self->args, 'g')) + cmdq_print(cmdq, "%s", style_tostring(&wp->colgc)); + return (CMD_RETURN_NORMAL); + } + if (args_has(self->args, 'L')) wp = window_pane_find_left(wp); else if (args_has(self->args, 'R')) diff --git a/options-table.c b/options-table.c index fb231f7d..5e72d14f 100644 --- a/options-table.c +++ b/options-table.c @@ -668,6 +668,16 @@ const struct options_table_entry window_options_table[] = { .default_num = 0 /* overridden in main() */ }, + { .name = "window-active-style", + .type = OPTIONS_TABLE_STYLE, + .default_str = "default" + }, + + { .name = "window-style", + .type = OPTIONS_TABLE_STYLE, + .default_str = "default" + }, + { .name = "window-status-activity-attr", .type = OPTIONS_TABLE_ATTRIBUTES, .default_num = GRID_ATTR_REVERSE, diff --git a/screen-redraw.c b/screen-redraw.c index c2b2ece6..e3369b82 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -266,7 +266,7 @@ screen_redraw_pane(struct client *c, struct window_pane *wp) yoff++; for (i = 0; i < wp->sy; i++) - tty_draw_line(&c->tty, wp->screen, i, wp->xoff, yoff); + tty_draw_pane(&c->tty, wp, i, wp->xoff, yoff); tty_reset(&c->tty); } @@ -323,9 +323,9 @@ screen_redraw_draw_borders(struct client *c, int status, u_int top) small && i > msgx && j == msgy) continue; if (screen_redraw_check_active(i, j, type, w, wp)) - tty_attributes(tty, &active_gc); + tty_attributes(tty, &active_gc, NULL); else - tty_attributes(tty, &other_gc); + tty_attributes(tty, &other_gc, NULL); tty_cursor(tty, i, top + j); tty_putc(tty, CELL_BORDERS[type]); } @@ -333,7 +333,7 @@ screen_redraw_draw_borders(struct client *c, int status, u_int top) if (small) { memcpy(&msg_gc, &grid_default_cell, sizeof msg_gc); - tty_attributes(tty, &msg_gc); + tty_attributes(tty, &msg_gc, NULL); tty_cursor(tty, msgx, msgy); tty_puts(tty, msg); } @@ -346,15 +346,13 @@ screen_redraw_draw_panes(struct client *c, u_int top) struct window *w = c->session->curw->window; struct tty *tty = &c->tty; struct window_pane *wp; - struct screen *s; u_int i; TAILQ_FOREACH(wp, &w->panes, entry) { if (!window_pane_visible(wp)) continue; - s = wp->screen; for (i = 0; i < wp->sy; i++) - tty_draw_line(tty, s, i, wp->xoff, top + wp->yoff); + tty_draw_pane(tty, wp, i, wp->xoff, top + wp->yoff); if (c->flags & CLIENT_IDENTIFY) screen_redraw_draw_number(c, wp); } @@ -367,9 +365,9 @@ screen_redraw_draw_status(struct client *c, u_int top) struct tty *tty = &c->tty; if (top) - tty_draw_line(tty, &c->status, 0, 0, 0); + tty_draw_line(tty, NULL, &c->status, 0, 0, 0); else - tty_draw_line(tty, &c->status, 0, 0, tty->sy - 1); + tty_draw_line(tty, NULL, &c->status, 0, 0, tty->sy - 1); } /* Draw number on a pane. */ @@ -411,7 +409,7 @@ screen_redraw_draw_number(struct client *c, struct window_pane *wp) colour_set_bg(&gc, active_colour); else colour_set_bg(&gc, colour); - tty_attributes(tty, &gc); + tty_attributes(tty, &gc, wp); for (ptr = buf; *ptr != '\0'; ptr++) { if (*ptr < '0' || *ptr > '9') continue; @@ -438,7 +436,7 @@ draw_text: colour_set_fg(&gc, active_colour); else colour_set_fg(&gc, colour); - tty_attributes(tty, &gc); + tty_attributes(tty, &gc, wp); tty_puts(tty, buf); tty_cursor(tty, 0, 0); diff --git a/server-client.c b/server-client.c index 8373a0ef..352e8ab6 100644 --- a/server-client.c +++ b/server-client.c @@ -329,8 +329,7 @@ server_client_check_mouse(struct client *c, struct window_pane *wp) 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_status_window(wp->window); - server_redraw_window_borders(wp->window); + server_redraw_window(wp->window); wp = wp->window->active; /* may have changed */ } diff --git a/tmux.1 b/tmux.1 index abdeba54..a3dac237 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1730,14 +1730,17 @@ and .Ic previous-layout commands. .It Xo Ic select-pane -.Op Fl DdeLlRU +.Op Fl DdegLlRU +.Op Fl P Ar style .Op Fl t Ar target-pane .Xc .D1 (alias: Ic selectp ) Make pane .Ar target-pane the active pane in window -.Ar target-window . +.Ar target-window , +or set it's style (with +.Fl P ) . If one of .Fl D , .Fl L , @@ -1754,6 +1757,22 @@ command. enables or .Fl d disables input to the pane. +.Pp +Each pane has a style: by default the +.Ic window-style +and +.Ic window-active-style +options are used, +.Ic select-pane +.Fl P +sets the style for a single pane. +For example, to set the pane 1 background to red: +.Bd -literal -offset indent +select-pane -t:.1 -P 'bg=red' +.Ed +.Pp +.Fl g +shows the current pane style. .It Xo Ic select-window .Op Fl lnpT .Op Fl t Ar target-window @@ -2070,7 +2089,7 @@ also supports user options which are prefixed with a User options may have any name, so long as they are prefixed with .Ql \&@ , and be set to any string. -For example +For example: .Bd -literal -offset indent $ tmux setw -q @foo "abc123" $ tmux showw -v @foo @@ -2936,6 +2955,14 @@ Instructs .Nm to expect UTF-8 sequences to appear in this window. .Pp +.It Ic window-active-style Ar style +Set the style for the window's active pane. +For how to specify +.Ar style , +see the +.Ic message-command-style +option. +.Pp .It Ic window-status-activity-style Ar style Set status line style for windows with an activity alert. For how to specify @@ -2993,6 +3020,14 @@ see the .Ic message-command-style option. .Pp +.It Ic window-style Ar style +Set the default window style. +For how to specify +.Ar style , +see the +.Ic message-command-style +option. +.Pp .It Xo Ic xterm-keys .Op Ic on | off .Xc diff --git a/tmux.h b/tmux.h index 4e1ee1b3..19a8aa3a 100644 --- a/tmux.h +++ b/tmux.h @@ -156,6 +156,7 @@ enum key_code { enum tty_code_code { TTYC_AX = 0, TTYC_ACSC, /* acs_chars, ac */ + TTYC_BCE, /* back_color_erase, ut */ TTYC_BEL, /* bell, bl */ TTYC_BLINK, /* enter_blink_mode, mb */ TTYC_BOLD, /* enter_bold_mode, md */ @@ -902,6 +903,8 @@ struct window_pane { struct input_ctx ictx; + struct grid_cell colgc; + int pipe_fd; struct bufferevent *pipe_event; size_t pipe_off; @@ -1606,7 +1609,8 @@ void environ_push(struct environ *); /* tty.c */ void tty_init_termios(int, struct termios *, struct bufferevent *); void tty_raw(struct tty *, const char *); -void tty_attributes(struct tty *, const struct grid_cell *); +void tty_attributes(struct tty *, const struct grid_cell *, + const struct window_pane *); void tty_reset(struct tty *); void tty_region_pane(struct tty *, const struct tty_ctx *, u_int, u_int); void tty_region(struct tty *, u_int, u_int); @@ -1630,7 +1634,10 @@ void tty_stop_tty(struct tty *); void tty_set_title(struct tty *, const char *); void tty_update_mode(struct tty *, int, struct screen *); void tty_force_cursor_colour(struct tty *, const char *); -void tty_draw_line(struct tty *, struct screen *, u_int, u_int, u_int); +void tty_draw_pane(struct tty *, const struct window_pane *, u_int, u_int, + u_int); +void tty_draw_line(struct tty *, const struct window_pane *, struct screen *, + u_int, u_int, u_int); int tty_open(struct tty *, char **); void tty_close(struct tty *); void tty_free(struct tty *); diff --git a/tty-term.c b/tty-term.c index c8a6ba8b..f6f6b444 100644 --- a/tty-term.c +++ b/tty-term.c @@ -35,6 +35,7 @@ struct tty_terms tty_terms = LIST_HEAD_INITIALIZER(tty_terms); const struct tty_term_code_entry tty_term_codes[NTTYCODE] = { { TTYC_ACSC, TTYCODE_STRING, "acsc" }, { TTYC_AX, TTYCODE_FLAG, "AX" }, + { TTYC_BCE, TTYCODE_FLAG, "bce" }, { TTYC_BEL, TTYCODE_STRING, "bel" }, { TTYC_BLINK, TTYCODE_STRING, "blink" }, { TTYC_BOLD, TTYCODE_STRING, "bold" }, diff --git a/tty.c b/tty.c index 83ae8282..59ed3bb8 100644 --- a/tty.c +++ b/tty.c @@ -43,11 +43,14 @@ void tty_colours_fg(struct tty *, const struct grid_cell *); void tty_colours_bg(struct tty *, const struct grid_cell *); int tty_large_region(struct tty *, const struct tty_ctx *); +int tty_fake_bce(const struct tty *, const struct window_pane *); void tty_redraw_region(struct tty *, const struct tty_ctx *); void tty_emulate_repeat( struct tty *, enum tty_code_code, enum tty_code_code, u_int); void tty_repeat_space(struct tty *, u_int); -void tty_cell(struct tty *, const struct grid_cell *); +void tty_cell(struct tty *, const struct grid_cell *, + const struct window_pane *); +void tty_default_colours(struct grid_cell *, const struct window_pane *); #define tty_use_acs(tty) \ (tty_term_has((tty)->term, TTYC_ACSC) && !((tty)->flags & TTY_UTF8)) @@ -581,6 +584,23 @@ tty_large_region(unused struct tty *tty, const struct tty_ctx *ctx) return (ctx->orlower - ctx->orupper >= screen_size_y(wp->screen) / 2); } +/* + * Return if BCE is needed but the terminal doesn't have it - it'll need to be + * emulated. + */ +int +tty_fake_bce(const struct tty *tty, const struct window_pane *wp) +{ + struct grid_cell gc; + + memcpy(&gc, &grid_default_cell, sizeof gc); + tty_default_colours(&gc, wp); + + if (gc.bg == 8 && !(gc.flags & GRID_FLAG_BG256)) + return (0); + return (!tty_term_flag(tty->term, TTYC_BCE)); +} + /* * Redraw scroll region using data from screen (already updated). Used when * CSR not supported, or window is a pane that doesn't take up the full @@ -604,15 +624,23 @@ tty_redraw_region(struct tty *tty, const struct tty_ctx *ctx) if (ctx->ocy < ctx->orupper || ctx->ocy > ctx->orlower) { for (i = ctx->ocy; i < screen_size_y(s); i++) - tty_draw_line(tty, s, i, ctx->xoff, ctx->yoff); + tty_draw_pane(tty, wp, i, ctx->xoff, ctx->yoff); } else { for (i = ctx->orupper; i <= ctx->orlower; i++) - tty_draw_line(tty, s, i, ctx->xoff, ctx->yoff); + tty_draw_pane(tty, wp, i, ctx->xoff, ctx->yoff); } } void -tty_draw_line(struct tty *tty, struct screen *s, u_int py, u_int ox, u_int oy) +tty_draw_pane(struct tty *tty, const struct window_pane *wp, u_int py, u_int ox, + u_int oy) +{ + tty_draw_line(tty, wp, wp->screen, py, ox, oy); +} + +void +tty_draw_line(struct tty *tty, const struct window_pane *wp, + struct screen *s, u_int py, u_int ox, u_int oy) { const struct grid_cell *gc; struct grid_line *gl; @@ -650,20 +678,20 @@ tty_draw_line(struct tty *tty, struct screen *s, u_int py, u_int ox, u_int oy) ~(GRID_FLAG_FG256|GRID_FLAG_BG256); tmpgc.flags |= s->sel.cell.flags & (GRID_FLAG_FG256|GRID_FLAG_BG256); - tty_cell(tty, &tmpgc); + tty_cell(tty, &tmpgc, wp); } else - tty_cell(tty, gc); + tty_cell(tty, gc, wp); } if (sx >= tty->sx) { tty_update_mode(tty, tty->mode, s); return; } - tty_reset(tty); + tty_attributes(tty, &grid_default_cell, wp); tty_cursor(tty, ox + sx, oy + py); if (sx != screen_size_x(s) && ox + screen_size_x(s) >= tty->sx && - tty_term_has(tty->term, TTYC_EL)) + tty_term_has(tty->term, TTYC_EL) && !tty_fake_bce(tty, wp)) tty_putcode(tty, TTYC_EL); else tty_repeat_space(tty, screen_size_x(s) - sx); @@ -713,19 +741,19 @@ tty_cmd_insertcharacter(struct tty *tty, const struct tty_ctx *ctx) struct window_pane *wp = ctx->wp; if (!tty_pane_full_width(tty, ctx)) { - tty_draw_line(tty, wp->screen, ctx->ocy, ctx->xoff, ctx->yoff); + tty_draw_pane(tty, wp, ctx->ocy, ctx->xoff, ctx->yoff); return; } - tty_reset(tty); + tty_attributes(tty, &grid_default_cell, wp); tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); - if (tty_term_has(tty->term, TTYC_ICH) || - tty_term_has(tty->term, TTYC_ICH1)) + if (!tty_fake_bce(tty, wp) && (tty_term_has(tty->term, TTYC_ICH) || + tty_term_has(tty->term, TTYC_ICH1))) tty_emulate_repeat(tty, TTYC_ICH, TTYC_ICH1, ctx->num); else - tty_draw_line(tty, wp->screen, ctx->ocy, ctx->xoff, ctx->yoff); + tty_draw_pane(tty, wp, ctx->ocy, ctx->xoff, ctx->yoff); } void @@ -733,14 +761,14 @@ tty_cmd_deletecharacter(struct tty *tty, const struct tty_ctx *ctx) { struct window_pane *wp = ctx->wp; - if (!tty_pane_full_width(tty, ctx) || + if (!tty_pane_full_width(tty, ctx) || tty_fake_bce(tty, wp) || (!tty_term_has(tty->term, TTYC_DCH) && !tty_term_has(tty->term, TTYC_DCH1))) { - tty_draw_line(tty, wp->screen, ctx->ocy, ctx->xoff, ctx->yoff); + tty_draw_pane(tty, wp, ctx->ocy, ctx->xoff, ctx->yoff); return; } - tty_reset(tty); + tty_attributes(tty, &grid_default_cell, wp); tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); @@ -754,11 +782,11 @@ tty_cmd_clearcharacter(struct tty *tty, const struct tty_ctx *ctx) { u_int i; - tty_reset(tty); + tty_attributes(tty, &grid_default_cell, ctx->wp); tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); - if (tty_term_has(tty->term, TTYC_ECH)) + if (tty_term_has(tty->term, TTYC_ECH) && !tty_fake_bce(tty, ctx->wp)) tty_putcode1(tty, TTYC_ECH, ctx->num); else { for (i = 0; i < ctx->num; i++) @@ -769,14 +797,14 @@ tty_cmd_clearcharacter(struct tty *tty, const struct tty_ctx *ctx) void tty_cmd_insertline(struct tty *tty, const struct tty_ctx *ctx) { - if (!tty_pane_full_width(tty, ctx) || + if (!tty_pane_full_width(tty, ctx) || tty_fake_bce(tty, ctx->wp) || !tty_term_has(tty->term, TTYC_CSR) || !tty_term_has(tty->term, TTYC_IL1)) { tty_redraw_region(tty, ctx); return; } - tty_reset(tty); + tty_attributes(tty, &grid_default_cell, ctx->wp); tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); @@ -787,14 +815,14 @@ tty_cmd_insertline(struct tty *tty, const struct tty_ctx *ctx) void tty_cmd_deleteline(struct tty *tty, const struct tty_ctx *ctx) { - if (!tty_pane_full_width(tty, ctx) || + if (!tty_pane_full_width(tty, ctx) || tty_fake_bce(tty, ctx->wp) || !tty_term_has(tty->term, TTYC_CSR) || !tty_term_has(tty->term, TTYC_DL1)) { tty_redraw_region(tty, ctx); return; } - tty_reset(tty); + tty_attributes(tty, &grid_default_cell, ctx->wp); tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); @@ -808,11 +836,12 @@ tty_cmd_clearline(struct tty *tty, const struct tty_ctx *ctx) struct window_pane *wp = ctx->wp; struct screen *s = wp->screen; - tty_reset(tty); + tty_attributes(tty, &grid_default_cell, wp); tty_cursor_pane(tty, ctx, 0, ctx->ocy); - if (tty_pane_full_width(tty, ctx) && tty_term_has(tty->term, TTYC_EL)) + if (tty_pane_full_width(tty, ctx) && !tty_fake_bce(tty, wp) && + tty_term_has(tty->term, TTYC_EL)) tty_putcode(tty, TTYC_EL); else tty_repeat_space(tty, screen_size_x(s)); @@ -824,11 +853,12 @@ tty_cmd_clearendofline(struct tty *tty, const struct tty_ctx *ctx) struct window_pane *wp = ctx->wp; struct screen *s = wp->screen; - tty_reset(tty); + tty_attributes(tty, &grid_default_cell, wp); tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); - if (tty_pane_full_width(tty, ctx) && tty_term_has(tty->term, TTYC_EL)) + if (tty_pane_full_width(tty, ctx) && + tty_term_has(tty->term, TTYC_EL) && !tty_fake_bce(tty, wp)) tty_putcode(tty, TTYC_EL); else tty_repeat_space(tty, screen_size_x(s) - ctx->ocx); @@ -837,9 +867,10 @@ tty_cmd_clearendofline(struct tty *tty, const struct tty_ctx *ctx) void tty_cmd_clearstartofline(struct tty *tty, const struct tty_ctx *ctx) { - tty_reset(tty); + tty_attributes(tty, &grid_default_cell, ctx->wp); - if (ctx->xoff == 0 && tty_term_has(tty->term, TTYC_EL1)) { + if (ctx->xoff == 0 && tty_term_has(tty->term, TTYC_EL1) && + !tty_fake_bce(tty, ctx->wp)) { tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); tty_putcode(tty, TTYC_EL1); } else { @@ -854,14 +885,14 @@ tty_cmd_reverseindex(struct tty *tty, const struct tty_ctx *ctx) if (ctx->ocy != ctx->orupper) return; - if (!tty_pane_full_width(tty, ctx) || + if (!tty_pane_full_width(tty, ctx) || tty_fake_bce(tty, ctx->wp) || !tty_term_has(tty->term, TTYC_CSR) || !tty_term_has(tty->term, TTYC_RI)) { tty_redraw_region(tty, ctx); return; } - tty_reset(tty); + tty_attributes(tty, &grid_default_cell, ctx->wp); tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); tty_cursor_pane(tty, ctx, ctx->ocx, ctx->orupper); @@ -877,7 +908,7 @@ tty_cmd_linefeed(struct tty *tty, const struct tty_ctx *ctx) if (ctx->ocy != ctx->orlower) return; - if (!tty_pane_full_width(tty, ctx) || + if (!tty_pane_full_width(tty, ctx) || tty_fake_bce(tty, wp) || !tty_term_has(tty->term, TTYC_CSR)) { if (tty_large_region(tty, ctx)) wp->flags |= PANE_REDRAW; @@ -894,7 +925,7 @@ tty_cmd_linefeed(struct tty *tty, const struct tty_ctx *ctx) if (ctx->num && !(tty->term->flags & TERM_EARLYWRAP)) return; - tty_reset(tty); + tty_attributes(tty, &grid_default_cell, wp); tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); @@ -909,12 +940,13 @@ tty_cmd_clearendofscreen(struct tty *tty, const struct tty_ctx *ctx) struct screen *s = wp->screen; u_int i, j; - tty_reset(tty); + tty_attributes(tty, &grid_default_cell, wp); tty_region_pane(tty, ctx, 0, screen_size_y(s) - 1); tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); - if (tty_pane_full_width(tty, ctx) && tty_term_has(tty->term, TTYC_EL)) { + if (tty_pane_full_width(tty, ctx) && + tty_term_has(tty->term, TTYC_EL) && !tty_fake_bce(tty, wp)) { tty_putcode(tty, TTYC_EL); if (ctx->ocy != screen_size_y(s) - 1) { tty_cursor_pane(tty, ctx, 0, ctx->ocy + 1); @@ -942,12 +974,13 @@ tty_cmd_clearstartofscreen(struct tty *tty, const struct tty_ctx *ctx) struct screen *s = wp->screen; u_int i, j; - tty_reset(tty); + tty_attributes(tty, &grid_default_cell, wp); tty_region_pane(tty, ctx, 0, screen_size_y(s) - 1); tty_cursor_pane(tty, ctx, 0, 0); - if (tty_pane_full_width(tty, ctx) && tty_term_has(tty->term, TTYC_EL)) { + if (tty_pane_full_width(tty, ctx) && + tty_term_has(tty->term, TTYC_EL) && !tty_fake_bce(tty, wp)) { for (i = 0; i < ctx->ocy; i++) { tty_putcode(tty, TTYC_EL); tty_emulate_repeat(tty, TTYC_CUD, TTYC_CUD1, 1); @@ -969,12 +1002,13 @@ tty_cmd_clearscreen(struct tty *tty, const struct tty_ctx *ctx) struct screen *s = wp->screen; u_int i, j; - tty_reset(tty); + tty_attributes(tty, &grid_default_cell, wp); tty_region_pane(tty, ctx, 0, screen_size_y(s) - 1); tty_cursor_pane(tty, ctx, 0, 0); - if (tty_pane_full_width(tty, ctx) && tty_term_has(tty->term, TTYC_EL)) { + if (tty_pane_full_width(tty, ctx) && + tty_term_has(tty->term, TTYC_EL) && !tty_fake_bce(tty, wp)) { for (i = 0; i < screen_size_y(s); i++) { tty_putcode(tty, TTYC_EL); if (i != screen_size_y(s) - 1) { @@ -997,7 +1031,7 @@ tty_cmd_alignmenttest(struct tty *tty, const struct tty_ctx *ctx) struct screen *s = wp->screen; u_int i, j; - tty_reset(tty); + tty_attributes(tty, &grid_default_cell, wp); tty_region_pane(tty, ctx, 0, screen_size_y(s) - 1); @@ -1038,12 +1072,12 @@ tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx) */ cx = screen_size_x(s) - grid_cell_width(&ctx->last_cell); tty_cursor_pane(tty, ctx, cx, ctx->ocy); - tty_cell(tty, &ctx->last_cell); + tty_cell(tty, &ctx->last_cell, wp); } } else tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); - tty_cell(tty, ctx->cell); + tty_cell(tty, ctx->cell, wp); } void @@ -1055,7 +1089,7 @@ tty_cmd_utf8character(struct tty *tty, const struct tty_ctx *ctx) * Cannot rely on not being a partial character, so just redraw the * whole line. */ - tty_draw_line(tty, wp->screen, ctx->ocy, ctx->xoff, ctx->yoff); + tty_draw_pane(tty, wp, ctx->ocy, ctx->xoff, ctx->yoff); } void @@ -1088,12 +1122,13 @@ tty_cmd_rawstring(struct tty *tty, const struct tty_ctx *ctx) tty->cx = tty->cy = UINT_MAX; tty->rupper = tty->rlower = UINT_MAX; - tty_reset(tty); + tty_attributes(tty, &grid_default_cell, ctx->wp); tty_cursor(tty, 0, 0); } void -tty_cell(struct tty *tty, const struct grid_cell *gc) +tty_cell(struct tty *tty, const struct grid_cell *gc, + const struct window_pane *wp) { struct utf8_data ud; u_int i; @@ -1108,7 +1143,7 @@ tty_cell(struct tty *tty, const struct grid_cell *gc) return; /* Set the attributes. */ - tty_attributes(tty, gc); + tty_attributes(tty, gc, wp); /* Get the cell and if ASCII write with putc to do ACS translation. */ grid_cell_get(gc, &ud); @@ -1312,12 +1347,14 @@ out: } void -tty_attributes(struct tty *tty, const struct grid_cell *gc) +tty_attributes(struct tty *tty, const struct grid_cell *gc, + const struct window_pane *wp) { struct grid_cell *tc = &tty->cell, gc2; u_char changed; memcpy(&gc2, gc, sizeof gc2); + tty_default_colours(&gc2, wp); /* * If no setab, try to use the reverse attribute as a best-effort for a @@ -1609,6 +1646,47 @@ tty_try_256(struct tty *tty, u_char colour, const char *type) return (-1); } +void +tty_default_colours(struct grid_cell *gc, const struct window_pane *wp) +{ + const struct grid_cell *agc, *pgc, *wgc; + + if (wp == NULL) + return; + + pgc = &wp->colgc; + agc = options_get_style(&wp->window->options, "window-active-style"); + wgc = options_get_style(&wp->window->options, "window-style"); + + if (gc->fg == 8 && !(gc->flags & GRID_FLAG_FG256)) { + if (pgc->fg != 8 || (pgc->flags & GRID_FLAG_FG256)) { + gc->fg = pgc->fg; + gc->flags |= (pgc->flags & GRID_FLAG_FG256); + } else if (wp == wp->window->active && + (agc->fg != 8 || (agc->flags & GRID_FLAG_FG256))) { + gc->fg = agc->fg; + gc->flags |= (agc->flags & GRID_FLAG_FG256); + } else { + gc->fg = wgc->fg; + gc->flags |= (wgc->flags & GRID_FLAG_FG256); + } + } + + if (gc->bg == 8 && !(gc->flags & GRID_FLAG_BG256)) { + if (pgc->bg != 8 || (pgc->flags & GRID_FLAG_BG256)) { + gc->bg = pgc->bg; + gc->flags |= (pgc->flags & GRID_FLAG_BG256); + } else if (wp == wp->window->active && + (agc->bg != 8 || (agc->flags & GRID_FLAG_BG256))) { + gc->bg = agc->bg; + gc->flags |= (agc->flags & GRID_FLAG_BG256); + } else { + gc->bg = wgc->bg; + gc->flags |= (wgc->flags & GRID_FLAG_BG256); + } + } +} + void tty_bell(struct tty *tty) { diff --git a/window.c b/window.c index 6a742a2e..4abcf495 100644 --- a/window.c +++ b/window.c @@ -705,6 +705,8 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit) wp->saved_grid = NULL; + memcpy(&wp->colgc, &grid_default_cell, sizeof wp->colgc); + screen_init(&wp->base, sx, sy, hlimit); wp->screen = &wp->base; From bf635e7741f7b881f67ec7e4a5caa02f7ff3d786 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 19 Apr 2015 21:34:21 +0000 Subject: [PATCH 2/6] 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. --- cfg.c | 2 +- cmd-command-prompt.c | 2 +- cmd-confirm-before.c | 2 +- cmd-copy-mode.c | 15 +- cmd-if-shell.c | 4 +- cmd-queue.c | 12 +- cmd-resize-pane.c | 66 ++++++++- cmd-send-keys.c | 21 ++- cmd.c | 94 ++++++++++++- control.c | 2 +- input-keys.c | 95 ++++++------- key-bindings.c | 13 +- key-string.c | 17 ++- layout.c | 52 ------- mode-key.c | 14 ++ options-table.c | 21 +-- server-client.c | 325 ++++++++++++++++++++++++++++++++----------- server-fn.c | 7 +- status.c | 13 +- tmux.1 | 99 +++++++++---- tmux.h | 140 +++++++++++-------- tty-keys.c | 81 +++-------- tty.c | 4 + window-choose.c | 107 ++++++-------- window-clock.c | 8 +- window-copy.c | 198 +++++++++++++------------- window.c | 49 +++---- 27 files changed, 879 insertions(+), 584 deletions(-) diff --git a/cfg.c b/cfg.c index 23828923..b4c9bff4 100644 --- a/cfg.c +++ b/cfg.c @@ -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++; } diff --git a/cmd-command-prompt.c b/cmd-command-prompt.c index 22b1d84e..1622e0b7 100644 --- a/cmd-command-prompt.c +++ b/cmd-command-prompt.c @@ -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) diff --git a/cmd-confirm-before.c b/cmd-confirm-before.c index 0bf58449..5e4816ed 100644 --- a/cmd-confirm-before.c +++ b/cmd-confirm-before.c @@ -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); diff --git a/cmd-copy-mode.c b/cmd-copy-mode.c index b46a5a46..19dca5ff 100644 --- a/cmd-copy-mode.c +++ b/cmd-copy-mode.c @@ -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); diff --git a/cmd-if-shell.c b/cmd-if-shell.c index 8c6620da..9659511e 100644 --- a/cmd-if-shell.c +++ b/cmd-if-shell.c @@ -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); } diff --git a/cmd-queue.c b/cmd-queue.c index 6be532a8..61b14147 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -20,6 +20,7 @@ #include #include +#include #include #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. */ diff --git a/cmd-resize-pane.c b/cmd-resize-pane.c index 42f0f39a..b342307d 100644 --- a/cmd-resize-pane.c +++ b/cmd-resize-pane.c @@ -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; +} diff --git a/cmd-send-keys.c b/cmd-send-keys.c index 7a4d97d5..27da410d 100644 --- a/cmd-send-keys.c +++ b/cmd-send-keys.c @@ -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); } } diff --git a/cmd.c b/cmd.c index 66ac58b8..dd1a0a7b 100644 --- a/cmd.c +++ b/cmd.c @@ -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) diff --git a/control.c b/control.c index 0ace6c12..11fa2d80 100644 --- a/control.c +++ b/control.c @@ -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); } diff --git a/input-keys.c b/input-keys.c index f2d010d8..ae00e4a9 100644 --- a/input-keys.c +++ b/input-keys.c @@ -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); } diff --git a/key-bindings.c b/key-bindings.c index 00f73d73..c6cdeeb7 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -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) "", 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); } diff --git a/key-string.c b/key-string.c index db968279..b6474c4f 100644 --- a/key-string.c +++ b/key-string.c @@ -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 (""); + if (key == KEYC_MOUSE) + return (""); /* * Special case: display C-@ as C-Space. Could do this below in diff --git a/layout.c b/layout.c index b91b86cd..bb1bbf8d 100644 --- a/layout.c +++ b/layout.c @@ -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( diff --git a/mode-key.c b/mode-key.c index 72d66f37..c06d7ed5 100644 --- a/mode-key.c +++ b/mode-key.c @@ -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 } }; diff --git a/options-table.c b/options-table.c index 5e72d14f..5e21c692 100644 --- a/options-table.c +++ b/options-table.c @@ -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" diff --git a/server-client.c b/server-client.c index 352e8ab6..3968c50b 100644 --- a/server-client.c +++ b/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; diff --git a/server-fn.c b/server-fn.c index f89eca8e..83ea9474 100644 --- a/server-fn.c +++ b/server-fn.c @@ -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); + } } diff --git a/status.c b/status.c index 5f8895fb..ae5d99ab 100644 --- a/status.c +++ b/status.c @@ -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. */ diff --git a/tmux.1 b/tmux.1 index a3dac237..fb08ddc1 100644 --- a/tmux.1 +++ b/tmux.1 @@ -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 diff --git a/tmux.h b/tmux.h index 19a8aa3a..477fd436 100644 --- a/tmux.h +++ b/tmux.h @@ -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; diff --git a/tty-keys.c b/tty-keys.c index a987c44b..1df22d4c 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -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); } diff --git a/tty.c b/tty.c index 59ed3bb8..a5067b12 100644 --- a/tty.c +++ b/tty.c @@ -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 diff --git a/window-choose.c b/window-choose.c index 8bed8d45..5de87572 100644 --- a/window-choose.c +++ b/window-choose.c @@ -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) diff --git a/window-clock.c b/window-clock.c index ede8df5b..3cabd9e9 100644 --- a/window-clock.c +++ b/window-clock.c @@ -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); } diff --git a/window-copy.c b/window-copy.c index feb8c481..a0560716 100644 --- a/window-copy.c +++ b/window-copy.c @@ -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); +} diff --git a/window.c b/window.c index 4abcf495..53e74a1d 100644 --- a/window.c +++ b/window.c @@ -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) { From bbac2aee1fe16b1ff67b0e2dd8faada68e157a5e Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 19 Apr 2015 21:46:52 +0000 Subject: [PATCH 3/6] Honour renumber-windows when unlinking a window, from Thomas Adam. --- server-fn.c | 1 + 1 file changed, 1 insertion(+) diff --git a/server-fn.c b/server-fn.c index 83ea9474..a065dd76 100644 --- a/server-fn.c +++ b/server-fn.c @@ -351,6 +351,7 @@ server_unlink_window(struct session *s, struct winlink *wl) server_destroy_session_group(s); else server_redraw_session_group(s); + session_renumber_windows(s); } void From 8101f1ef16d2f163dde885a42ef5480ff2562e43 Mon Sep 17 00:00:00 2001 From: jmc Date: Sun, 19 Apr 2015 22:10:30 +0000 Subject: [PATCH 4/6] tweak previous; --- tmux.1 | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tmux.1 b/tmux.1 index fb08ddc1..9b6f8341 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1028,7 +1028,7 @@ The 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 ). +.Sx MOUSE SUPPORT ) . .El .Pp Each window displayed by @@ -1678,7 +1678,7 @@ 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 ). +.Sx MOUSE SUPPORT ) . .It Xo Ic respawn-pane .Op Fl k .Op Fl t Ar target-pane @@ -1746,7 +1746,7 @@ Make pane .Ar target-pane the active pane in window .Ar target-window , -or set it's style (with +or set its style (with .Fl P ) . If one of .Fl D , @@ -2011,7 +2011,7 @@ 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 ). +.Sx MOUSE SUPPORT ) . .It Xo Ic send-prefix .Op Fl 2 .Op Fl t Ar target-pane @@ -3092,7 +3092,7 @@ The following mouse events are available: .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" +.It Li "WheelUp" Ta "WheelDown" Ta "" .El .Pp Each should be suffixed with a location, for example From 6f587570ed8c0e2db8bd4e676f4363b0824beb5f Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 20 Apr 2015 07:50:49 +0000 Subject: [PATCH 5/6] Use a more sensible buffer size for flags string. --- window.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/window.c b/window.c index 53e74a1d..86cc8bb0 100644 --- a/window.c +++ b/window.c @@ -650,7 +650,7 @@ window_destroy_panes(struct window *w) char * window_printable_flags(struct session *s, struct winlink *wl) { - char flags[BUFSIZ]; + char flags[32]; int pos; pos = 0; From 0fd9a97202dc2878d9cf21f3bea01b599c21e61b Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 20 Apr 2015 09:39:21 +0000 Subject: [PATCH 6/6] Make jump-to-backward/jump-to-forward repeatable with jump-reverse/jump-again, from Jacob Niehus. --- window-copy.c | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/window-copy.c b/window-copy.c index a0560716..afa609ed 100644 --- a/window-copy.c +++ b/window-copy.c @@ -75,8 +75,8 @@ void window_copy_cursor_up(struct window_pane *, int); void window_copy_cursor_down(struct window_pane *, int); void window_copy_cursor_jump(struct window_pane *); void window_copy_cursor_jump_back(struct window_pane *); -void window_copy_cursor_jump_to(struct window_pane *); -void window_copy_cursor_jump_to_back(struct window_pane *); +void window_copy_cursor_jump_to(struct window_pane *, int); +void window_copy_cursor_jump_to_back(struct window_pane *, int); void window_copy_cursor_next_word(struct window_pane *, const char *); void window_copy_cursor_next_word_end(struct window_pane *, const char *); void window_copy_cursor_previous_word(struct window_pane *, const char *); @@ -397,11 +397,11 @@ window_copy_key(struct window_pane *wp, struct client *c, struct session *sess, } if (data->inputtype == WINDOW_COPY_JUMPTOFORWARD) { for (; np != 0; np--) - window_copy_cursor_jump_to(wp); + window_copy_cursor_jump_to(wp, 0); } if (data->inputtype == WINDOW_COPY_JUMPTOBACK) { for (; np != 0; np--) - window_copy_cursor_jump_to_back(wp); + window_copy_cursor_jump_to_back(wp, 0); } } data->jumptype = data->inputtype; @@ -643,10 +643,10 @@ window_copy_key(struct window_pane *wp, struct client *c, struct session *sess, window_copy_cursor_jump_back(wp); } else if (data->jumptype == WINDOW_COPY_JUMPTOFORWARD) { for (; np != 0; np--) - window_copy_cursor_jump_to(wp); + window_copy_cursor_jump_to(wp, 1); } else if (data->jumptype == WINDOW_COPY_JUMPTOBACK) { for (; np != 0; np--) - window_copy_cursor_jump_to_back(wp); + window_copy_cursor_jump_to_back(wp, 1); } break; case MODEKEYCOPY_JUMPREVERSE: @@ -658,10 +658,10 @@ window_copy_key(struct window_pane *wp, struct client *c, struct session *sess, window_copy_cursor_jump(wp); } else if (data->jumptype == WINDOW_COPY_JUMPTOFORWARD) { for (; np != 0; np--) - window_copy_cursor_jump_to_back(wp); + window_copy_cursor_jump_to_back(wp, 1); } else if (data->jumptype == WINDOW_COPY_JUMPTOBACK) { for (; np != 0; np--) - window_copy_cursor_jump_to(wp); + window_copy_cursor_jump_to(wp, 1); } break; case MODEKEYCOPY_JUMPBACK: @@ -1944,7 +1944,7 @@ window_copy_cursor_jump_back(struct window_pane *wp) } void -window_copy_cursor_jump_to(struct window_pane *wp) +window_copy_cursor_jump_to(struct window_pane *wp, int jump_again) { struct window_copy_mode_data *data = wp->modedata; struct screen *back_s = data->backing; @@ -1952,7 +1952,7 @@ window_copy_cursor_jump_to(struct window_pane *wp) struct utf8_data ud; u_int px, py, xx; - px = data->cx + 1; + px = data->cx + 1 + jump_again; py = screen_hsize(back_s) + data->cy - data->oy; xx = window_copy_find_length(wp, py); @@ -1971,7 +1971,7 @@ window_copy_cursor_jump_to(struct window_pane *wp) } void -window_copy_cursor_jump_to_back(struct window_pane *wp) +window_copy_cursor_jump_to_back(struct window_pane *wp, int jump_again) { struct window_copy_mode_data *data = wp->modedata; struct screen *back_s = data->backing; @@ -1985,6 +1985,9 @@ window_copy_cursor_jump_to_back(struct window_pane *wp) if (px > 0) px--; + if (jump_again && px > 0) + px--; + for (;;) { gc = grid_peek_cell(back_s->grid, px, py); grid_cell_get(gc, &ud);