To: vim_dev@googlegroups.com Subject: Patch 8.0.1598 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.0.1598 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. Files: src/libvterm/include/vterm.h, src/libvterm/src/state.c, src/libvterm/src/vterm_internal.h, src/terminal.c, src/proto/terminal.pro, src/ui.c *** ../vim-8.0.1597/src/libvterm/include/vterm.h 2017-11-29 22:33:31.051416026 +0100 --- src/libvterm/include/vterm.h 2018-03-11 17:54:21.447062569 +0100 *************** *** 259,264 **** --- 259,277 ---- 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,277 **** --- 285,291 ---- 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); *** ../vim-8.0.1597/src/libvterm/src/state.c 2018-02-24 14:03:49.748678084 +0100 --- src/libvterm/src/state.c 2018-03-11 18:12:34.052424018 +0100 *************** *** 1793,1798 **** --- 1793,1806 ---- *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) { *** ../vim-8.0.1597/src/libvterm/src/vterm_internal.h 2018-02-24 14:03:49.748678084 +0100 --- src/libvterm/src/vterm_internal.h 2018-03-11 17:49:52.268699591 +0100 *************** *** 95,103 **** 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; --- 95,100 ---- *** ../vim-8.0.1597/src/terminal.c 2018-03-11 16:55:30.008616433 +0100 --- src/terminal.c 2018-03-11 19:24:19.510441852 +0100 *************** *** 38,45 **** * 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 --- 38,43 ---- *************** *** 900,905 **** --- 898,1002 ---- 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,1011 **** 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_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_X1MOUSE: /* TODO */ return 0; case K_X1DRAG: /* TODO */ return 0; case K_X1RELEASE: /* TODO */ return 0; --- 1092,1112 ---- case K_MOUSERIGHT: /* TODO */ return 0; case K_LEFTMOUSE: ! case K_LEFTMOUSE_NM: ! case K_LEFTDRAG: case K_LEFTRELEASE: ! 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,1478 **** --- 1574,1581 ---- 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,1489 **** { 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. */ --- 1586,1591 ---- *************** *** 1732,1737 **** --- 1834,1862 ---- } /* + * 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. */ *** ../vim-8.0.1597/src/proto/terminal.pro 2018-03-10 20:27:32.071757661 +0100 --- src/proto/terminal.pro 2018-03-11 18:43:41.941098461 +0100 *************** *** 12,17 **** --- 12,18 ---- 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); *** ../vim-8.0.1597/src/ui.c 2018-03-04 18:07:04.284592244 +0100 --- src/ui.c 2018-03-11 18:36:56.139560777 +0100 *************** *** 2827,2837 **** * (MOUSE_FOCUS was set above if we dragged first). */ if (dragwin == NULL || (flags & MOUSE_RELEASED)) win_enter(wp, TRUE); /* can make wp invalid! */ ! #ifdef CHECK_DOUBLE_CLICK ! /* set topline, to be able to check for double click ourselves */ if (curwin != old_curwin) set_mouse_topline(curwin); #endif if (on_status_line) /* In (or below) status line */ { /* Don't use start_arrow() if we're in the same window */ --- 2827,2844 ---- * (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 */ 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 */ *** ../vim-8.0.1597/src/version.c 2018-03-11 17:02:07.310194396 +0100 --- src/version.c 2018-03-11 19:28:41.804859926 +0100 *************** *** 768,769 **** --- 768,771 ---- { /* Add new patch number below this line */ + /**/ + 1598, /**/ -- Normal people believe that if it ain't broke, don't fix it. Engineers believe that if it ain't broke, it doesn't have enough features yet. (Scott Adams - The Dilbert principle) /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///