]> granicus.if.org Git - vim/commitdiff
patch 8.0.1598: cannot select text in a terminal with the mouse v8.0.1598
authorBram Moolenaar <Bram@vim.org>
Sun, 11 Mar 2018 18:30:45 +0000 (19:30 +0100)
committerBram Moolenaar <Bram@vim.org>
Sun, 11 Mar 2018 18:30:45 +0000 (19:30 +0100)
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.

src/libvterm/include/vterm.h
src/libvterm/src/state.c
src/libvterm/src/vterm_internal.h
src/proto/terminal.pro
src/terminal.c
src/ui.c
src/version.c

index f731dcb8b70d3f5bcf3d5c63590dc96b243b2098..df8e968777338ca9f1ce25d0b48041a160002639 100644 (file)
@@ -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_fbdata(VTermState *state);
 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);
index 05dbe5a964cb710931e17c2ddc99299dee4823c9..9070e64a553eee5e872028251987708919a5ca5f 100644 (file)
@@ -1793,6 +1793,14 @@ void vterm_state_get_cursorpos(const VTermState *state, VTermPos *cursorpos)
   *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) {
index 3b337c0c196d05db83adf5a4fff20dff4cd59d13..3e7f1e5cb3383517f3631cfb2bd5d789fe466a83 100644 (file)
@@ -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;
 
index f7d06c050bb0621e4c26fd7245ff11e083ade062..977537b71626a803c8529dd8fc7fe2ff364af560 100644 (file)
@@ -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);
index 7a80636ad09836c823d46cc1e339e70d7d244e8e..43a2a317e23428adc6309e1bf59a4b9e0f8e1ef5 100644 (file)
@@ -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, int pressed)
     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, char *buf)
        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, int typed)
 {
     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. */
@@ -1731,6 +1833,29 @@ prepare_restore_cursor_props(void)
     may_output_cursor_props();
 }
 
+/*
+ * 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.
index 981f07f45a821108d8e8cd1dd738ae53bd3d30b5..87f3c1eb600d5015b4ece93c2e218e92d9b9730e 100644 (file)
--- 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! */
-#ifdef CHECK_DOUBLE_CLICK
-       /* set topline, to be able to check for double click ourselves */
+
        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 */
index e30c251766c98fe0e2fcbd65ebbe634ee06f2e2c..4c192717fc2a097cb7d8edca59a956ef6a650aea 100644 (file)
@@ -766,6 +766,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1598,
 /**/
     1597,
 /**/