# HG changeset patch # User Christian Brabandt # Date 1520793905 -3600 # Node ID a62b0bbc8834a8cdeebe8c93807af123a0369ba7 # Parent 17eebaa3188f59f17fa23d69a14ddfb5058d79bb patch 8.0.1598: cannot select text in a terminal with the mouse commit https://github.com/vim/vim/commit/c48369c3fc507f398abbc933a60f653c6abe6701 Author: Bram Moolenaar Date: Sun Mar 11 19:30:45 2018 +0100 patch 8.0.1598: cannot select text in a terminal with the mouse Problem: Cannot select text in a terminal with the mouse. Solution: When a job in a terminal is not consuming mouse events, use them for modeless selection. Also stop Insert mode when clicking in a terminal window. diff --git a/src/libvterm/include/vterm.h b/src/libvterm/include/vterm.h --- a/src/libvterm/include/vterm.h +++ b/src/libvterm/include/vterm.h @@ -259,6 +259,19 @@ typedef struct { int (*setlineinfo)(int row, const VTermLineInfo *newinfo, const VTermLineInfo *oldinfo, void *user); } VTermStateCallbacks; +typedef struct { + VTermPos pos; + int buttons; +#define MOUSE_BUTTON_LEFT 0x01 +#define MOUSE_BUTTON_MIDDLE 0x02 +#define MOUSE_BUTTON_RIGHT 0x04 + int flags; +#define MOUSE_WANT_CLICK 0x01 +#define MOUSE_WANT_DRAG 0x02 +#define MOUSE_WANT_MOVE 0x04 + /* useful to add protocol? */ +} VTermMouseState; + VTermState *vterm_obtain_state(VTerm *vt); void vterm_state_set_callbacks(VTermState *state, const VTermStateCallbacks *callbacks, void *user); @@ -272,6 +285,7 @@ void *vterm_state_get_unrecognised_fbdat void vterm_state_reset(VTermState *state, int hard); void vterm_state_get_cursorpos(const VTermState *state, VTermPos *cursorpos); +void vterm_state_get_mousestate(const VTermState *state, VTermMouseState *mousestate); void vterm_state_get_default_colors(const VTermState *state, VTermColor *default_fg, VTermColor *default_bg); void vterm_state_get_palette_color(const VTermState *state, int index, VTermColor *col); void vterm_state_set_default_colors(VTermState *state, const VTermColor *default_fg, const VTermColor *default_bg); diff --git a/src/libvterm/src/state.c b/src/libvterm/src/state.c --- a/src/libvterm/src/state.c +++ b/src/libvterm/src/state.c @@ -1793,6 +1793,14 @@ void vterm_state_get_cursorpos(const VTe *cursorpos = state->pos; } +void vterm_state_get_mousestate(const VTermState *state, VTermMouseState *mousestate) +{ + mousestate->pos.col = state->mouse_col; + mousestate->pos.row = state->mouse_row; + mousestate->buttons = state->mouse_buttons; + mousestate->flags = state->mouse_flags; +} + void vterm_state_set_callbacks(VTermState *state, const VTermStateCallbacks *callbacks, void *user) { if(callbacks) { diff --git a/src/libvterm/src/vterm_internal.h b/src/libvterm/src/vterm_internal.h --- a/src/libvterm/src/vterm_internal.h +++ b/src/libvterm/src/vterm_internal.h @@ -95,9 +95,6 @@ struct VTermState int mouse_col, mouse_row; int mouse_buttons; int mouse_flags; -#define MOUSE_WANT_CLICK 0x01 -#define MOUSE_WANT_DRAG 0x02 -#define MOUSE_WANT_MOVE 0x04 enum { MOUSE_X10, MOUSE_UTF8, MOUSE_SGR, MOUSE_RXVT } mouse_protocol; diff --git a/src/proto/terminal.pro b/src/proto/terminal.pro --- a/src/proto/terminal.pro +++ b/src/proto/terminal.pro @@ -12,6 +12,7 @@ void term_enter_job_mode(void); int send_keys_to_term(term_T *term, int c, int typed); int terminal_is_active(void); cursorentry_T *term_get_cursor_shape(guicolor_T *fg, guicolor_T *bg); +void term_win_entered(void); int term_use_loop(void); int terminal_loop(int blocking); void term_job_ended(job_T *job); diff --git a/src/terminal.c b/src/terminal.c --- a/src/terminal.c +++ b/src/terminal.c @@ -38,8 +38,6 @@ * in tl_scrollback are no longer used. * * TODO: - * - if the job in the terminal does not support the mouse, we can use the - * mouse in the Terminal window for copy/paste and scrolling. * - When using 'termguicolors' still use the 16 ANSI colors as-is. Helps for * - In the GUI use a terminal emulator for :!cmd. Make the height the same as * the window and position it higher up when it gets filled, so it looks like @@ -900,6 +898,105 @@ term_send_mouse(VTerm *vterm, int button return TRUE; } +static int enter_mouse_col = -1; +static int enter_mouse_row = -1; + +/* + * Handle a mouse click, drag or release. + * Return TRUE when a mouse event is sent to the terminal. + */ + static int +term_mouse_click(VTerm *vterm, int key) +{ +#if defined(FEAT_CLIPBOARD) + /* For modeless selection mouse drag and release events are ignored, unless + * they are preceded with a mouse down event */ + static int ignore_drag_release = TRUE; + VTermMouseState mouse_state; + + vterm_state_get_mousestate(vterm_obtain_state(vterm), &mouse_state); + if (mouse_state.flags == 0) + { + /* Terminal is not using the mouse, use modeless selection. */ + switch (key) + { + case K_LEFTDRAG: + case K_LEFTRELEASE: + case K_RIGHTDRAG: + case K_RIGHTRELEASE: + /* Ignore drag and release events when the button-down wasn't + * seen before. */ + if (ignore_drag_release) + { + int save_mouse_col, save_mouse_row; + + if (enter_mouse_col < 0) + break; + + /* mouse click in the window gave us focus, handle that + * click now */ + save_mouse_col = mouse_col; + save_mouse_row = mouse_row; + mouse_col = enter_mouse_col; + mouse_row = enter_mouse_row; + clip_modeless(MOUSE_LEFT, TRUE, FALSE); + mouse_col = save_mouse_col; + mouse_row = save_mouse_row; + } + /* FALLTHROUGH */ + case K_LEFTMOUSE: + case K_RIGHTMOUSE: + if (key == K_LEFTRELEASE || key == K_RIGHTRELEASE) + ignore_drag_release = TRUE; + else + ignore_drag_release = FALSE; + /* Should we call mouse_has() here? */ + if (clip_star.available) + { + int button, is_click, is_drag; + + button = get_mouse_button(KEY2TERMCAP1(key), + &is_click, &is_drag); + if (mouse_model_popup() && button == MOUSE_LEFT + && (mod_mask & MOD_MASK_SHIFT)) + { + /* Translate shift-left to right button. */ + button = MOUSE_RIGHT; + mod_mask &= ~MOD_MASK_SHIFT; + } + clip_modeless(button, is_click, is_drag); + } + break; + + case K_MIDDLEMOUSE: + if (clip_star.available) + insert_reg('*', TRUE); + break; + } + enter_mouse_col = -1; + return FALSE; + } +#endif + enter_mouse_col = -1; + + switch (key) + { + case K_LEFTMOUSE: + case K_LEFTMOUSE_NM: term_send_mouse(vterm, 1, 1); break; + case K_LEFTDRAG: term_send_mouse(vterm, 1, 1); break; + case K_LEFTRELEASE: + case K_LEFTRELEASE_NM: term_send_mouse(vterm, 1, 0); break; + case K_MOUSEMOVE: term_send_mouse(vterm, 0, 0); break; + case K_MIDDLEMOUSE: term_send_mouse(vterm, 2, 1); break; + case K_MIDDLEDRAG: term_send_mouse(vterm, 2, 1); break; + case K_MIDDLERELEASE: term_send_mouse(vterm, 2, 0); break; + case K_RIGHTMOUSE: term_send_mouse(vterm, 3, 1); break; + case K_RIGHTDRAG: term_send_mouse(vterm, 3, 1); break; + case K_RIGHTRELEASE: term_send_mouse(vterm, 3, 0); break; + } + return TRUE; +} + /* * Convert typed key "c" into bytes to send to the job. * Return the number of bytes in "buf". @@ -995,17 +1092,21 @@ term_convert_key(term_T *term, int c, ch case K_MOUSERIGHT: /* TODO */ return 0; case K_LEFTMOUSE: - case K_LEFTMOUSE_NM: other = term_send_mouse(vterm, 1, 1); break; - case K_LEFTDRAG: other = term_send_mouse(vterm, 1, 1); break; + case K_LEFTMOUSE_NM: + case K_LEFTDRAG: case K_LEFTRELEASE: - case K_LEFTRELEASE_NM: other = term_send_mouse(vterm, 1, 0); break; - case K_MOUSEMOVE: other = term_send_mouse(vterm, 0, 0); break; - case K_MIDDLEMOUSE: other = term_send_mouse(vterm, 2, 1); break; - case K_MIDDLEDRAG: other = term_send_mouse(vterm, 2, 1); break; - case K_MIDDLERELEASE: other = term_send_mouse(vterm, 2, 0); break; - case K_RIGHTMOUSE: other = term_send_mouse(vterm, 3, 1); break; - case K_RIGHTDRAG: other = term_send_mouse(vterm, 3, 1); break; - case K_RIGHTRELEASE: other = term_send_mouse(vterm, 3, 0); break; + case K_LEFTRELEASE_NM: + case K_MOUSEMOVE: + case K_MIDDLEMOUSE: + case K_MIDDLEDRAG: + case K_MIDDLERELEASE: + case K_RIGHTMOUSE: + case K_RIGHTDRAG: + case K_RIGHTRELEASE: if (!term_mouse_click(vterm, c)) + return 0; + other = TRUE; + break; + case K_X1MOUSE: /* TODO */ return 0; case K_X1DRAG: /* TODO */ return 0; case K_X1RELEASE: /* TODO */ return 0; @@ -1473,6 +1574,8 @@ term_vgetc() return c; } +static int mouse_was_outside = FALSE; + /* * Send keys to terminal. * Return FAIL when the key needs to be handled in Normal mode. @@ -1483,7 +1586,6 @@ send_keys_to_term(term_T *term, int c, i { char msg[KEY_BUF_LEN]; size_t len; - static int mouse_was_outside = FALSE; int dragging_outside = FALSE; /* Catch keys that need to be handled as in Normal mode. */ @@ -1732,6 +1834,29 @@ prepare_restore_cursor_props(void) } /* + * Called when entering a window with the mouse. If this is a terminal window + * we may want to change state. + */ + void +term_win_entered() +{ + term_T *term = curbuf->b_term; + + if (term != NULL) + { + if (term_use_loop()) + { + reset_VIsual_and_resel(); + if (State & INSERT) + stop_insert_mode = TRUE; + } + mouse_was_outside = FALSE; + enter_mouse_col = mouse_col; + enter_mouse_row = mouse_row; + } +} + +/* * Returns TRUE if the current window contains a terminal and we are sending * keys to the job. */ diff --git a/src/ui.c b/src/ui.c --- a/src/ui.c +++ b/src/ui.c @@ -2827,11 +2827,18 @@ retnomove: * (MOUSE_FOCUS was set above if we dragged first). */ if (dragwin == NULL || (flags & MOUSE_RELEASED)) win_enter(wp, TRUE); /* can make wp invalid! */ + + if (curwin != old_curwin) + { #ifdef CHECK_DOUBLE_CLICK - /* set topline, to be able to check for double click ourselves */ - if (curwin != old_curwin) + /* set topline, to be able to check for double click ourselves */ set_mouse_topline(curwin); #endif +#ifdef FEAT_TERMINAL + /* when entering a terminal window may change state */ + term_win_entered(); +#endif + } if (on_status_line) /* In (or below) status line */ { /* Don't use start_arrow() if we're in the same window */ diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -767,6 +767,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1598, +/**/ 1597, /**/ 1596,