Skip to content

Commit c48369c

Browse files
committed
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.
1 parent e87303a commit c48369c

File tree

7 files changed

+172
-18
lines changed

7 files changed

+172
-18
lines changed

src/libvterm/include/vterm.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,19 @@ typedef struct {
259259
int (*setlineinfo)(int row, const VTermLineInfo *newinfo, const VTermLineInfo *oldinfo, void *user);
260260
} VTermStateCallbacks;
261261

262+
typedef struct {
263+
VTermPos pos;
264+
int buttons;
265+
#define MOUSE_BUTTON_LEFT 0x01
266+
#define MOUSE_BUTTON_MIDDLE 0x02
267+
#define MOUSE_BUTTON_RIGHT 0x04
268+
int flags;
269+
#define MOUSE_WANT_CLICK 0x01
270+
#define MOUSE_WANT_DRAG 0x02
271+
#define MOUSE_WANT_MOVE 0x04
272+
/* useful to add protocol? */
273+
} VTermMouseState;
274+
262275
VTermState *vterm_obtain_state(VTerm *vt);
263276

264277
void vterm_state_set_callbacks(VTermState *state, const VTermStateCallbacks *callbacks, void *user);
@@ -272,6 +285,7 @@ void *vterm_state_get_unrecognised_fbdata(VTermState *state);
272285
void vterm_state_reset(VTermState *state, int hard);
273286

274287
void vterm_state_get_cursorpos(const VTermState *state, VTermPos *cursorpos);
288+
void vterm_state_get_mousestate(const VTermState *state, VTermMouseState *mousestate);
275289
void vterm_state_get_default_colors(const VTermState *state, VTermColor *default_fg, VTermColor *default_bg);
276290
void vterm_state_get_palette_color(const VTermState *state, int index, VTermColor *col);
277291
void vterm_state_set_default_colors(VTermState *state, const VTermColor *default_fg, const VTermColor *default_bg);

src/libvterm/src/state.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1793,6 +1793,14 @@ void vterm_state_get_cursorpos(const VTermState *state, VTermPos *cursorpos)
17931793
*cursorpos = state->pos;
17941794
}
17951795

1796+
void vterm_state_get_mousestate(const VTermState *state, VTermMouseState *mousestate)
1797+
{
1798+
mousestate->pos.col = state->mouse_col;
1799+
mousestate->pos.row = state->mouse_row;
1800+
mousestate->buttons = state->mouse_buttons;
1801+
mousestate->flags = state->mouse_flags;
1802+
}
1803+
17961804
void vterm_state_set_callbacks(VTermState *state, const VTermStateCallbacks *callbacks, void *user)
17971805
{
17981806
if(callbacks) {

src/libvterm/src/vterm_internal.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,9 +95,6 @@ struct VTermState
9595
int mouse_col, mouse_row;
9696
int mouse_buttons;
9797
int mouse_flags;
98-
#define MOUSE_WANT_CLICK 0x01
99-
#define MOUSE_WANT_DRAG 0x02
100-
#define MOUSE_WANT_MOVE 0x04
10198

10299
enum { MOUSE_X10, MOUSE_UTF8, MOUSE_SGR, MOUSE_RXVT } mouse_protocol;
103100

src/proto/terminal.pro

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ void term_enter_job_mode(void);
1212
int send_keys_to_term(term_T *term, int c, int typed);
1313
int terminal_is_active(void);
1414
cursorentry_T *term_get_cursor_shape(guicolor_T *fg, guicolor_T *bg);
15+
void term_win_entered(void);
1516
int term_use_loop(void);
1617
int terminal_loop(int blocking);
1718
void term_job_ended(job_T *job);

src/terminal.c

Lines changed: 138 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,6 @@
3838
* in tl_scrollback are no longer used.
3939
*
4040
* TODO:
41-
* - if the job in the terminal does not support the mouse, we can use the
42-
* mouse in the Terminal window for copy/paste and scrolling.
4341
* - When using 'termguicolors' still use the 16 ANSI colors as-is. Helps for
4442
* - In the GUI use a terminal emulator for :!cmd. Make the height the same as
4543
* 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)
900898
return TRUE;
901899
}
902900

