Merge pull request #19481 from zeertzjq/vim-8.2.4674

Add 'mousemoveevent' as a UI option
This commit is contained in:
zeertzjq 2022-09-04 21:44:31 +08:00 committed by GitHub
commit 5ac6654334
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 245 additions and 18 deletions

View File

@ -1067,9 +1067,11 @@ nvim_input_mouse({button}, {action}, {modifier}, {grid}, {row}, {col})
|api-fast|
Parameters: ~
{button} Mouse button: one of "left", "right", "middle", "wheel".
{button} Mouse button: one of "left", "right", "middle", "wheel",
"move".
{action} For ordinary buttons, one of "press", "drag", "release".
For the wheel, one of "up", "down", "left", "right".
Ignored for "move".
{modifier} String of modifiers each represented by a single char. The
same specifiers are used as for a key press, except that
the "-" separator is optional, so "C-A-", "c-a" and "CA"

View File

@ -4231,6 +4231,15 @@ A jump table for the options with a short description can be found at |Q_op|.
The 'mousemodel' option is set by the |:behave| command.
*'mousemoveevent'* *'mousemev'*
'mousemoveevent' 'mousemev' boolean (default off)
global
When on, mouse move events are delivered to the input queue and are
available for mapping. The default, off, avoids the mouse movement
overhead except when needed.
Warning: Setting this option can make pending mappings to be aborted
when the mouse is moved.
*'mousescroll'*
'mousescroll' string (default "ver:3,hor:6")
global

View File

@ -207,6 +207,7 @@ the editor.
'guifontwide'
'linespace'
'mousefocus'
'mousemoveevent'
'pumblend'
'showtabline'
'termguicolors'

View File