901+
static int enter_mouse_col = -1;
902+
static int enter_mouse_row = -1;
903+
904+
/*
905+
* Handle a mouse click, drag or release.
906+
* Return TRUE when a mouse event is sent to the terminal.
907+
*/
908+
static int
909+
term_mouse_click(VTerm *vterm, int key)
910+
{
911+
#if defined(FEAT_CLIPBOARD)
912+
/* For modeless selection mouse drag and release events are ignored, unless
913+
* they are preceded with a mouse down event */
914+
static int ignore_drag_release = TRUE;
915+
VTermMouseState mouse_state;
916+
917+
vterm_state_get_mousestate(vterm_obtain_state(vterm), &mouse_state);
918+
if (mouse_state.flags == 0)
919+
{
920+
/* Terminal is not using the mouse, use modeless selection. */
921+
switch (key)
922+
{
923+
case K_LEFTDRAG:
924+
case K_LEFTRELEASE:
925+
case K_RIGHTDRAG:
926+
case K_RIGHTRELEASE:
927+
/* Ignore drag and release events when the button-down wasn't
928+
* seen before. */
929+
if (ignore_drag_release)
930+
{
931+
int save_mouse_col, save_mouse_row;
932+
933+
if (enter_mouse_col < 0)
934+
break;
935+
936+
/* mouse click in the window gave us focus, handle that
937+
* click now */
938+
save_mouse_col = mouse_col;
939+
save_mouse_row = mouse_row;
940+
mouse_col = enter_mouse_col;
941+
mouse_row = enter_mouse_row;
942+
clip_modeless(MOUSE_LEFT, TRUE, FALSE);
943+
mouse_col = save_mouse_col;
944+
mouse_row = save_mouse_row;
945+
}
946+
/* FALLTHROUGH */
947+
case K_LEFTMOUSE:
948+
case K_RIGHTMOUSE:
949+
if (key == K_LEFTRELEASE || key == K_RIGHTRELEASE)
950+
ignore_drag_release = TRUE;
951+
else
952+
ignore_drag_release = FALSE;
953+
/* Should we call mouse_has() here? */
954+
if (clip_star.available)
955+
{
956+
int button, is_click, is_drag;
957+
958+
button = get_mouse_button(KEY2TERMCAP1(key),
959+
&is_click, &is_drag);
960+
if (mouse_model_popup() && button == MOUSE_LEFT
961+
&& (mod_mask & MOD_MASK_SHIFT))
962+
{
963+
/* Translate shift-left to right button. */
964+
button = MOUSE_RIGHT;
965+
mod_mask &= ~MOD_MASK_SHIFT;
966+
}
967+
clip_modeless(button, is_click, is_drag);
968+
}
969+
break;
970+
971+
case K_MIDDLEMOUSE:
972+
if (clip_star.available)
973+
insert_reg('*', TRUE);
974+
break;
975+
}
976+
enter_mouse_col = -1;
977+
return FALSE;
978+
}
979+
#endif
980+
enter_mouse_col = -1;
981+
982+
switch (key)
983+
{
984+
case K_LEFTMOUSE:
985+
case K_LEFTMOUSE_NM: term_send_mouse(vterm, 1, 1); break;
986+
case K_LEFTDRAG: term_send_mouse(vterm, 1, 1); break;
987+
case K_LEFTRELEASE:
988+
case K_LEFTRELEASE_NM: term_send_mouse(vterm, 1, 0); break;
989+
case K_MOUSEMOVE: term_send_mouse(vterm, 0, 0); break;
990+
case K_MIDDLEMOUSE: term_send_mouse(vterm, 2, 1); break;
991+
case K_MIDDLEDRAG: term_send_mouse(vterm, 2, 1); break;
992+
case K_MIDDLERELEASE: term_send_mouse(vterm, 2, 0); break;
993+
case K_RIGHTMOUSE: term_send_mouse(vterm, 3, 1); break;
994+
case K_RIGHTDRAG: term_send_mouse(vterm, 3, 1); break;
995+
case K_RIGHTRELEASE: term_send_mouse(vterm, 3, 0); break;
996+
}
997+
return TRUE;
998+
}
999+
9031000
/*
9041001
* Convert typed key "c" into bytes to send to the job.
9051002
* Return the number of bytes in "buf".
@@ -995,17 +1092,21 @@ term_convert_key(term_T *term, int c, char *buf)
9951092
case K_MOUSERIGHT: /* TODO */ return 0;
9961093