@ -336,9 +336,9 @@ Integer nvim_input(String keys)
/// mouse input in a GUI. The deprecated pseudokey form
/// ("<LeftMouse><col,row>") of |nvim_input()| has the same limitation.
///
/// @param button Mouse button: one of "left", "right", "middle", "wheel".
/// @param button Mouse button: one of "left", "right", "middle", "wheel", "move".
/// @param action For ordinary buttons, one of "press", "drag", "release".
/// For the wheel, one of "up", "down", "left", "right".
/// For the wheel, one of "up", "down", "left", "right". Ignored for "move".
/// @param modifier String of modifiers each represented by a single char.
/// The same specifiers are used as for a key press, except
/// that the "-" separator is optional, so "C-A-", "c-a"
@ -365,6 +365,8 @@ void nvim_input_mouse(String button, String action, String modifier, Integer gri
code = KE_RIGHTMOUSE;
} else if (strequal(button.data, "wheel")) {
code = KE_MOUSEDOWN;
} else if (strequal(button.data, "move")) {
code = KE_MOUSEMOVE;
} else {
goto error;
}
@ -381,7 +383,7 @@ void nvim_input_mouse(String button, String action, String modifier, Integer gri
} else {
goto error;
}
} else {
} else if (code != KE_MOUSEMOVE) {
if (strequal(action.data, "press")) {
// pass
} else if (strequal(action.data, "drag")) {

View File

@ -616,6 +616,7 @@ EXTERN int p_ma; ///< 'modifiable'
EXTERN int p_mod; ///< 'modified'
EXTERN char *p_mouse; // 'mouse'
EXTERN char *p_mousem; // 'mousemodel'
EXTERN int p_mousemev; ///< 'mousemoveevent'
EXTERN int p_mousef; // 'mousefocus'
EXTERN char *p_mousescroll; // 'mousescroll'
EXTERN long p_mousescroll_vert INIT(= MOUSESCROLL_VERT_DFLT);

View File

@ -1621,6 +1621,14 @@ return {
varname='p_mousem',
defaults={if_true="popup_setpos"}
},
{
full_name='mousemoveevent', abbreviation='mousemev',
short_desc=N_("deliver mouse move events to input queue"),
type='bool', scope={'global'},
redraw={'ui_option'},
varname='p_mousemev',
defaults={if_true=false}
},
{
full_name='mousescroll',
short_desc=N_("amount to scroll by when scrolling with a mouse"),

View File

@ -293,7 +293,8 @@ static uint8_t check_multiclick(int code, int grid, int row, int col)
|| code == KE_MOUSEDOWN
|| code == KE_MOUSEUP
|| code == KE_MOUSELEFT
|| code == KE_MOUSERIGHT) {
|| code == KE_MOUSERIGHT
|| code == KE_MOUSEMOVE) {
return 0;
}
uint64_t mouse_time = os_hrtime(); // time of current mouse click (ns)
@ -347,7 +348,8 @@ static unsigned int handle_mouse_event(char **ptr, uint8_t *buf, unsigned int bu
if (type != KS_EXTRA
|| !((mouse_code >= KE_LEFTMOUSE && mouse_code <= KE_RIGHTRELEASE)
|| (mouse_code >= KE_MOUSEDOWN && mouse_code <= KE_MOUSERIGHT))) {
|| (mouse_code >= KE_MOUSEDOWN && mouse_code <= KE_MOUSERIGHT)
|| mouse_code == KE_MOUSEMOVE)) {
return bufsize;
}

View File

@ -1042,6 +1042,10 @@ void pum_show_popupmenu(vimmenu_T *menu)
pum_scrollbar = 0;
pum_height = pum_size;
pum_position_at_mouse(20);
if (!p_mousemev) {
// Pretend 'mousemoveevent' is set.
ui_call_option_set(STATIC_CSTR_AS_STRING("mousemoveevent"), BOOLEAN_OBJ(true));
}
pum_selected = -1;
pum_first = 0;
@ -1102,6 +1106,9 @@ void pum_show_popupmenu(vimmenu_T *menu)
xfree(array);
pum_undisplay(true);
if (!p_mousemev) {
ui_call_option_set(STATIC_CSTR_AS_STRING("mousemoveevent"), BOOLEAN_OBJ(false));
}
}
void pum_make_popup(const char *path_name, int use_mouse_pos)

View File

@ -406,8 +406,8 @@ static void forward_mouse_event(TermInput *input, TermKeyKey *key)
}
}
if (button == 0 || (ev != TERMKEY_MOUSE_PRESS && ev != TERMKEY_MOUSE_DRAG
&& ev != TERMKEY_MOUSE_RELEASE)) {
if ((button == 0 && ev != TERMKEY_MOUSE_RELEASE)
|| (ev != TERMKEY_MOUSE_PRESS && ev != TERMKEY_MOUSE_DRAG && ev != TERMKEY_MOUSE_RELEASE)) {
return;
}
@ -453,7 +453,8 @@ static void forward_mouse_event(TermInput *input, TermKeyKey *key)
len += (size_t)snprintf(buf + len, sizeof(buf) - len, "Drag");
break;
case TERMKEY_MOUSE_RELEASE:
len += (size_t)snprintf(buf + len, sizeof(buf) - len, "Release");
len += (size_t)snprintf(buf + len, sizeof(buf) - len, button ? "Release" : "MouseMove");
last_pressed_button = 0;
break;
case TERMKEY_MOUSE_UNKNOWN:
abort();

View File

@ -103,6 +103,7 @@ struct TUIData {
bool immediate_wrap_after_last_column;
bool bce;
bool mouse_enabled;
bool mouse_move_enabled;
bool busy, is_invisible, want_invisible;
bool cork, overflow;
bool cursor_color_changed;
@ -117,6 +118,7 @@ struct TUIData {
ModeShape showing_mode;
struct {
int enable_mouse, disable_mouse;
int enable_mouse_move, disable_mouse_move;
int enable_bracketed_paste, disable_bracketed_paste;
int enable_lr_margin, disable_lr_margin;
int enter_strikethrough_mode;
@ -236,6 +238,8 @@ static void terminfo_start(UI *ui)
data->showing_mode = SHAPE_IDX_N;
data->unibi_ext.enable_mouse = -1;
data->unibi_ext.disable_mouse = -1;
data->unibi_ext.enable_mouse_move = -1;
data->unibi_ext.disable_mouse_move = -1;
data->unibi_ext.set_cursor_color = -1;
data->unibi_ext.reset_cursor_color = -1;
data->unibi_ext.enable_bracketed_paste = -1;
@ -1138,6 +1142,9 @@ static void tui_mouse_on(UI *ui)
TUIData *data = ui->data;
if (!data->mouse_enabled) {
unibi_out_ext(ui, data->unibi_ext.enable_mouse);
if (data->mouse_move_enabled) {
unibi_out_ext(ui, data->unibi_ext.enable_mouse_move);
}
data->mouse_enabled = true;
}
}
@ -1146,6 +1153,9 @@ static void tui_mouse_off(UI *ui)
{
TUIData *data = ui->data;
if (data->mouse_enabled) {
if (data->mouse_move_enabled) {
unibi_out_ext(ui, data->unibi_ext.disable_mouse_move);
}
unibi_out_ext(ui, data->unibi_ext.disable_mouse);
data->mouse_enabled = false;
}
@ -1457,9 +1467,18 @@ static void tui_screenshot(UI *ui, String path)
static void tui_option_set(UI *ui, String name, Object value)
{
TUIData *data = ui->data;
if (strequal(name.data, "termguicolors")) {
if (strequal(name.data, "mousemoveevent")) {
if (data->mouse_move_enabled != value.data.boolean) {
if (data->mouse_enabled) {
tui_mouse_off(ui);
data->mouse_move_enabled = value.data.boolean;
tui_mouse_on(ui);
} else {
data->mouse_move_enabled = value.data.boolean;
}
}
} else if (strequal(name.data, "termguicolors")) {
ui->rgb = value.data.boolean;
data->print_attr_id = -1;
invalidate(ui, 0, data->grid.height, 0, data->grid.width);
} else if (strequal(name.data, "ttimeout")) {
@ -2135,6 +2154,10 @@ static void augment_terminfo(TUIData *data, const char *term, long vte_version,
"\x1b[?1002h\x1b[?1006h");
data->unibi_ext.disable_mouse = (int)unibi_add_ext_str(ut, "ext.disable_mouse",
"\x1b[?1002l\x1b[?1006l");
data->unibi_ext.enable_mouse_move = (int)unibi_add_ext_str(ut, "ext.enable_mouse_move",
"\x1b[?1003h");
data->unibi_ext.disable_mouse_move = (int)unibi_add_ext_str(ut, "ext.disable_mouse_move",
"\x1b[?1003l");
// Extended underline.
// terminfo will have Smulx for this (but no support for colors yet).

View File

@ -21,6 +21,7 @@ local nvim_set = helpers.nvim_set
local ok = helpers.ok
local read_file = helpers.read_file
local funcs = helpers.funcs
local meths = helpers.meths
if helpers.pending_win32(pending) then return end
@ -666,6 +667,57 @@ describe('TUI', function()
]], attrs)
end)
it('mouse events work with right-click menu', function()
child_session:request('nvim_command', [[
call setline(1, 'popup menu test')
set mouse=a mousemodel=popup
aunmenu PopUp
menu PopUp.foo :let g:menustr = 'foo'<CR>
menu PopUp.bar :let g:menustr = 'bar'<CR>
menu PopUp.baz :let g:menustr = 'baz'<CR>
highlight Pmenu ctermbg=NONE ctermfg=NONE cterm=underline,reverse
highlight PmenuSel ctermbg=NONE ctermfg=NONE cterm=underline,reverse,bold
]])
local attrs = screen:get_default_attr_ids()
attrs[11] = {underline = true, reverse = true}
attrs[12] = {underline = true, reverse = true, bold = true}
meths.input_mouse('right', 'press', '', 0, 0, 4)
screen:expect([[
{1:p}opup menu test |
{4:~ }{11: foo }{4: }|
{4:~ }{11: bar }{4: }|
{4:~ }{11: baz }{4: }|
{5:[No Name] [+] }|
|
{3:-- TERMINAL --} |
]], attrs)
meths.input_mouse('right', 'release', '', 0, 0, 4)
screen:expect_unchanged()
meths.input_mouse('move', '', '', 0, 3, 6)
screen:expect([[
{1:p}opup menu test |
{4:~ }{11: foo }{4: }|
{4:~ }{11: bar }{4: }|
{4:~ }{12: baz }{4: }|
{5:[No Name] [+] }|
|
{3:-- TERMINAL --} |
]], attrs)
meths.input_mouse('left', 'press', '', 0, 2, 6)
screen:expect([[
{1:p}opup menu test |
{4:~ }|
{4:~ }|
{4:~ }|
{5:[No Name] [+] }|
:let g:menustr = 'bar' |
{3:-- TERMINAL --} |
]], attrs)
meths.input_mouse('left', 'release', '', 0, 2, 6)
screen:expect_unchanged()
end)
it('paste: Insert mode', function()
-- "bracketed paste"
feed_data('i""\027i\027[200~')

View File

@ -1585,7 +1585,20 @@ describe('ui/mouse/input', function()
eq(0, meths.get_var('mouse_up2'))
end)
it('feeding <MouseMove> does not use uninitialized memory #19480', function()
it('<MouseMove> is not translated into multiclicks and can be mapped', function()
meths.set_var('mouse_move', 0)
meths.set_var('mouse_move2', 0)
command('nnoremap <MouseMove> <Cmd>let g:mouse_move += 1<CR>')
command('nnoremap <2-MouseMove> <Cmd>let g:mouse_move2 += 1<CR>')
feed('<MouseMove><0,0>')
feed('<MouseMove><0,0>')
meths.input_mouse('move', '', '', 0, 0, 0)
meths.input_mouse('move', '', '', 0, 0, 0)
eq(4, meths.get_var('mouse_move'))
eq(0, meths.get_var('mouse_move2'))
end)
it('feeding <MouseMove> in Normal mode does not use uninitialized memory #19480', function()
feed('<MouseMove>')
helpers.poke_eventloop()
helpers.assert_alive()

View File

@ -19,6 +19,7 @@ describe('UI receives option updates', function()
linespace=0,
pumblend=0,
mousefocus=false,
mousemoveevent=false,
showtabline=1,
termguicolors=false,
ttimeout=true,
@ -131,6 +132,12 @@ describe('UI receives option updates', function()
eq(expected, screen.options)
end)
command("set mousemoveevent")
expected.mousemoveevent = true
screen:expect(function()
eq(expected, screen.options)
end)
command("set nottimeout")
expected.ttimeout = false
screen:expect(function()

View File

@ -2755,7 +2755,7 @@ describe('builtin popupmenu', function()
menu PopUp.bar :let g:menustr = 'bar'<CR>
menu PopUp.baz :let g:menustr = 'baz'<CR>
]])
meths.input_mouse('right', 'press', '', 0, 0, 4)
feed('<RightMouse><4,0>')
screen:expect([[
^popup menu test |
{1:~ }{n: foo }{1: }|
@ -2792,7 +2792,7 @@ describe('builtin popupmenu', function()
:let g:menustr = 'bar' |
]])
eq('bar', meths.get_var('menustr'))
meths.input_mouse('right', 'press', '', 0, 1, 20)
feed('<RightMouse><20,1>')
screen:expect([[
^popup menu test |
{1:~ }|
@ -2801,7 +2801,7 @@ describe('builtin popupmenu', function()
{1:~ }{n: baz }{1: }|
:let g:menustr = 'bar' |
]])
meths.input_mouse('left', 'press', '', 0, 4, 22)
feed('<LeftMouse><22,4>')
screen:expect([[
^popup menu test |
{1:~ }|
@ -2811,7 +2811,7 @@ describe('builtin popupmenu', function()
:let g:menustr = 'baz' |
]])
eq('baz', meths.get_var('menustr'))
meths.input_mouse('right', 'press', '', 0, 0, 4)
feed('<RightMouse><4,0>')
screen:expect([[
^popup menu test |
{1:~ }{n: foo }{1: }|
@ -2820,7 +2820,7 @@ describe('builtin popupmenu', function()
{1:~ }|
:let g:menustr = 'baz' |
]])
meths.input_mouse('right', 'drag', '', 0, 3, 6)
feed('<RightDrag><6,3>')
screen:expect([[
^popup menu test |
{1:~ }{n: foo }{1: }|
@ -2829,7 +2829,7 @@ describe('builtin popupmenu', function()
{1:~ }|
:let g:menustr = 'baz' |
]])
meths.input_mouse('right', 'release', '', 0, 1, 6)
feed('<RightRelease><6,1>')
screen:expect([[
^popup menu test |
{1:~ }|
@ -2839,6 +2839,38 @@ describe('builtin popupmenu', function()
:let g:menustr = 'foo' |
]])
eq('foo', meths.get_var('menustr'))
eq(false, screen.options.mousemoveevent)
feed('<RightMouse><4,0>')
screen:expect([[
^popup menu test |
{1:~ }{n: foo }{1: }|
{1:~ }{n: bar }{1: }|
{1:~ }{n: baz }{1: }|
{1:~ }|
:let g:menustr = 'foo' |
]])
eq(true, screen.options.mousemoveevent)
feed('<MouseMove><6,3>')
screen:expect([[
^popup menu test |
{1:~ }{n: foo }{1: }|
{1:~ }{n: bar }{1: }|
{1:~ }{s: baz }{1: }|
{1:~ }|
:let g:menustr = 'foo' |
]])
eq(true, screen.options.mousemoveevent)
feed('<LeftMouse><6,2>')
screen:expect([[
^popup menu test |
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
:let g:menustr = 'bar' |
]])
eq(false, screen.options.mousemoveevent)
eq('bar', meths.get_var('menustr'))
end)
end)
@ -3047,5 +3079,72 @@ describe('builtin popupmenu with ui/ext_multigrid', function()
:let g:menustr = 'foo' |
]]})
eq('foo', meths.get_var('menustr'))
eq(false, screen.options.mousemoveevent)
meths.input_mouse('right', 'press', '', 2, 0, 4)
screen:expect({grid=[[
## grid 1
[2:--------------------------------]|
[2:--------------------------------]|
[2:--------------------------------]|
[2:--------------------------------]|
[2:--------------------------------]|
[3:--------------------------------]|
## grid 2
^popup menu test |
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
## grid 3
:let g:menustr = 'foo' |
## grid 4
{n: foo }|
{n: bar }|
{n: baz }|
]], float_pos={[4] = {{id = -1}, 'NW', 2, 1, 3, false, 100}}})
eq(true, screen.options.mousemoveevent)
meths.input_mouse('move', '', '', 2, 3, 6)
screen:expect({grid=[[
## grid 1
[2:--------------------------------]|
[2:--------------------------------]|
[2:--------------------------------]|
[2:--------------------------------]|
[2:--------------------------------]|
[3:--------------------------------]|
## grid 2
^popup menu test |
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
## grid 3
:let g:menustr = 'foo' |
## grid 4
{n: foo }|
{n: bar }|
{s: baz }|
]], float_pos={[4] = {{id = -1}, 'NW', 2, 1, 3, false, 100}}})
eq(true, screen.options.mousemoveevent)
meths.input_mouse('left', 'press', '', 2, 2, 6)
screen:expect({grid=[[
## grid 1
[2:--------------------------------]|
[2:--------------------------------]|
[2:--------------------------------]|
[2:--------------------------------]|
[2:--------------------------------]|
[3:--------------------------------]|
## grid 2
^popup menu test |
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
## grid 3
:let g:menustr = 'bar' |
]]})
eq(false, screen.options.mousemoveevent)
eq('bar', meths.get_var('menustr'))
end)
end)