9971094
case K_LEFTMOUSE:
998-
case K_LEFTMOUSE_NM: other = term_send_mouse(vterm, 1, 1); break;
999-
case K_LEFTDRAG: other = term_send_mouse(vterm, 1, 1); break;
1095+
case K_LEFTMOUSE_NM:
1096+
case K_LEFTDRAG:
10001097
case K_LEFTRELEASE:
1001-
case K_LEFTRELEASE_NM: other = term_send_mouse(vterm, 1, 0); break;
1002-
case K_MOUSEMOVE: other = term_send_mouse(vterm, 0, 0); break;
1003-
case K_MIDDLEMOUSE: other = term_send_mouse(vterm, 2, 1); break;
1004-
case K_MIDDLEDRAG: other = term_send_mouse(vterm, 2, 1); break;
1005-
case K_MIDDLERELEASE: other = term_send_mouse(vterm, 2, 0); break;
1006-
case K_RIGHTMOUSE: other = term_send_mouse(vterm, 3, 1); break;
1007-
case K_RIGHTDRAG: other = term_send_mouse(vterm, 3, 1); break;
1008-
case K_RIGHTRELEASE: other = term_send_mouse(vterm, 3, 0); break;
1098+
case K_LEFTRELEASE_NM:
1099+
case K_MOUSEMOVE:
1100+
case K_MIDDLEMOUSE:
1101+
case K_MIDDLEDRAG:
1102+
case K_MIDDLERELEASE:
1103+
case K_RIGHTMOUSE:
1104+
case K_RIGHTDRAG:
1105+
case K_RIGHTRELEASE: if (!term_mouse_click(vterm, c))
1106+
return 0;
1107+
other = TRUE;
1108+
break;
1109+
10091110
case K_X1MOUSE: /* TODO */ return 0;
10101111
case K_X1DRAG: /* TODO */ return 0;
10111112
case K_X1RELEASE: /* TODO */ return 0;
@@ -1473,6 +1574,8 @@ term_vgetc()
14731574
return c;
14741575
}
14751576

1577+
static int mouse_was_outside = FALSE;
1578+
14761579
/*
14771580
* Send keys to terminal.
14781581
* 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)
14831586
{
14841587
char msg[KEY_BUF_LEN];
14851588
size_t len;
1486-
static int mouse_was_outside = FALSE;
14871589
int dragging_outside = FALSE;
14881590

14891591
/* Catch keys that need to be handled as in Normal mode. */
@@ -1731,6 +1833,29 @@ prepare_restore_cursor_props(void)
17311833
may_output_cursor_props();
17321834
}
17331835

1836+
/*
1837+
* Called when entering a window with the mouse. If this is a terminal window
1838+
* we may want to change state.
1839+
*/
1840+
void
1841+
term_win_entered()
1842+
{
1843+
term_T *term = curbuf->b_term;
1844+
1845+
if (term != NULL)
1846+
{
1847+
if (term_use_loop())
1848+
{
1849+
reset_VIsual_and_resel();
1850+
if (State & INSERT)
1851+
stop_insert_mode = TRUE;
1852+
}
1853+
mouse_was_outside = FALSE;
1854+
enter_mouse_col = mouse_col;
1855+
enter_mouse_row = mouse_row;
1856+
}
1857+
}
1858+
17341859
/*
17351860
* Returns TRUE if the current window contains a terminal and we are sending
17361861
* keys to the job.

src/ui.c

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2827,11 +2827,18 @@ jump_to_mouse(
28272827
* (MOUSE_FOCUS was set above if we dragged first). */
28282828
if (dragwin == NULL || (flags & MOUSE_RELEASED))
28292829
win_enter(wp, TRUE); /* can make wp invalid! */
2830-
#ifdef CHECK_DOUBLE_CLICK
2831-
/* set topline, to be able to check for double click ourselves */
2830+
28322831
if (curwin != old_curwin)
2832+
{
2833+
#ifdef CHECK_DOUBLE_CLICK
2834+
/* set topline, to be able to check for double click ourselves */
28332835
set_mouse_topline(curwin);
28342836
#endif
2837+
#ifdef FEAT_TERMINAL
2838+
/* when entering a terminal window may change state */
2839+
term_win_entered();
2840+
#endif
2841+
}
28352842
if (on_status_line) /* In (or below) status line */
28362843
{
28372844
/* Don't use start_arrow() if we're in the same window */

src/version.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -766,6 +766,8 @@ static char *(features[]) =
766766

767767
static int included_patches[] =
768768
{ /* Add new patch number below this line */
769+
/**/
770+
1598,
769771
/**/
770772
1597,
771773
/**/

0 commit comments

Comments
 (0)