Patch 9.0.1084

29 views
Skip to first unread message

Bram Moolenaar

unread,
Dec 20, 2022, 3:02:41 PM12/20/22
to vim...@googlegroups.com

Patch 9.0.1084
Problem: Code handling low level MS-Windows events cannot be tested.
Solution: Add test_mswin_event() and tests using it. (Christopher Plewright,
closes #11622)
Files: runtime/doc/builtin.txt, runtime/doc/testing.txt,
runtime/doc/usr_41.txt, src/evalfunc.c, src/gui_w32.c,
src/os_win32.c, src/proto/gui_w32.pro, src/proto/os_win32.pro,
src/proto/testing.pro, src/term.c, src/testing.c,
src/testdir/Make_all.mak, src/testdir/mouse.vim,
src/testdir/test_gui.vim, src/testdir/test_mswin_event.vim,
src/testdir/test_termcodes.vim


*** ../vim-9.0.1083/runtime/doc/builtin.txt 2022-12-05 13:50:49.718052362 +0000
--- runtime/doc/builtin.txt 2022-12-20 19:22:31.969188032 +0000
***************
*** 666,671 ****
--- 666,673 ----
test_getvalue({string}) any get value of an internal variable
test_gui_event({event}, {args}) bool generate a GUI event for testing
test_ignore_error({expr}) none ignore a specific error
+ test_mswin_event({event}, {args})
+ bool generate MS-Windows event for testing
test_null_blob() Blob null value for testing
test_null_channel() Channel null value for testing
test_null_dict() Dict null value for testing
*** ../vim-9.0.1083/runtime/doc/testing.txt 2022-07-23 05:04:07.580839529 +0100
--- runtime/doc/testing.txt 2022-12-20 19:27:27.353314009 +0000
***************
*** 94,100 ****
"findrepl" search and replace text.
"mouse" mouse button click event.
"scrollbar" move or drag the scrollbar.
! "sendevent" send a low-level GUI event.
"tabline" select a tab page by mouse click.
"tabmenu" select a tabline menu entry.

--- 94,100 ----
"findrepl" search and replace text.
"mouse" mouse button click event.
"scrollbar" move or drag the scrollbar.
! "key" send a low-level keyboard event.
"tabline" select a tab page by mouse click.
"tabmenu" select a tabline menu entry.

***************
*** 135,143 ****
Inject either a mouse button click, or a mouse move, event.
The supported items in {args} are:
button: mouse button. The supported values are:
! 0 right mouse button
1 middle mouse button
! 2 left mouse button
3 mouse button release
4 scroll wheel down
5 scroll wheel up
--- 135,143 ----
Inject either a mouse button click, or a mouse move, event.
The supported items in {args} are:
button: mouse button. The supported values are:
! 0 left mouse button
1 middle mouse button
! 2 right mouse button
3 mouse button release
4 scroll wheel down
5 scroll wheel up
***************
*** 178,191 ****
dragging: 1 to drag the scrollbar and 0 to click in the
scrollbar.

! "sendevent":
! Send a low-level GUI event (e.g. key-up or down).
Currently only supported on MS-Windows.
The supported items in {args} are:
event: The supported string values are:
keyup generate a keyup event
keydown generate a keydown event
keycode: Keycode to use for a keyup or a keydown event.

"tabline":
Inject a mouse click event on the tabline to select a
--- 178,192 ----
dragging: 1 to drag the scrollbar and 0 to click in the
scrollbar.

! "key":
! Send a low-level keyboard event (e.g. key-up or down).
Currently only supported on MS-Windows.
The supported items in {args} are:
event: The supported string values are:
keyup generate a keyup event
keydown generate a keydown event
keycode: Keycode to use for a keyup or a keydown event.
+ *E1291*

"tabline":
Inject a mouse click event on the tabline to select a
***************
*** 222,227 ****
--- 223,294 ----
Can also be used as a |method|: >
GetErrorText()->test_ignore_error()

+
+ test_mswin_event({event}, {args}) *test_mswin_event()*
+ Generate a low-level MS-Windows {event} with arguments {args}
+ for testing Vim functionality. It works for MS-Windows GUI
+ and for the console.
+
+ {event} is a String and the supported values are:
+ "mouse" mouse event.
+ "key" keyboard event.
+
+ "mouse":
+ Inject either a mouse button click, or a mouse move, event.
+ The supported items in {args} are:
+ button: mouse button. The supported values are:
+ 0 right mouse button
+ 1 middle mouse button
+ 2 left mouse button
+ 3 mouse button release
+ 4 scroll wheel down
+ 5 scroll wheel up
+ 6 scroll wheel left
+ 7 scroll wheel right
+ row: mouse click row number. The first row of the
+ Vim window is 1 and the last row is 'lines'.
+ col: mouse click column number. The maximum value
+ of {col} is 'columns'.
+ Note: row and col are always interpreted as
+ screen cells for the console application.
+ But, they may be interpreted as pixels
+ for the GUI, depending on "cell".
+ multiclick: set to 1 to inject a double-click mouse event.
+ modifiers: key modifiers. The supported values are:
+ 4 shift is pressed
+ 8 alt is pressed
+ 16 ctrl is pressed
+ move: Optional; if used and TRUE then a mouse move
+ event can be generated.
+ Only {args} row: and col: are used and
+ required.
+ Only results in an event when 'mousemoveevent'
+ is set or a popup uses mouse move events.
+ cell: Optional for the GUI: when present and TRUE
+ then "move" uses screen cells instead of pixel
+ positions. Not used by the console.
+
+ "key":
+ Send a low-level keyboard event (e.g. keyup or keydown).
+ The supported items in {args} are:
+ event: The supported string values are:
+ keyup generate a keyup event
+ keydown generate a keydown event
+ keycode: Keycode to use for a keyup or a keydown event.
+ modifiers: Optional; key modifiers.
+ The supported values are:
+ 2 shift is pressed
+ 4 ctrl is pressed
+ 8 alt is pressed
+ Note: These values are different from the
+ mouse modifiers.
+ *E1291*
+ Returns TRUE if the event is successfully added, FALSE if
+ there is a failure.
+
+ Can also be used as a |method|: >
+ GetEvent()->test_mswin_event({args})
+ <

test_null_blob() *test_null_blob()*
Return a |Blob| that is null. Only useful for testing.
*** ../vim-9.0.1083/runtime/doc/usr_41.txt 2022-12-05 13:50:49.718052362 +0000
--- runtime/doc/usr_41.txt 2022-12-20 19:27:44.801319311 +0000
***************
*** 1185,1190 ****
--- 1186,1192 ----
test_getvalue() get value of an internal variable
test_gui_event() generate a GUI event for testing
test_ignore_error() ignore a specific error message
+ test_mswin_event() generate an MS-Windows event
test_null_blob() return a null Blob
test_null_channel() return a null Channel
test_null_dict() return a null Dict
*** ../vim-9.0.1083/src/evalfunc.c 2022-12-08 15:32:11.079034172 +0000
--- src/evalfunc.c 2022-12-20 19:28:32.093332756 +0000
***************
*** 2694,2699 ****
--- 2694,2701 ----
ret_bool, f_test_gui_event},
{"test_ignore_error", 1, 1, FEARG_1, arg1_string,
ret_void, f_test_ignore_error},
+ {"test_mswin_event", 2, 2, FEARG_1, arg2_string_dict,
+ ret_number, f_test_mswin_event},
{"test_null_blob", 0, 0, 0, NULL,
ret_blob, f_test_null_blob},
{"test_null_channel", 0, 0, 0, NULL,
***************
*** 4387,4393 ****

if (*keys != NUL || execute)
{
! if (lowlevel)
{
#ifdef USE_INPUT_BUF
ch_log(NULL, "feedkeys() lowlevel: %s", keys);
--- 4389,4400 ----

if (*keys != NUL || execute)
{
! if (lowlevel
! #ifdef FEAT_VTP
! && (!is_term_win32()
! || (keys[0] == 3 && ctrl_c_interrupts && typed))
! #endif
! )
{
#ifdef USE_INPUT_BUF
ch_log(NULL, "feedkeys() lowlevel: %s", keys);
*** ../vim-9.0.1083/src/gui_w32.c 2022-11-16 12:02:24.861138121 +0000
--- src/gui_w32.c 2022-12-20 19:45:48.112743098 +0000
***************
*** 8643,8683 ****
#endif

#if defined(FEAT_EVAL) || defined(PROTO)
! int
! test_gui_w32_sendevent(dict_T *args)
{
! char_u *event;
! INPUT inputs[1];

! event = dict_get_string(args, "event", TRUE);
! if (event == NULL)
return FALSE;

! ZeroMemory(inputs, sizeof(inputs));

! if (STRICMP(event, "keydown") == 0 || STRICMP(event, "keyup") == 0)
{
! WORD vkCode;

! vkCode = dict_get_number_def(args, "keycode", 0);
if (vkCode <= 0 || vkCode >= 0xFF)
{
semsg(_(e_invalid_argument_nr), (long)vkCode);
return FALSE;
}

inputs[0].type = INPUT_KEYBOARD;
inputs[0].ki.wVk = vkCode;
if (STRICMP(event, "keyup") == 0)
inputs[0].ki.dwFlags = KEYEVENTF_KEYUP;
(void)SetForegroundWindow(s_hwnd);
SendInput(ARRAYSIZE(inputs), inputs, sizeof(INPUT));
! }
! else
! semsg(_(e_invalid_argument_str), event);

! vim_free(event);

return TRUE;
}
#endif
--- 8643,8818 ----
#endif

#if defined(FEAT_EVAL) || defined(PROTO)
!
! // TODO: at the moment, this is just a copy of test_gui_mouse_event.
! // But, we could instead generate actual Win32 mouse event messages,
! // ie. to make it consistent wih test_gui_w32_sendevent_keyboard.
! static int
! test_gui_w32_sendevent_mouse(dict_T *args)
{
! if (!dict_has_key(args, "row") || !dict_has_key(args, "col"))
! return FALSE;

! // Note: "move" is optional, requires fewer arguments
! int move = (int)dict_get_bool(args, "move", FALSE);
!
! if (!move && (!dict_has_key(args, "button")
! || !dict_has_key(args, "multiclick")
! || !dict_has_key(args, "modifiers")))
return FALSE;

! int row = (int)dict_get_number(args, "row");
! int col = (int)dict_get_number(args, "col");

! if (move)
{
! // the "move" argument expects row and col coordnates to be in pixels,
! // unless "cell" is specified and is TRUE.
! if (dict_get_bool(args, "cell", FALSE))
! {
! // calculate the middle of the character cell
! // Note: Cell coordinates are 1-based from vimscript
! int pY = (row - 1) * gui.char_height + gui.char_height / 2;
! int pX = (col - 1) * gui.char_width + gui.char_width / 2;
! gui_mouse_moved(pX, pY);
! }
! else
! gui_mouse_moved(col, row);
! }
! else
! {
! int button = (int)dict_get_number(args, "button");
! int repeated_click = (int)dict_get_number(args, "multiclick");
! int_u mods = (int)dict_get_number(args, "modifiers");
!
! // Reset the scroll values to known values.
! // XXX: Remove this when/if the scroll step is made configurable.
! mouse_set_hor_scroll_step(6);
! mouse_set_vert_scroll_step(3);
!
! gui_send_mouse_event(button, TEXT_X(col - 1), TEXT_Y(row - 1),
! repeated_click, mods);
! }
! return TRUE;
! }
!
! static int
! test_gui_w32_sendevent_keyboard(dict_T *args)
! {
! INPUT inputs[1];
! INPUT modkeys[3];
! SecureZeroMemory(inputs, sizeof(INPUT));
! SecureZeroMemory(modkeys, 3 * sizeof(INPUT));
!
! char_u *event = dict_get_string(args, "event", TRUE);

! if (event && (STRICMP(event, "keydown") == 0
! || STRICMP(event, "keyup") == 0))
! {
! WORD vkCode = dict_get_number_def(args, "keycode", 0);
if (vkCode <= 0 || vkCode >= 0xFF)
{
semsg(_(e_invalid_argument_nr), (long)vkCode);
return FALSE;
}

+ BOOL isModKey = (vkCode == VK_SHIFT || vkCode == VK_CONTROL
+ || vkCode == VK_MENU || vkCode == VK_LSHIFT || vkCode == VK_RSHIFT
+ || vkCode == VK_LCONTROL || vkCode == VK_RCONTROL
+ || vkCode == VK_LMENU || vkCode == VK_RMENU );
+
+ BOOL unwrapMods = FALSE;
+ int mods = (int)dict_get_number(args, "modifiers");
+
+ // If there are modifiers in the args, and it is not a keyup event and
+ // vkCode is not a modifier key, then we generate virtual modifier key
+ // messages before sending the actual key message.
+ if(mods && STRICMP(event, "keydown") == 0 && !isModKey)
+ {
+ int n = 0;
+ if (mods & MOD_MASK_SHIFT)
+ {
+ modkeys[n].type = INPUT_KEYBOARD;
+ modkeys[n].ki.wVk = VK_LSHIFT;
+ n++;
+ }
+ if (mods & MOD_MASK_CTRL)
+ {
+ modkeys[n].type = INPUT_KEYBOARD;
+ modkeys[n].ki.wVk = VK_LCONTROL;
+ n++;
+ }
+ if (mods & MOD_MASK_ALT)
+ {
+ modkeys[n].type = INPUT_KEYBOARD;
+ modkeys[n].ki.wVk = VK_LMENU;
+ n++;
+ }
+ if (n)
+ {
+ (void)SetForegroundWindow(s_hwnd);
+ SendInput(n, modkeys, sizeof(INPUT));
+ }
+ }
+
inputs[0].type = INPUT_KEYBOARD;
inputs[0].ki.wVk = vkCode;
if (STRICMP(event, "keyup") == 0)
+ {
inputs[0].ki.dwFlags = KEYEVENTF_KEYUP;
+ if(!isModKey)
+ unwrapMods = TRUE;
+ }
+
(void)SetForegroundWindow(s_hwnd);
SendInput(ARRAYSIZE(inputs), inputs, sizeof(INPUT));
! vim_free(event);

! if (unwrapMods)
! {
! modkeys[0].type = INPUT_KEYBOARD;
! modkeys[0].ki.wVk = VK_LSHIFT;
! modkeys[0].ki.dwFlags = KEYEVENTF_KEYUP;
!
! modkeys[1].type = INPUT_KEYBOARD;
! modkeys[1].ki.wVk = VK_LCONTROL;
! modkeys[1].ki.dwFlags = KEYEVENTF_KEYUP;
!
! modkeys[2].type = INPUT_KEYBOARD;
! modkeys[2].ki.wVk = VK_LMENU;
! modkeys[2].ki.dwFlags = KEYEVENTF_KEYUP;

+ (void)SetForegroundWindow(s_hwnd);
+ SendInput(3, modkeys, sizeof(INPUT));
+ }
+ }
+ else
+ {
+ if (event == NULL)
+ {
+ semsg(_(e_missing_argument_str), "event");
+ }
+ else
+ {
+ semsg(_(e_invalid_value_for_argument_str_str), "event", event);
+ vim_free(event);
+ }
+ return FALSE;
+ }
return TRUE;
}
+
+ int
+ test_gui_w32_sendevent(char_u *event, dict_T *args)
+ {
+ if (STRICMP(event, "key") == 0)
+ return test_gui_w32_sendevent_keyboard(args);
+ else if (STRICMP(event, "mouse") == 0)
+ return test_gui_w32_sendevent_mouse(args);
+ else
+ {
+ semsg(_(e_invalid_value_for_argument_str_str), "event", event);
+ return FALSE;
+ }
+ }
#endif
*** ../vim-9.0.1083/src/os_win32.c 2022-11-30 18:11:52.690904297 +0000
--- src/os_win32.c 2022-12-20 19:43:38.056916734 +0000
***************
*** 177,182 ****
--- 177,201 ----
static void standout(void);
static int s_cursor_visible = TRUE;
static int did_create_conin = FALSE;
+ // The 'input_record_buffer' is an internal dynamic fifo queue of MS-Windows
+ // console INPUT_RECORD events that are normally read from the console input
+ // buffer. This provides an injection point for testing the low-level handling
+ // of INPUT_RECORDs.
+ typedef struct input_record_buffer_node_S
+ {
+ INPUT_RECORD ir;
+ struct input_record_buffer_node_S *next;
+ } input_record_buffer_node_T;
+ typedef struct input_record_buffer_S
+ {
+ input_record_buffer_node_T *head;
+ input_record_buffer_node_T *tail;
+ int length;
+ } input_record_buffer_T;
+ static input_record_buffer_T input_record_buffer;
+ static int peek_input_record_buffer(INPUT_RECORD* irEvents, int nMaxLength);
+ static int read_input_record_buffer(INPUT_RECORD* irEvents, int nMaxLength);
+ static int write_input_record_buffer(INPUT_RECORD* irEvents, int nLength);
#endif
#ifdef FEAT_GUI_MSWIN
static int s_dont_use_vimrun = TRUE;
***************
*** 224,230 ****
static void set_console_color_rgb(void);
static void reset_console_color_rgb(void);
static void restore_console_color_rgb(void);
! #endif

// This flag is newly created from Windows 10
#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
--- 243,249 ----
static void set_console_color_rgb(void);
static void reset_console_color_rgb(void);
static void restore_console_color_rgb(void);
! #endif // !FEAT_GUI_MSWIN || VIMDLL

// This flag is newly created from Windows 10
#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
***************
*** 319,324 ****
--- 338,350 ----
int i;
static INPUT_RECORD s_irPseudo;

+ if (s_dwMax == 0 && input_record_buffer.length > 0)
+ {
+ dwEvents = read_input_record_buffer(s_irCache, IRSIZE);
+ s_dwIndex = 0;
+ s_dwMax = dwEvents;
+ }
+
if (nLength == -2)
return (s_dwMax > 0) ? TRUE : FALSE;

***************
*** 431,437 ****
return WaitForSingleObject(hHandle, dwMilliseconds);
}
# endif
! #endif

static void
get_exe_name(void)
--- 457,463 ----
return WaitForSingleObject(hHandle, dwMilliseconds);
}
# endif
! #endif // !FEAT_GUI_MSWIN || VIMDLL

static void
get_exe_name(void)
***************
*** 1014,1020 ****
return 1;
}

! if (pker->uChar.UnicodeChar != 0)
return 1;

CLEAR_FIELD(abKeystate);
--- 1040,1046 ----
return 1;
}

! if (pker->uChar.UnicodeChar > 0 && pker->uChar.UnicodeChar < 0xfffd)
return 1;

CLEAR_FIELD(abKeystate);
***************
*** 1080,1086 ****

// special cases
if ((nModifs & CTRL) != 0 && (nModifs & ~CTRL) == 0
! && pker->uChar.UnicodeChar == NUL)
{
// Ctrl-6 is Ctrl-^
if (pker->wVirtualKeyCode == '6')
--- 1106,1113 ----

// special cases
if ((nModifs & CTRL) != 0 && (nModifs & ~CTRL) == 0
! && (pker->uChar.UnicodeChar == NUL
! || pker->uChar.UnicodeChar == 0xfffd))
{
// Ctrl-6 is Ctrl-^
if (pker->wVirtualKeyCode == '6')
***************
*** 1168,1174 ****
return (*pch != NUL);
}

! #endif // FEAT_GUI_MSWIN


/*
--- 1195,1307 ----
return (*pch != NUL);
}

! # if defined(FEAT_EVAL)
! static int
! encode_key_event(dict_T *args, INPUT_RECORD *ir)
! {
! static int s_dwMods = 0;
!
! char_u *event = dict_get_string(args, "event", TRUE);
! if (event && (STRICMP(event, "keydown") == 0
! || STRICMP(event, "keyup") == 0))
! {
! WORD vkCode = dict_get_number_def(args, "keycode", 0);
! if (vkCode <= 0 || vkCode >= 0xFF)
! {
! semsg(_(e_invalid_argument_nr), (long)vkCode);
! return FALSE;
! }
!
! ir->EventType = KEY_EVENT;
! KEY_EVENT_RECORD ker;
! ZeroMemory(&ker, sizeof(ker));
! ker.bKeyDown = STRICMP(event, "keydown") == 0;
! ker.wRepeatCount = 1;
! ker.wVirtualScanCode = 0;
! ker.dwControlKeyState = 0;
! int mods = (int)dict_get_number(args, "modifiers");
! // Encode the win32 console key modifiers from Vim keyboard modifiers.
! if (mods)
! {
! // If "modifiers" is explicitly set in the args, then we reset any
! // remembered modifer key state that may have been set from earlier
! // mod-key-down events, even if they are not yet unset by earlier
! // mod-key-up events.
! s_dwMods = 0;
! if (mods & MOD_MASK_SHIFT)
! ker.dwControlKeyState |= SHIFT_PRESSED;
! if (mods & MOD_MASK_CTRL)
! ker.dwControlKeyState |= LEFT_CTRL_PRESSED;
! if (mods & MOD_MASK_ALT)
! ker.dwControlKeyState |= LEFT_ALT_PRESSED;
! }
!
! if (vkCode == VK_LSHIFT || vkCode == VK_RSHIFT || vkCode == VK_SHIFT)
! {
! if (STRICMP(event, "keydown") == 0)
! s_dwMods |= SHIFT_PRESSED;
! else
! s_dwMods &= ~SHIFT_PRESSED;
! }
! else if (vkCode == VK_LCONTROL || vkCode == VK_CONTROL)
! {
! if (STRICMP(event, "keydown") == 0)
! s_dwMods |= LEFT_CTRL_PRESSED;
! else
! s_dwMods &= ~LEFT_CTRL_PRESSED;
! }
! else if (vkCode == VK_RCONTROL)
! {
! if (STRICMP(event, "keydown") == 0)
! s_dwMods |= RIGHT_CTRL_PRESSED;
! else
! s_dwMods &= ~RIGHT_CTRL_PRESSED;
! }
! else if (vkCode == VK_LMENU || vkCode == VK_MENU)
! {
! if (STRICMP(event, "keydown") == 0)
! s_dwMods |= LEFT_ALT_PRESSED;
! else
! s_dwMods &= ~LEFT_ALT_PRESSED;
! }
! else if (vkCode == VK_RMENU)
! {
! if (STRICMP(event, "keydown") == 0)
! s_dwMods |= RIGHT_ALT_PRESSED;
! else
! s_dwMods &= ~RIGHT_ALT_PRESSED;
! }
! ker.dwControlKeyState |= s_dwMods;
! ker.wVirtualKeyCode = vkCode;
! win32_kbd_patch_key(&ker);
!
! for (int i = ARRAY_LENGTH(VirtKeyMap);
! --i >= 0 && !ker.uChar.UnicodeChar; )
! {
! if (VirtKeyMap[i].wVirtKey == vkCode)
! ker.uChar.UnicodeChar = 0xfffd; // REPLACEMENT CHARACTER
! }
!
! ir->Event.KeyEvent = ker;
! vim_free(event);
! }
! else
! {
! if (event == NULL)
! {
! semsg(_(e_missing_argument_str), "event");
! }
! else
! {
! semsg(_(e_invalid_value_for_argument_str_str), "event", event);
! vim_free(event);
! }
! return FALSE;
! }
! return TRUE;
! }
! # endif // FEAT_EVAL
! #endif // !FEAT_GUI_MSWIN || VIMDLL


/*
***************
*** 1179,1185 ****
mch_setmouse(int on UNUSED)
{
}
! #else
static int g_fMouseAvail = FALSE; // mouse present
static int g_fMouseActive = FALSE; // mouse enabled
static int g_nMouseClick = -1; // mouse status
--- 1312,1318 ----
mch_setmouse(int on UNUSED)
{
}
! #else // !FEAT_GUI_MSWIN || VIMDLL
static int g_fMouseAvail = FALSE; // mouse present
static int g_fMouseActive = FALSE; // mouse enabled
static int g_nMouseClick = -1; // mouse status
***************
*** 1234,1254 ****

/*
* Win32 console mouse scroll event handler.
! * Loosely based on the _OnMouseWheel() function in gui_w32.c
*
* This encodes the mouse scroll direction and keyboard modifiers into
* g_nMouseClick, and the mouse position into g_xMouse and g_yMouse
*
* The direction of the scroll is decoded from two fields of the win32 console
* mouse event record;
! * 1. The axis - vertical or horizontal flag - from dwEventFlags, and
* 2. The sign - positive or negative (aka delta flag) - from dwButtonState
*
! * When scroll axis is HORIZONTAL
* - If the high word of the dwButtonState member contains a positive
* value, the wheel was rotated to the right.
* - Otherwise, the wheel was rotated to the left.
! * When scroll axis is VERTICAL
* - If the high word of the dwButtonState member contains a positive value,
* the wheel was rotated forward, away from the user.
* - Otherwise, the wheel was rotated backward, toward the user.
--- 1367,1387 ----

/*
* Win32 console mouse scroll event handler.
! * Console version of the _OnMouseWheel() function in gui_w32.c
*
* This encodes the mouse scroll direction and keyboard modifiers into
* g_nMouseClick, and the mouse position into g_xMouse and g_yMouse
*
* The direction of the scroll is decoded from two fields of the win32 console
* mouse event record;
! * 1. The orientation - vertical or horizontal flag - from dwEventFlags
* 2. The sign - positive or negative (aka delta flag) - from dwButtonState
*
! * When scroll orientation is HORIZONTAL
* - If the high word of the dwButtonState member contains a positive
* value, the wheel was rotated to the right.
* - Otherwise, the wheel was rotated to the left.
! * When scroll orientation is VERTICAL
* - If the high word of the dwButtonState member contains a positive value,
* the wheel was rotated forward, away from the user.
* - Otherwise, the wheel was rotated backward, toward the user.
***************
*** 1594,1601 ****
return TRUE;
}

! #endif // FEAT_GUI_MSWIN


#ifdef MCH_CURSOR_SHAPE
/*
--- 1727,1957 ----
return TRUE;
}

! # ifdef FEAT_EVAL
! static int
! encode_mouse_event(dict_T *args, INPUT_RECORD *ir)
! {
! int button;
! int row;
! int col;
! int repeated_click;
! int_u mods;
! int move;
!
! if (!dict_has_key(args, "row") || !dict_has_key(args, "col"))
! return FALSE;
!
! // Note: "move" is optional, requires fewer arguments
! move = (int)dict_get_bool(args, "move", FALSE);
! if (!move && (!dict_has_key(args, "button")
! || !dict_has_key(args, "multiclick")
! || !dict_has_key(args, "modifiers")))
! return FALSE;
!
! row = (int)dict_get_number(args, "row") - 1;
! col = (int)dict_get_number(args, "col") - 1;
!
! ir->EventType = MOUSE_EVENT;
! MOUSE_EVENT_RECORD mer;
! ZeroMemory(&mer, sizeof(mer));
! mer.dwMousePosition.X = col;
! mer.dwMousePosition.Y = row;
!
! if (move)
! {
! mer.dwButtonState = 0;
! mer.dwEventFlags = MOUSE_MOVED;
! }
! else
! {
! button = (int)dict_get_number(args, "button");
! repeated_click = (int)dict_get_number(args, "multiclick");
! mods = (int)dict_get_number(args, "modifiers");
! // Reset the scroll values to known values.
! // XXX: Remove this when/if the scroll step is made configurable.
! mouse_set_hor_scroll_step(6);
! mouse_set_vert_scroll_step(3);
!
! switch (button)
! {
! case MOUSE_LEFT:
! mer.dwButtonState = FROM_LEFT_1ST_BUTTON_PRESSED;
! mer.dwEventFlags = repeated_click == 1 ? DOUBLE_CLICK : 0;
! break;
! case MOUSE_MIDDLE:
! mer.dwButtonState = FROM_LEFT_2ND_BUTTON_PRESSED;
! mer.dwEventFlags = repeated_click == 1 ? DOUBLE_CLICK : 0;
! break;
! case MOUSE_RIGHT:
! mer.dwButtonState = RIGHTMOST_BUTTON_PRESSED;
! mer.dwEventFlags = repeated_click == 1 ? DOUBLE_CLICK : 0;
! break;
! case MOUSE_RELEASE:
! // umm? Assume Left Release?
! mer.dwEventFlags = 0;
!
! case MOUSE_MOVE:
! mer.dwButtonState = 0;
! mer.dwEventFlags = MOUSE_MOVED;
! break;
! case MOUSE_X1:
! mer.dwButtonState = FROM_LEFT_3RD_BUTTON_PRESSED;
! break;
! case MOUSE_X2:
! mer.dwButtonState = FROM_LEFT_4TH_BUTTON_PRESSED;
! break;
! case MOUSE_4: // KE_MOUSEDOWN;
! mer.dwButtonState = -1;
! mer.dwEventFlags = MOUSE_WHEELED;
! break;
! case MOUSE_5: // KE_MOUSEUP;
! mer.dwButtonState = +1;
! mer.dwEventFlags = MOUSE_WHEELED;
! break;
! case MOUSE_6: // KE_MOUSELEFT;
! mer.dwButtonState = -1;
! mer.dwEventFlags = MOUSE_HWHEELED;
! break;
! case MOUSE_7: // KE_MOUSERIGHT;
! mer.dwButtonState = +1;
! mer.dwEventFlags = MOUSE_HWHEELED;
! break;
! default:
! semsg(_(e_invalid_argument_str), "button");
! return FALSE;
! }
! }
!
! mer.dwControlKeyState = 0;
! if (mods != 0)
! {
! // Encode the win32 console key modifiers from Vim MOUSE modifiers.
! if (mods & MOUSE_SHIFT)
! mer.dwControlKeyState |= SHIFT_PRESSED;
! if (mods & MOUSE_CTRL)
! mer.dwControlKeyState |= LEFT_CTRL_PRESSED;
! if (mods & MOUSE_ALT)
! mer.dwControlKeyState |= LEFT_ALT_PRESSED;
! }
! ir->Event.MouseEvent = mer;
! return TRUE;
! }
! # endif // FEAT_EVAL
!
! static int
! write_input_record_buffer(INPUT_RECORD* irEvents, int nLength)
! {
! int nCount = 0;
! while (nCount < nLength)
! {
! input_record_buffer.length++;
! input_record_buffer_node_T *event_node =
! malloc(sizeof(input_record_buffer_node_T));
! event_node->ir = irEvents[nCount++];
! event_node->next = NULL;
! if (input_record_buffer.tail == NULL)
! {
! input_record_buffer.head = event_node;
! input_record_buffer.tail = event_node;
! }
! else
! {
! input_record_buffer.tail->next = event_node;
! input_record_buffer.tail = input_record_buffer.tail->next;
! }
! }
! return nCount;
! }
!
! static int
! read_input_record_buffer(INPUT_RECORD* irEvents, int nMaxLength)
! {
! int nCount = 0;
! while (nCount < nMaxLength && input_record_buffer.head != NULL)
! {
! input_record_buffer.length--;
! input_record_buffer_node_T *pop_head = input_record_buffer.head;
! irEvents[nCount++] = pop_head->ir;
! input_record_buffer.head = pop_head->next;
! vim_free(pop_head);
! if (input_record_buffer.length == 0)
! input_record_buffer.tail = NULL;
! }
! return nCount;
! }
! static int
! peek_input_record_buffer(INPUT_RECORD* irEvents, int nMaxLength)
! {
! int nCount = 0;
! input_record_buffer_node_T *temp = input_record_buffer.head;
! while (nCount < nMaxLength && temp != NULL)
! {
! irEvents[nCount++] = temp->ir;
! temp = temp->next;
! }
! return nCount;
! }
! #endif // !FEAT_GUI_MSWIN || VIMDLL
!
! #ifdef FEAT_EVAL
! /*
! * The 'test_mswin_event' function is for testing Vim's low-level handling of
! * user input events. ie, this manages the encoding of INPUT_RECORD events
! * so that we have a way to test how Vim decodes INPUT_RECORD events in Windows
! * consoles.
! *
! * The 'test_mswin_event' function is based on 'test_gui_event'. In fact, when
! * the Windows GUI is running, the arguments; 'event' and 'args', are the same.
! * So, it acts as an alias for 'test_gui_event' for the Windows GUI.
! *
! * When the Windows console is running, the arguments; 'event' and 'args', are
! * a subset of what 'test_gui_event' handles, ie, only "key" and "mouse"
! * events are encoded as INPUT_RECORD events.
! *
! * Note: INPUT_RECORDs are only used by the Windows console, not the GUI. The
! * GUI sends MSG structs instead.
! */
! int
! test_mswin_event(char_u *event, dict_T *args)
! {
! int lpEventsWritten = 0;
!
! # if defined(VIMDLL) || defined(FEAT_GUI_MSWIN)
! if (gui.in_use)
! return test_gui_w32_sendevent(event, args);
! # endif
!
! # if defined(VIMDLL) || !defined(FEAT_GUI_MSWIN)
!
! // Currently implemented event record types are; KEY_EVENT and MOUSE_EVENT
! // Potentially could also implement: FOCUS_EVENT and WINDOW_BUFFER_SIZE_EVENT
! // Maybe also: MENU_EVENT
!
! INPUT_RECORD ir;
! BOOL input_encoded = FALSE;
! if (STRCMP(event, "key") == 0)
! input_encoded = encode_key_event(args, &ir);
! else if (STRCMP(event, "mouse") == 0)
! input_encoded = encode_mouse_event(args, &ir);
! else
! {
! semsg(_(e_invalid_value_for_argument_str_str), "event", event);
! return FALSE;
! }
!
! // Ideally, WriteConsoleInput would be used to inject these low-level
! // events. But, this doesnt work well in the CI test environment. So
! // implementing an input_record_buffer instead.
! if (input_encoded)
! lpEventsWritten = write_input_record_buffer(&ir, 1);

+ if (STRCMP(event, "mouse") == 0)
+ exec_normal(TRUE, TRUE, TRUE);
+
+ # endif
+ return lpEventsWritten;
+ }
+ #endif // FEAT_EVAL

#ifdef MCH_CURSOR_SHAPE
/*
*** ../vim-9.0.1083/src/proto/gui_w32.pro 2022-07-23 05:04:07.580839529 +0100
--- src/proto/gui_w32.pro 2022-12-20 19:45:50.336740416 +0000
***************
*** 96,100 ****
BalloonEval *gui_mch_create_beval_area(void *target, char_u *mesg, void (*mesgCB)(BalloonEval *, int), void *clientData);
void gui_mch_destroy_beval_area(BalloonEval *beval);
void netbeans_draw_multisign_indicator(int row);
! int test_gui_w32_sendevent(dict_T *args);
/* vim: set ft=c : */
--- 96,100 ----
BalloonEval *gui_mch_create_beval_area(void *target, char_u *mesg, void (*mesgCB)(BalloonEval *, int), void *clientData);
void gui_mch_destroy_beval_area(BalloonEval *beval);
void netbeans_draw_multisign_indicator(int row);
! int test_gui_w32_sendevent(char_u *event, dict_T *args);
/* vim: set ft=c : */
*** ../vim-9.0.1083/src/proto/os_win32.pro 2022-11-12 18:45:59.818782978 +0000
--- src/proto/os_win32.pro 2022-12-20 19:46:12.692713979 +0000
***************
*** 9,14 ****
--- 9,15 ----
void PlatformId(void);
void mch_setmouse(int on);
void mch_bevalterm_changed(void);
+ int test_mswin_event(char_u *event, dict_T *args);
void mch_update_cursor(void);
int mch_char_avail(void);
int mch_check_messages(void);
*** ../vim-9.0.1083/src/proto/testing.pro 2022-06-27 23:15:25.000000000 +0100
--- src/proto/testing.pro 2022-12-20 19:47:23.100636236 +0000
***************
*** 33,38 ****
--- 33,39 ----
void f_test_unknown(typval_T *argvars, typval_T *rettv);
void f_test_void(typval_T *argvars, typval_T *rettv);
void f_test_setmouse(typval_T *argvars, typval_T *rettv);
+ void f_test_mswin_event(typval_T *argvars, typval_T *rettv);
void f_test_gui_event(typval_T *argvars, typval_T *rettv);
void f_test_settime(typval_T *argvars, typval_T *rettv);
/* vim: set ft=c : */
*** ../vim-9.0.1083/src/term.c 2022-12-19 18:56:44.169594372 +0000
--- src/term.c 2022-12-20 19:22:31.977188037 +0000
***************
*** 827,833 ****
};

/*
! * These codes are valid for the Win32 Console . The entries that start with
* ESC | are translated into console calls in os_win32.c. The function keys
* are also translated in os_win32.c.
*/
--- 827,833 ----
};

/*
! * These codes are valid for the Win32 Console. The entries that start with
* ESC | are translated into console calls in os_win32.c. The function keys
* are also translated in os_win32.c.
*/
*** ../vim-9.0.1083/src/testing.c 2022-12-08 15:32:11.083034191 +0000
--- src/testing.c 2022-12-20 19:22:31.977188037 +0000
***************
*** 1388,1400 ****

if (move)
{
if (dict_get_bool(args, "cell", FALSE))
{
! // click in the middle of the character cell
! row = row * gui.char_height + gui.char_height / 2;
! col = col * gui.char_width + gui.char_width / 2;
}
! gui_mouse_moved(col, row);
}
else
{
--- 1388,1405 ----

if (move)
{
+ int pY = row;
+ int pX = col;
+ // the "move" argument expects row and col coordnates to be in pixels,
+ // unless "cell" is specified and is TRUE.
if (dict_get_bool(args, "cell", FALSE))
{
! // calculate the middle of the character cell
! // Note: Cell coordinates are 1-based from vimscript
! pY = (row - 1) * gui.char_height + gui.char_height / 2;
! pX = (col - 1) * gui.char_width + gui.char_width / 2;
}
! gui_mouse_moved(pX, pY);
}
else
{
***************
*** 1489,1494 ****
--- 1494,1523 ----
# endif

void
+ f_test_mswin_event(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
+ {
+ # ifdef MSWIN
+ rettv->v_type = VAR_BOOL;
+ rettv->vval.v_number = FALSE;
+
+ if (sandbox != 0)
+ {
+ emsg(_(e_not_allowed_in_sandbox));
+ return;
+ }
+
+ if (check_for_string_arg(argvars, 0) == FAIL
+ || check_for_dict_arg(argvars, 1) == FAIL
+ || argvars[1].vval.v_dict == NULL)
+ return;
+
+ char_u *event = tv_get_string(&argvars[0]);
+ rettv->vval.v_number = test_mswin_event(event, argvars[1].vval.v_dict);
+
+ # endif
+ }
+
+ void
f_test_gui_event(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
{
# ifdef FEAT_GUI
***************
*** 1515,1520 ****
--- 1544,1553 ----
else if (STRCMP(event, "findrepl") == 0)
rettv->vval.v_number = test_gui_find_repl(argvars[1].vval.v_dict);
# endif
+ # ifdef MSWIN
+ else if (STRCMP(event, "key") == 0 || STRCMP(event, "mouse") == 0)
+ rettv->vval.v_number = test_mswin_event(event, argvars[1].vval.v_dict);
+ # endif
else if (STRCMP(event, "mouse") == 0)
rettv->vval.v_number = test_gui_mouse_event(argvars[1].vval.v_dict);
else if (STRCMP(event, "scrollbar") == 0)
***************
*** 1523,1532 ****
rettv->vval.v_number = test_gui_tabline_event(argvars[1].vval.v_dict);
else if (STRCMP(event, "tabmenu") == 0)
rettv->vval.v_number = test_gui_tabmenu_event(argvars[1].vval.v_dict);
- # ifdef FEAT_GUI_MSWIN
- else if (STRCMP(event, "sendevent") == 0)
- rettv->vval.v_number = test_gui_w32_sendevent(argvars[1].vval.v_dict);
- # endif
else
{
semsg(_(e_invalid_argument_str), event);
--- 1556,1561 ----
*** ../vim-9.0.1083/src/testdir/Make_all.mak 2022-12-08 15:32:11.087034211 +0000
--- src/testdir/Make_all.mak 2022-12-20 19:22:31.977188037 +0000
***************
*** 210,215 ****
--- 210,216 ----
test_modeless \
test_modeline \
test_move \
+ test_mswin_event \
test_mzscheme \
test_nested_function \
test_netbeans \
***************
*** 454,459 ****
--- 455,461 ----
test_mksession.res \
test_modeless.res \
test_modeline.res \
+ test_mswin_event.res \
test_mzscheme.res \
test_nested_function.res \
test_netbeans.res \
*** ../vim-9.0.1083/src/testdir/mouse.vim 2020-07-18 11:57:17.000000000 +0100
--- src/testdir/mouse.vim 2022-12-20 19:49:05.844535922 +0000
***************
*** 20,25 ****
--- 20,46 ----
let g:Ttymouse_netterm = []
endif

+ " Vim Mouse Codes.
+ " Used by the GUI and by MS-Windows Consoles.
+ " Keep these in sync with vim.h
+ let s:MOUSE_CODE = {
+ \ 'BTN_LEFT' : 0x00,
+ \ 'BTN_MIDDLE' : 0x01,
+ \ 'BTN_RIGHT' : 0x02,
+ \ 'BTN_RELEASE' : 0x03,
+ \ 'BTN_X1' : 0x300,
+ \ 'BTN_X2' : 0x400,
+ \ 'SCRL_DOWN' : 0x100,
+ \ 'SCRL_UP' : 0x200,
+ \ 'SCRL_LEFT' : 0x500,
+ \ 'SCRL_RIGHT' : 0x600,
+ \ 'MOVE' : 0x700,
+ \ 'MOD_SHIFT' : 0x04,
+ \ 'MOD_ALT' : 0x08,
+ \ 'MOD_CTRL' : 0x10,
+ \ }
+
+
" Helper function to emit a terminal escape code.
func TerminalEscapeCode(code, row, col, m)
if &ttymouse ==# 'xterm2'
***************
*** 47,52 ****
--- 68,98 ----
return printf("\<Esc>}%d,%d\r", a:row, a:col)
endfunc

+ " Send low level mouse event to MS-Windows consoles or GUI
+ func MSWinMouseEvent(button, row, col, move, multiclick, modifiers)
+ let args = { }
+ let args.button = a:button
+ " Scroll directions are inverted in the GUI, no idea why.
+ if has('gui_running')
+ if a:button == s:MOUSE_CODE.SCRL_UP
+ let args.button = s:MOUSE_CODE.SCRL_DOWN
+ elseif a:button == s:MOUSE_CODE.SCRL_DOWN
+ let args.button = s:MOUSE_CODE.SCRL_UP
+ elseif a:button == s:MOUSE_CODE.SCRL_LEFT
+ let args.button = s:MOUSE_CODE.SCRL_RIGHT
+ elseif a:button == s:MOUSE_CODE.SCRL_RIGHT
+ let args.button = s:MOUSE_CODE.SCRL_LEFT
+ endif
+ endif
+ let args.row = a:row
+ let args.col = a:col
+ let args.move = a:move
+ let args.multiclick = a:multiclick
+ let args.modifiers = a:modifiers
+ call test_mswin_event("mouse", args)
+ unlet args
+ endfunc
+
func MouseLeftClickCode(row, col)
if &ttymouse ==# 'dec'
return DecEscapeCode(2, 4, a:row, a:col)
***************
*** 58,64 ****
endfunc

func MouseLeftClick(row, col)
! call feedkeys(MouseLeftClickCode(a:row, a:col), 'Lx!')
endfunc

func MouseMiddleClickCode(row, col)
--- 104,114 ----
endfunc

func MouseLeftClick(row, col)
! if has('win32')
! call MSWinMouseEvent(s:MOUSE_CODE.BTN_LEFT, a:row, a:col, 0, 0, 0)
! else
! call feedkeys(MouseLeftClickCode(a:row, a:col), 'Lx!')
! endif
endfunc

func MouseMiddleClickCode(row, col)
***************
*** 70,76 ****
endfunc

func MouseMiddleClick(row, col)
! call feedkeys(MouseMiddleClickCode(a:row, a:col), 'Lx!')
endfunc

func MouseRightClickCode(row, col)
--- 120,130 ----
endfunc

func MouseMiddleClick(row, col)
! if has('win32')
! call MSWinMouseEvent(s:MOUSE_CODE.BTN_MIDDLE, a:row, a:col, 0, 0, 0)
! else
! call feedkeys(MouseMiddleClickCode(a:row, a:col), 'Lx!')
! endif
endfunc

func MouseRightClickCode(row, col)
***************
*** 82,88 ****
endfunc

func MouseRightClick(row, col)
! call feedkeys(MouseRightClickCode(a:row, a:col), 'Lx!')
endfunc

func MouseCtrlLeftClickCode(row, col)
--- 136,146 ----
endfunc

func MouseRightClick(row, col)
! if has('win32')
! call MSWinMouseEvent(s:MOUSE_CODE.BTN_RIGHT, a:row, a:col, 0, 0, 0)
! else
! call feedkeys(MouseRightClickCode(a:row, a:col), 'Lx!')
! endif
endfunc

func MouseCtrlLeftClickCode(row, col)
***************
*** 91,97 ****
endfunc

func MouseCtrlLeftClick(row, col)
! call feedkeys(MouseCtrlLeftClickCode(a:row, a:col), 'Lx!')
endfunc

func MouseCtrlRightClickCode(row, col)
--- 149,160 ----
endfunc

func MouseCtrlLeftClick(row, col)
! if has('win32')
! call MSWinMouseEvent(s:MOUSE_CODE.BTN_LEFT, a:row, a:col, 0, 0,
! \ s:MOUSE_CODE.MOD_CTRL)
! else
! call feedkeys(MouseCtrlLeftClickCode(a:row, a:col), 'Lx!')
! endif
endfunc

func MouseCtrlRightClickCode(row, col)
***************
*** 100,106 ****
endfunc

func MouseCtrlRightClick(row, col)
! call feedkeys(MouseCtrlRightClickCode(a:row, a:col), 'Lx!')
endfunc

func MouseAltLeftClickCode(row, col)
--- 163,174 ----
endfunc

func MouseCtrlRightClick(row, col)
! if has('win32')
! call MSWinMouseEvent(s:MOUSE_CODE.BTN_RIGHT, a:row, a:col, 0, 0,
! \ s:MOUSE_CODE.MOD_CTRL)
! else
! call feedkeys(MouseCtrlRightClickCode(a:row, a:col), 'Lx!')
! endif
endfunc

func MouseAltLeftClickCode(row, col)
***************
*** 109,115 ****
endfunc

func MouseAltLeftClick(row, col)
! call feedkeys(MouseAltLeftClickCode(a:row, a:col), 'Lx!')
endfunc

func MouseAltRightClickCode(row, col)
--- 177,188 ----
endfunc

func MouseAltLeftClick(row, col)
! if has('win32')
! call MSWinMouseEvent(s:MOUSE_CODE.BTN_LEFT, a:row, a:col, 0, 0,
! \ s:MOUSE_CODE.MOD_ALT)
! else
! call feedkeys(MouseAltLeftClickCode(a:row, a:col), 'Lx!')
! endif
endfunc

func MouseAltRightClickCode(row, col)
***************
*** 118,124 ****
endfunc

func MouseAltRightClick(row, col)
! call feedkeys(MouseAltRightClickCode(a:row, a:col), 'Lx!')
endfunc

func MouseLeftReleaseCode(row, col)
--- 191,202 ----
endfunc

func MouseAltRightClick(row, col)
! if has('win32')
! call MSWinMouseEvent(s:MOUSE_CODE.BTN_RIGHT, a:row, a:col, 0, 0,
! \ s:MOUSE_CODE.MOD_ALT)
! else
! call feedkeys(MouseAltRightClickCode(a:row, a:col), 'Lx!')
! endif
endfunc

func MouseLeftReleaseCode(row, col)
***************
*** 132,138 ****
endfunc

func MouseLeftRelease(row, col)
! call feedkeys(MouseLeftReleaseCode(a:row, a:col), 'Lx!')
endfunc

func MouseMiddleReleaseCode(row, col)
--- 210,220 ----
endfunc

func MouseLeftRelease(row, col)
! if has('win32')
! call MSWinMouseEvent(s:MOUSE_CODE.BTN_RELEASE, a:row, a:col, 0, 0, 0)
! else
! call feedkeys(MouseLeftReleaseCode(a:row, a:col), 'Lx!')
! endif
endfunc

func MouseMiddleReleaseCode(row, col)
***************
*** 144,150 ****
endfunc

func MouseMiddleRelease(row, col)
! call feedkeys(MouseMiddleReleaseCode(a:row, a:col), 'Lx!')
endfunc

func MouseRightReleaseCode(row, col)
--- 226,236 ----
endfunc

func MouseMiddleRelease(row, col)
! if has('win32')
! call MSWinMouseEvent(s:MOUSE_CODE.BTN_RELEASE, a:row, a:col, 0, 0, 0)
! else
! call feedkeys(MouseMiddleReleaseCode(a:row, a:col), 'Lx!')
! endif
endfunc

func MouseRightReleaseCode(row, col)
***************
*** 156,162 ****
endfunc

func MouseRightRelease(row, col)
! call feedkeys(MouseRightReleaseCode(a:row, a:col), 'Lx!')
endfunc

func MouseLeftDragCode(row, col)
--- 242,252 ----
endfunc

func MouseRightRelease(row, col)
! if has('win32')
! call MSWinMouseEvent(s:MOUSE_CODE.BTN_RELEASE, a:row, a:col, 0, 0, 0)
! else
! call feedkeys(MouseRightReleaseCode(a:row, a:col), 'Lx!')
! endif
endfunc

func MouseLeftDragCode(row, col)
***************
*** 168,174 ****
endfunc

func MouseLeftDrag(row, col)
! call feedkeys(MouseLeftDragCode(a:row, a:col), 'Lx!')
endfunc

func MouseWheelUpCode(row, col)
--- 258,268 ----
endfunc

func MouseLeftDrag(row, col)
! if has('win32')
! call MSWinMouseEvent(s:MOUSE_CODE.BTN_LEFT, a:row, a:col, 1, 0, 0)
! else
! call feedkeys(MouseLeftDragCode(a:row, a:col), 'Lx!')
! endif
endfunc

func MouseWheelUpCode(row, col)
***************
*** 176,182 ****
endfunc

func MouseWheelUp(row, col)
! call feedkeys(MouseWheelUpCode(a:row, a:col), 'Lx!')
endfunc

func MouseWheelDownCode(row, col)
--- 270,280 ----
endfunc

func MouseWheelUp(row, col)
! if has('win32')
! call MSWinMouseEvent(s:MOUSE_CODE.SCRL_UP, a:row, a:col, 0, 0, 0)
! else
! call feedkeys(MouseWheelUpCode(a:row, a:col), 'Lx!')
! endif
endfunc

func MouseWheelDownCode(row, col)
***************
*** 184,190 ****
endfunc

func MouseWheelDown(row, col)
! call feedkeys(MouseWheelDownCode(a:row, a:col), 'Lx!')
endfunc

func MouseWheelLeftCode(row, col)
--- 282,292 ----
endfunc

func MouseWheelDown(row, col)
! if has('win32')
! call MSWinMouseEvent(s:MOUSE_CODE.SCRL_DOWN, a:row, a:col, 0, 0, 0)
! else
! call feedkeys(MouseWheelDownCode(a:row, a:col), 'Lx!')
! endif
endfunc

func MouseWheelLeftCode(row, col)
***************
*** 192,198 ****
endfunc

func MouseWheelLeft(row, col)
! call feedkeys(MouseWheelLeftCode(a:row, a:col), 'Lx!')
endfunc

func MouseWheelRightCode(row, col)
--- 294,304 ----
endfunc

func MouseWheelLeft(row, col)
! if has('win32')
! call MSWinMouseEvent(s:MOUSE_CODE.SCRL_LEFT, a:row, a:col, 0, 0, 0)
! else
! call feedkeys(MouseWheelLeftCode(a:row, a:col), 'Lx!')
! endif
endfunc

func MouseWheelRightCode(row, col)
***************
*** 200,206 ****
endfunc

func MouseWheelRight(row, col)
! call feedkeys(MouseWheelRightCode(a:row, a:col), 'Lx!')
endfunc

" vim: shiftwidth=2 sts=2 expandtab
--- 306,372 ----
endfunc

func MouseWheelRight(row, col)
! if has('win32')
! call MSWinMouseEvent(s:MOUSE_CODE.SCRL_RIGHT, a:row, a:col, 0, 0, 0)
! else
! call feedkeys(MouseWheelRightCode(a:row, a:col), 'Lx!')
! endif
! endfunc
!
! func MouseShiftWheelUpCode(row, col)
! " todo feed shift mod.
! return TerminalEscapeCode(0x40, a:row, a:col, 'M')
! endfunc
!
! func MouseShiftWheelUp(row, col)
! if has('win32')
! call MSWinMouseEvent(s:MOUSE_CODE.SCRL_UP, a:row, a:col, 0, 0,
! \ s:MOUSE_CODE.MOD_SHIFT)
! else
! call feedkeys(MouseShiftWheelUpCode(a:row, a:col), 'Lx!')
! endif
! endfunc
!
! func MouseShiftWheelDownCode(row, col)
! " todo feed shift mod.
! return TerminalEscapeCode(0x41, a:row, a:col, 'M')
! endfunc
!
! func MouseShiftWheelDown(row, col)
! if has('win32')
! call MSWinMouseEvent(s:MOUSE_CODE.SCRL_DOWN, a:row, a:col, 0, 0,
! \ s:MOUSE_CODE.MOD_SHIFT)
! else
! call feedkeys(MouseShiftWheelDownCode(a:row, a:col), 'Lx!')
! endif
! endfunc
!
! func MouseShiftWheelLeftCode(row, col)
! " todo feed shift mod.
! return TerminalEscapeCode(0x42, a:row, a:col, 'M')
! endfunc
!
! func MouseShiftWheelLeft(row, col)
! if has('win32')
! call MSWinMouseEvent(s:MOUSE_CODE.SCRL_LEFT, a:row, a:col, 0, 0,
! \ s:MOUSE_CODE.MOD_SHIFT)
! else
! call feedkeys(MouseShiftWheelLeftCode(a:row, a:col), 'Lx!')
! endif
! endfunc
!
! func MouseShiftWheelRightCode(row, col)
! " todo feed shift mod.
! return TerminalEscapeCode(0x43, a:row, a:col, 'M')
! endfunc
!
! func MouseShiftWheelRight(row, col)
! if has('win32')
! call MSWinMouseEvent(s:MOUSE_CODE.SCRL_RIGHT, a:row, a:col, 0, 0,
! \ s:MOUSE_CODE.MOD_SHIFT)
! else
! call feedkeys(MouseShiftWheelRightCode(a:row, a:col), 'Lx!')
! endif
endfunc

" vim: shiftwidth=2 sts=2 expandtab
*** ../vim-9.0.1083/src/testdir/test_gui.vim 2022-11-28 21:20:44.416909580 +0000
--- src/testdir/test_gui.vim 2022-12-20 19:22:31.977188037 +0000
***************
*** 1281,1287 ****
let g:eventlist = g:eventlist[1 : ]
endif

! call assert_equal([#{row: 4, col: 31}, #{row: 11, col: 31}], g:eventlist)

" wiggle the mouse around within a screen cell, shouldn't trigger events
call extend(args, #{cell: v:false})
--- 1281,1287 ----
let g:eventlist = g:eventlist[1 : ]
endif

! call assert_equal([#{row: 3, col: 30}, #{row: 10, col: 30}], g:eventlist)

" wiggle the mouse around within a screen cell, shouldn't trigger events
call extend(args, #{cell: v:false})
***************
*** 1638,1647 ****
" Test for sending low level key presses
func SendKeys(keylist)
for k in a:keylist
! call test_gui_event("sendevent", #{event: "keydown", keycode: k})
endfor
for k in reverse(a:keylist)
! call test_gui_event("sendevent", #{event: "keyup", keycode: k})
endfor
endfunc

--- 1638,1647 ----
" Test for sending low level key presses
func SendKeys(keylist)
for k in a:keylist
! call test_gui_event("key", #{event: "keydown", keycode: k})
endfor
for k in reverse(a:keylist)
! call test_gui_event("key", #{event: "keyup", keycode: k})
endfor
endfunc

*** ../vim-9.0.1083/src/testdir/test_mswin_event.vim 2022-12-20 19:59:53.400124626 +0000
--- src/testdir/test_mswin_event.vim 2022-12-20 19:57:01.584208587 +0000
***************
*** 0 ****
--- 1,651 ----
+ " Test MS-Windows console event handling.
+
+ source check.vim
+ CheckMSWindows
+ " The mswin events should also work in gui
+
+ source mouse.vim
+
+ " Helper function for sending a sequence of low level key presses
+ " The modifer key(s) can be included as normal key presses in the sequence
+ func SendKeys(keylist)
+ for k in a:keylist
+ call test_mswin_event("key", #{event: "keydown", keycode: k})
+ endfor
+ for k in reverse(copy(a:keylist))
+ call test_mswin_event("key", #{event: "keyup", keycode: k})
+ endfor
+ endfunc
+
+ " Send an individual key press
+ " the modifers for the key press can be specified in the modifiers arg.
+ func SendKey(key, modifiers)
+ let args = { }
+ let args.keycode = a:key
+ let args.modifiers = a:modifiers
+ let args.event = "keydown"
+ call test_mswin_event("key", args)
+ let args.event = "keyup"
+ call test_mswin_event("key", args)
+ unlet args
+ endfunc
+
+ " Test MS-Windows console key events
+ func Test_mswin_key_event()
+ CheckMSWindows
+ new
+
+ " flush out any garbage left in the buffer
+ while getchar(0)
+ endwhile
+
+ let VK = #{
+ \ SPACE : 0x20,
+ \ SHIFT : 0x10,
+ \ LSHIFT : 0xA0,
+ \ RSHIFT : 0xA1,
+ \ CONTROL : 0x11,
+ \ LCONTROL : 0xA2,
+ \ RCONTROL : 0xA3,
+ \ MENU : 0x12,
+ \ ALT : 0x12,
+ \ LMENU : 0xA4,
+ \ LALT : 0xA4,
+ \ RMENU : 0xA5,
+ \ RALT : 0xA5,
+ \ OEM_1 : 0xBA,
+ \ OEM_2 : 0xBF,
+ \ OEM_3 : 0xC0,
+ \ OEM_4 : 0xDB,
+ \ OEM_5 : 0xDC,
+ \ OEM_6 : 0xDD,
+ \ OEM_7 : 0xDE,
+ \ OEM_PLUS : 0xBB,
+ \ OEM_COMMA : 0xBC,
+ \ OEM_MINUS : 0xBD,
+ \ OEM_PERIOD : 0xBE,
+ \ PRIOR : 0x21,
+ \ NEXT : 0x22,
+ \ END : 0x23,
+ \ HOME : 0x24,
+ \ LEFT : 0x25,
+ \ UP : 0x26,
+ \ RIGHT : 0x27,
+ \ DOWN : 0x28,
+ \ KEY_0 : 0x30,
+ \ KEY_1 : 0x31,
+ \ KEY_2 : 0x32,
+ \ KEY_3 : 0x33,
+ \ KEY_4 : 0x34,
+ \ KEY_5 : 0x35,
+ \ KEY_6 : 0x36,
+ \ KEY_7 : 0x37,
+ \ KEY_8 : 0x38,
+ \ KEY_9 : 0x39,
+ \ NUMPAD0 : 0x60,
+ \ NUMPAD1 : 0x61,
+ \ NUMPAD2 : 0x62,
+ \ NUMPAD3 : 0x63,
+ \ NUMPAD4 : 0x64,
+ \ NUMPAD5 : 0x65,
+ \ NUMPAD6 : 0x66,
+ \ NUMPAD7 : 0x67,
+ \ NUMPAD8 : 0x68,
+ \ NUMPAD9 : 0x69,
+ \ MULTIPLY : 0x6A,
+ \ ADD : 0x6B,
+ \ SUBTRACT : 0x6D,
+ \ F1 : 0x70,
+ \ F2 : 0x71,
+ \ F3 : 0x72,
+ \ F4 : 0x73,
+ \ F5 : 0x74,
+ \ F6 : 0x75,
+ \ F7 : 0x76,
+ \ F8 : 0x77,
+ \ F9 : 0x78,
+ \ F10 : 0x79,
+ \ F11 : 0x7A,
+ \ F12 : 0x7B,
+ \ KEY_A : 0x41,
+ \ KEY_B : 0x42,
+ \ KEY_C : 0x43,
+ \ KEY_D : 0x44,
+ \ KEY_E : 0x45,
+ \ KEY_F : 0x46,
+ \ KEY_G : 0x47,
+ \ KEY_H : 0x48,
+ \ KEY_I : 0x49,
+ \ KEY_J : 0x4A,
+ \ KEY_K : 0x4B,
+ \ KEY_L : 0x4C,
+ \ KEY_M : 0x4D,
+ \ KEY_N : 0x4E,
+ \ KEY_O : 0x4F,
+ \ KEY_P : 0x50,
+ \ KEY_Q : 0x51,
+ \ KEY_R : 0x52,
+ \ KEY_S : 0x53,
+ \ KEY_T : 0x54,
+ \ KEY_U : 0x55,
+ \ KEY_V : 0x56,
+ \ KEY_W : 0x57,
+ \ KEY_X : 0x58,
+ \ KEY_Y : 0x59,
+ \ KEY_Z : 0x5A
+ \ }
+
+ let vim_MOD_MASK_SHIFT = 0x02
+ let vim_MOD_MASK_CTRL = 0x04
+ let vim_MOD_MASK_ALT = 0x08
+
+ let vim_key_modifiers = [
+ \ ["", 0, []],
+ \ ["S-", 2, [VK.SHIFT]],
+ \ ["C-", 4, [VK.CONTROL]],
+ \ ["C-S-", 6, [VK.CONTROL, VK.SHIFT]],
+ \ ["A-", 8, [VK.MENU]],
+ \ ["A-S-", 10, [VK.MENU, VK.SHIFT]],
+ \ ["A-C-", 12, [VK.MENU, VK.CONTROL]],
+ \ ["A-C-S-", 14, [VK.MENU, VK.CONTROL, VK.SHIFT]],
+ \]
+
+ " Some punctuation characters
+ " Assuming Standard US PC Keyboard layout
+ let test_punctuation_keys = [
+ \ [[VK.SPACE], ' '],
+ \ [[VK.OEM_1], ';'],
+ \ [[VK.OEM_2], '/'],
+ \ [[VK.OEM_3], '`'],
+ \ [[VK.OEM_4], '['],
+ \ [[VK.OEM_5], '\'],
+ \ [[VK.OEM_6], ']'],
+ \ [[VK.OEM_7], ''''],
+ \ [[VK.OEM_PLUS], '='],
+ \ [[VK.OEM_COMMA], ','],
+ \ [[VK.OEM_MINUS], '-'],
+ \ [[VK.OEM_PERIOD], '.'],
+ \ [[VK.SHIFT, VK.OEM_1], ':'],
+ \ [[VK.SHIFT, VK.OEM_2], '?'],
+ \ [[VK.SHIFT, VK.OEM_3], '~'],
+ \ [[VK.SHIFT, VK.OEM_4], '{'],
+ \ [[VK.SHIFT, VK.OEM_5], '|'],
+ \ [[VK.SHIFT, VK.OEM_6], '}'],
+ \ [[VK.SHIFT, VK.OEM_7], '"'],
+ \ [[VK.SHIFT, VK.OEM_PLUS], '+'],
+ \ [[VK.SHIFT, VK.OEM_COMMA], '<'],
+ \ [[VK.SHIFT, VK.OEM_MINUS], '_'],
+ \ [[VK.SHIFT, VK.OEM_PERIOD], '>'],
+ \ [[VK.SHIFT, VK.KEY_1], '!'],
+ \ [[VK.SHIFT, VK.KEY_2], '@'],
+ \ [[VK.SHIFT, VK.KEY_3], '#'],
+ \ [[VK.SHIFT, VK.KEY_4], '$'],
+ \ [[VK.SHIFT, VK.KEY_5], '%'],
+ \ [[VK.SHIFT, VK.KEY_6], '^'],
+ \ [[VK.SHIFT, VK.KEY_7], '&'],
+ \ [[VK.SHIFT, VK.KEY_8], '*'],
+ \ [[VK.SHIFT, VK.KEY_9], '('],
+ \ [[VK.SHIFT, VK.KEY_0], ')'],
+ \ [[VK.LSHIFT, VK.KEY_9], '('],
+ \ [[VK.RSHIFT, VK.KEY_0], ')']
+ \ ]
+
+ for [kcodes, kstr] in test_punctuation_keys
+ call SendKeys(kcodes)
+ let ch = getcharstr(0)
+ call assert_equal($"{kstr}", $"{ch}")
+ let mod_mask = getcharmod()
+ " the mod_mask is zero when no modifiers are used
+ " and when the virtual termcap maps shift the character
+ call assert_equal(0, mod_mask, $"key = {kstr}")
+ endfor
+
+ " flush out any garbage left in the buffer
+ while getchar(0)
+ endwhile
+
+ for [kcodes, kstr] in test_punctuation_keys
+ let modifiers = 0
+ let key = kcodes[0]
+
+ for key in kcodes
+ if index([VK.SHIFT, VK.LSHIFT, VK.RSHIFT], key) >= 0
+ let modifiers = modifiers + vim_MOD_MASK_SHIFT
+ endif
+ if index([VK.CONTROL, VK.LCONTROL, VK.RCONTROL], key) >= 0
+ let modifiers = modifiers + vim_MOD_MASK_CTRL
+ endif
+ if index([VK.ALT, VK.LALT, VK.RALT], key) >= 0
+ let modifiers = modifiers + vim_MOD_MASK_ALT
+ endif
+ endfor
+
+ call SendKey(key, modifiers)
+ let ch = getcharstr(0)
+ call assert_equal($"{kstr}", $"{ch}")
+ let mod_mask = getcharmod()
+ " workaround for the virtual termcap maps changing the character instead
+ " of sending Shift
+ if index([VK.SHIFT, VK.LSHIFT, VK.RSHIFT], kcodes[0]) >= 0
+ let modifiers = modifiers - vim_MOD_MASK_SHIFT
+ endif
+ call assert_equal(modifiers, mod_mask, $"key = {kstr}")
+ endfor
+
+ " flush out any garbage left in the buffer
+ while getchar(0)
+ endwhile
+
+ " Test keyboard codes for digits
+ " (0x30 - 0x39) : VK_0 - VK_9 are the same as ASCII '0' - '9'
+ for kc in range(48, 57)
+ call SendKeys([kc])
+ let ch = getcharstr(0)
+ call assert_equal(nr2char(kc), ch)
+ call SendKey(kc, 0)
+ let ch = getcharstr(0)
+ call assert_equal(nr2char(kc), ch)
+ endfor
+
+ " Test keyboard codes for Alt-0 to Alt-9
+ " Expect +128 from the digit char codes
+ for modkey in [VK.ALT, VK.LALT, VK.RALT]
+ for kc in range(48, 57)
+ call SendKeys([modkey, kc])
+ let ch = getchar(0)
+ call assert_equal(kc+128, ch)
+ call SendKey(kc, vim_MOD_MASK_ALT)
+ let ch = getchar(0)
+ call assert_equal(kc+128, ch)
+ endfor
+ endfor
+
+ " Test for lowercase 'a' to 'z', VK codes 65(0x41) - 90(0x5A)
+ " Note: VK_A-VK_Z virtual key codes coincide with uppercase ASCII codes A-Z.
+ " eg VK_A is 65, and the ASCII character code for uppercase 'A' is also 65.
+ " Caution: these are interpreted as lowercase when Shift is NOT pressed.
+ " eg, sending VK_A (65) 'A' Key code without shift modifier, will produce ASCII
+ " char 'a' (91) as the output. The ASCII codes for the lowercase letters are
+ " numbered 32 higher than their uppercase versions.
+ for kc in range(65, 90)
+ call SendKeys([kc])
+ let ch = getcharstr(0)
+ call assert_equal(nr2char(kc + 32), ch)
+ call SendKey(kc, 0)
+ let ch = getcharstr(0)
+ call assert_equal(nr2char(kc + 32), ch)
+ endfor
+
+ " Test for Uppercase 'A' - 'Z' keys
+ " ie. with VK_SHIFT, expect the keycode = character code.
+ for kc in range(65, 90)
+ call SendKeys([VK.SHIFT, kc])
+ let ch = getcharstr(0)
+ call assert_equal(nr2char(kc), ch)
+ call SendKey(kc, vim_MOD_MASK_SHIFT)
+ let ch = getcharstr(0)
+ call assert_equal(nr2char(kc), ch)
+ endfor
+
+ " Test for <Ctrl-A> to <Ctrl-Z> keys
+ " Same as for lowercase, except with Ctrl Key
+ " Expect the unicode characters 0x01 to 0x1A
+ for modkey in [VK.CONTROL, VK.LCONTROL, VK.RCONTROL]
+ for kc in range(65, 90)
+ call SendKeys([modkey, kc])
+ let ch = getcharstr(0)
+ call assert_equal(nr2char(kc - 64), ch)
+ call SendKey(kc, vim_MOD_MASK_CTRL)
+ let ch = getcharstr(0)
+ call assert_equal(nr2char(kc - 64), ch)
+ endfor
+ endfor
+
+ if !has("gui_running")
+ " Test for <Alt-A> to <Alt-Z> keys
+ " Expect the unicode characters 0xE1 to 0xFA
+ " ie. 160 higher than the lowercase equivalent
+ for kc in range(65, 90)
+ call SendKeys([VK.LMENU, kc])
+ let ch = getchar(0)
+ call assert_equal(kc+160, ch)
+ call SendKey(kc, vim_MOD_MASK_ALT)
+ let ch = getchar(0)
+ call assert_equal(kc+160, ch)
+ endfor
+ endif
+
+ if !has("gui_running")
+ " Test for Function Keys 'F1' to 'F12'
+ for n in range(1, 12)
+ let kstr = $"F{n}"
+ let keycode = eval('"\<' .. kstr .. '>"')
+ call SendKeys([111+n])
+ let ch = getcharstr(0)
+ call assert_equal(keycode, $"{ch}", $"key = <{kstr}>")
+ endfor
+ endif
+
+ bw!
+ endfunc
+
+ " Test MS-Windows console mouse events
+ func Test_mswin_mouse_event()
+ CheckMSWindows
+ new
+
+ set mousemodel=extend
+ call test_override('no_query_mouse', 1)
+ call WaitForResponses()
+
+ let msg = ''
+
+ call setline(1, ['one two three', 'four five six'])
+
+ " Test mouse movement
+ " by default, no mouse move events are generated
+ " this setting enables it to generate move events
+ set mousemev
+
+ if !has('gui_running')
+ " console version needs a button pressed,
+ " otherwise it ignores mouse movements.
+ call MouseLeftClick(2, 3)
+ endif
+ call MSWinMouseEvent(0x700, 8, 13, 0, 0, 0)
+ if has('gui_running')
+ call feedkeys("\<Esc>", 'Lx!')
+ endif
+ let pos = getmousepos()
+ call assert_equal(8, pos.screenrow)
+ call assert_equal(13, pos.screencol)
+
+ if !has('gui_running')
+ call MouseLeftClick(2, 3)
+ call MSWinMouseEvent(0x700, 6, 4, 1, 0, 0)
+ let pos = getmousepos()
+ call assert_equal(6, pos.screenrow)
+ call assert_equal(4, pos.screencol)
+ endif
+
+ " test cells vs pixels
+ if has('gui_running')
+ let args = { }
+ let args.row = 9
+ let args.col = 7
+ let args.move = 1
+ let args.cell = 1
+ call test_mswin_event("mouse", args)
+ call feedkeys("\<Esc>", 'Lx!')
+ let pos = getmousepos()
+ call assert_equal(9, pos.screenrow)
+ call assert_equal(7, pos.screencol)
+
+ let args.cell = 0
+ call test_mswin_event("mouse", args)
+ call feedkeys("\<Esc>", 'Lx!')
+ let pos = getmousepos()
+ call assert_equal(1, pos.screenrow)
+ call assert_equal(1, pos.screencol)
+
+ unlet args
+ endif
+
+ " finish testing mouse movement
+ set mousemev&
+
+ " place the cursor using left click and release in normal mode
+ call MouseLeftClick(2, 4)
+ call MouseLeftRelease(2, 4)
+ if has('gui_running')
+ call feedkeys("\<Esc>", 'Lx!')
+ endif
+ call assert_equal([0, 2, 4, 0], getpos('.'))
+
+ " select and yank a word
+ let @" = ''
+ call MouseLeftClick(1, 9)
+ let args = #{button: 0, row: 1, col: 9, multiclick: 1, modifiers: 0}
+ call test_mswin_event('mouse', args)
+ call MouseLeftRelease(1, 9)
+ call feedkeys("y", 'Lx!')
+ call assert_equal('three', @")
+
+ " create visual selection using right click
+ let @" = ''
+
+ call MouseLeftClick(2 ,6)
+ call MouseLeftRelease(2, 6)
+ call MouseRightClick(2, 13)
+ call MouseRightRelease(2, 13)
+ call feedkeys("y", 'Lx!')
+ call assert_equal('five six', @")
+
+ " paste using middle mouse button
+ let @* = 'abc '
+ call feedkeys('""', 'Lx!')
+ call MouseMiddleClick(1, 9)
+ call MouseMiddleRelease(1, 9)
+ if has('gui_running')
+ call feedkeys("\<Esc>", 'Lx!')
+ endif
+ call assert_equal(['one two abc three', 'four five six'], getline(1, '$'))
+
+ " test mouse scrolling (aka touchpad scrolling.)
+ %d _
+ set scrolloff=0
+ call setline(1, range(1, 100))
+
+ " Scroll Down
+ call MouseWheelDown(2, 1)
+ call MouseWheelDown(2, 1)
+ call MouseWheelDown(2, 1)
+ call feedkeys("H", 'Lx!')
+ call assert_equal(10, line('.'))
+
+ " Scroll Up
+ call MouseWheelUp(2, 1)
+ call MouseWheelUp(2, 1)
+ call feedkeys("H", 'Lx!')
+ call assert_equal(4, line('.'))
+
+ " Shift Scroll Down
+ call MouseShiftWheelDown(2, 1)
+ call feedkeys("H", 'Lx!')
+ " should scroll from where it is (4) + visible buffer height - cmdheight
+ let shift_scroll_height = line('w$') - line('w0') - &cmdheight
+ call assert_equal(4 + shift_scroll_height, line('.'))
+
+ " Shift Scroll Up
+ call MouseShiftWheelUp(2, 1)
+ call feedkeys("H", 'Lx!')
+ call assert_equal(4, line('.'))
+
+ if !has('gui_running')
+ " Shift Scroll Down (using MOD)
+ call MSWinMouseEvent(0x100, 2, 1, 0, 0, 0x04)
+ call feedkeys("H", 'Lx!')
+ " should scroll from where it is (4) + visible buffer height - cmdheight
+ let shift_scroll_height = line('w$') - line('w0') - &cmdheight
+ call assert_equal(4 + shift_scroll_height, line('.'))
+
+ " Shift Scroll Up (using MOD)
+ call MSWinMouseEvent(0x200, 2, 1, 0, 0, 0x04)
+ call feedkeys("H", 'Lx!')
+ call assert_equal(4, line('.'))
+ endif
+
+ set scrolloff&
+
+ %d _
+ set nowrap
+ " make the buffer 500 wide.
+ call setline(1, range(10)->join('')->repeat(50))
+ " Scroll Right
+ call MouseWheelRight(1, 5)
+ call MouseWheelRight(1, 10)
+ call MouseWheelRight(1, 15)
+ call feedkeys('g0', 'Lx!')
+ call assert_equal(19, col('.'))
+
+ " Scroll Left
+ call MouseWheelLeft(1, 15)
+ call MouseWheelLeft(1, 10)
+ call feedkeys('g0', 'Lx!')
+ call assert_equal(7, col('.'))
+
+ " Shift Scroll Right
+ call MouseShiftWheelRight(1, 10)
+ call feedkeys('g0', 'Lx!')
+ " should scroll from where it is (7) + window width
+ call assert_equal(7 + winwidth(0), col('.'))
+
+ " Shift Scroll Left
+ call MouseShiftWheelLeft(1, 50)
+ call feedkeys('g0', 'Lx!')
+ call assert_equal(7, col('.'))
+ set wrap&
+
+ %d _
+ call setline(1, repeat([repeat('a', 60)], 10))
+
+ " record various mouse events
+ let mouseEventNames = [
+ \ 'LeftMouse', 'LeftRelease', '2-LeftMouse', '3-LeftMouse',
+ \ 'S-LeftMouse', 'A-LeftMouse', 'C-LeftMouse', 'MiddleMouse',
+ \ 'MiddleRelease', '2-MiddleMouse', '3-MiddleMouse',
+ \ 'S-MiddleMouse', 'A-MiddleMouse', 'C-MiddleMouse',
+ \ 'RightMouse', 'RightRelease', '2-RightMouse',
+ \ '3-RightMouse', 'S-RightMouse', 'A-RightMouse', 'C-RightMouse',
+ \ ]
+ let mouseEventCodes = map(copy(mouseEventNames), "'<' .. v:val .. '>'")
+ let g:events = []
+ for e in mouseEventCodes
+ exe 'nnoremap ' .. e .. ' <Cmd>call add(g:events, "' ..
+ \ substitute(e, '[<>]', '', 'g') .. '")<CR>'
+ endfor
+
+ " Test various mouse buttons
+ "(0 - Left, 1 - Middle, 2 - Right,
+ " 0x300 - MOUSE_X1/FROM_LEFT_3RD_BUTTON,
+ " 0x400 - MOUSE_X2/FROM_LEFT_4TH_BUTTON)
+ for button in [0, 1, 2, 0x300, 0x400]
+ " Single click
+ let args = #{button: button, row: 2, col: 5, multiclick: 0, modifiers: 0}
+ call test_mswin_event('mouse', args)
+ let args.button = 3
+ call test_mswin_event('mouse', args)
+
+ " Double Click
+ let args.button = button
+ call test_mswin_event('mouse', args)
+ let args.multiclick = 1
+ call test_mswin_event('mouse', args)
+ let args.button = 3
+ let args.multiclick = 0
+ call test_mswin_event('mouse', args)
+
+ " Triple Click
+ let args.button = button
+ call test_mswin_event('mouse', args)
+ let args.multiclick = 1
+ call test_mswin_event('mouse', args)
+ call test_mswin_event('mouse', args)
+ let args.button = 3
+ let args.multiclick = 0
+ call test_mswin_event('mouse', args)
+
+ " Shift click
+ let args = #{button: button, row: 3, col: 7, multiclick: 0, modifiers: 4}
+ call test_mswin_event('mouse', args)
+ let args.button = 3
+ call test_mswin_event('mouse', args)
+
+ " Alt click
+ let args.button = button
+ let args.modifiers = 8
+ call test_mswin_event('mouse', args)
+ let args.button = 3
+ call test_mswin_event('mouse', args)
+
+ " Ctrl click
+ let args.button = button
+ let args.modifiers = 16
+ call test_mswin_event('mouse', args)
+ let args.button = 3
+ call test_mswin_event('mouse', args)
+
+ call feedkeys("\<Esc>", 'Lx!')
+ endfor
+
+ if has('gui_running')
+ call assert_equal(['LeftMouse', 'LeftRelease', 'LeftMouse',
+ \ '2-LeftMouse', 'LeftMouse', '2-LeftMouse', '3-LeftMouse',
+ \ 'S-LeftMouse', 'A-LeftMouse', 'C-LeftMouse', 'MiddleMouse',
+ \ 'MiddleRelease', 'MiddleMouse', '2-MiddleMouse', 'MiddleMouse',
+ \ '2-MiddleMouse', '3-MiddleMouse', 'S-MiddleMouse', 'A-MiddleMouse',
+ \ 'C-MiddleMouse', 'RightMouse', 'RightRelease', 'RightMouse',
+ \ '2-RightMouse', 'RightMouse', '2-RightMouse', '3-RightMouse',
+ \ 'S-RightMouse', 'A-RightMouse', 'C-RightMouse'],
+ \ g:events)
+ else
+ call assert_equal(['MiddleRelease', 'LeftMouse', '2-LeftMouse',
+ \ '3-LeftMouse', 'S-LeftMouse', 'MiddleMouse', '2-MiddleMouse',
+ \ '3-MiddleMouse', 'MiddleMouse', 'S-MiddleMouse', 'RightMouse',
+ \ '2-RightMouse', '3-RightMouse'],
+ \ g:events)
+ endif
+
+ for e in mouseEventCodes
+ exe 'nunmap ' .. e
+ endfor
+
+ bw!
+ call test_override('no_query_mouse', 0)
+ set mousemodel&
+ endfunc
+
+
+ " Test MS-Windows test_mswin_event error handling
+ func Test_mswin_event_error_handling()
+
+ let args = #{button: 0xfff, row: 2, col: 4, move: 0, multiclick: 0, modifiers: 0}
+ if !has('gui_running')
+ call assert_fails("call test_mswin_event('mouse', args)",'E475:')
+ endif
+ let args = #{button: 0, row: 2, col: 4, move: 0, multiclick: 0, modifiers: 0}
+ call assert_fails("call test_mswin_event('a1b2c3', args)", 'E475:')
+ call assert_fails("call test_mswin_event(test_null_string(), {})", 'E475:')
+
+ call assert_fails("call test_mswin_event([], args)", 'E1174:')
+ call assert_fails("call test_mswin_event('abc', [])", 'E1206:')
+
+ call assert_false(test_mswin_event('mouse', test_null_dict()))
+ let args = #{row: 2, col: 4, multiclick: 0, modifiers: 0}
+ call assert_false(test_mswin_event('mouse', args))
+ let args = #{button: 0, col: 4, multiclick: 0, modifiers: 0}
+ call assert_false(test_mswin_event('mouse', args))
+ let args = #{button: 0, row: 2, multiclick: 0, modifiers: 0}
+ call assert_false(test_mswin_event('mouse', args))
+ let args = #{button: 0, row: 2, col: 4, modifiers: 0}
+ call assert_false(test_mswin_event('mouse', args))
+ let args = #{button: 0, row: 2, col: 4, multiclick: 0}
+ call assert_false(test_mswin_event('mouse', args))
+
+ call assert_false(test_mswin_event('key', test_null_dict()))
+ call assert_fails("call test_mswin_event('key', [])", 'E1206:')
+ call assert_fails("call test_mswin_event('key', {'event': 'keydown', 'keycode': 0x0})", 'E1291:')
+ call assert_fails("call test_mswin_event('key', {'event': 'keydown', 'keycode': [15]})", 'E745:')
+ call assert_fails("call test_mswin_event('key', {'event': 'keys', 'keycode': 0x41})", 'E475:')
+ call assert_fails("call test_mswin_event('key', {'keycode': 0x41})", 'E417:')
+ call assert_fails("call test_mswin_event('key', {'event': 'keydown'})", 'E1291:')
+
+ call assert_fails("sandbox call test_mswin_event('key', {'event': 'keydown', 'keycode': 61 })", 'E48:')
+
+ " flush out any garbage left in the buffer.
+ while getchar(0)
+ endwhile
+ endfunc
+
+
+ " vim: shiftwidth=2 sts=2 expandtab
*** ../vim-9.0.1083/src/testdir/test_termcodes.vim 2022-12-05 13:50:49.722052362 +0000
--- src/testdir/test_termcodes.vim 2022-12-20 19:22:31.977188037 +0000
***************
*** 437,461 ****
call assert_equal(1, line('w0'), msg)
call assert_equal([0, 7, 1, 0], getpos('.'), msg)

! if has('gui')
! " Horizontal wheel scrolling currently only works when vim is
! " compiled with gui enabled.
! call MouseWheelRight(1, 1)
! call assert_equal(7, 1 + virtcol(".") - wincol(), msg)
! call assert_equal([0, 7, 7, 0], getpos('.'), msg)
!
! call MouseWheelRight(1, 1)
! call assert_equal(13, 1 + virtcol(".") - wincol(), msg)
! call assert_equal([0, 7, 13, 0], getpos('.'), msg)
!
! call MouseWheelLeft(1, 1)
! call assert_equal(7, 1 + virtcol(".") - wincol(), msg)
! call assert_equal([0, 7, 13, 0], getpos('.'), msg)
!
! call MouseWheelLeft(1, 1)
! call assert_equal(1, 1 + virtcol(".") - wincol(), msg)
! call assert_equal([0, 7, 13, 0], getpos('.'), msg)
! endif
endfor

let &mouse = save_mouse
--- 437,458 ----
call assert_equal(1, line('w0'), msg)
call assert_equal([0, 7, 1, 0], getpos('.'), msg)

! call MouseWheelRight(1, 1)
! call assert_equal(7, 1 + virtcol(".") - wincol(), msg)
! call assert_equal([0, 7, 7, 0], getpos('.'), msg)
!
! call MouseWheelRight(1, 1)
! call assert_equal(13, 1 + virtcol(".") - wincol(), msg)
! call assert_equal([0, 7, 13, 0], getpos('.'), msg)
!
! call MouseWheelLeft(1, 1)
! call assert_equal(7, 1 + virtcol(".") - wincol(), msg)
! call assert_equal([0, 7, 13, 0], getpos('.'), msg)
!
! call MouseWheelLeft(1, 1)
! call assert_equal(1, 1 + virtcol(".") - wincol(), msg)
! call assert_equal([0, 7, 13, 0], getpos('.'), msg)
!
endfor

let &mouse = save_mouse
*** ../vim-9.0.1083/src/version.c 2022-12-20 13:38:18.284419451 +0000
--- src/version.c 2022-12-20 19:23:31.093219669 +0000
***************
*** 697,698 ****
--- 697,700 ----
{ /* Add new patch number below this line */
+ /**/
+ 1084,
/**/

--
A poem: read aloud:

<> !*''# Waka waka bang splat tick tick hash,
^"`$$- Caret quote back-tick dollar dollar dash,
!*=@$_ Bang splat equal at dollar under-score,
%*<> ~#4 Percent splat waka waka tilde number four,
&[]../ Ampersand bracket bracket dot dot slash,
|{,,SYSTEM HALTED Vertical-bar curly-bracket comma comma CRASH.

Fred Bremmer and Steve Kroese (Calvin College & Seminary of Grand Rapids, MI.)

/// Bram Moolenaar -- Br...@Moolenaar.net -- http://www.Moolenaar.net \\\
/// \\\
\\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ ///
\\\ help me help AIDS victims -- http://ICCF-Holland.org ///

John Marriott

unread,
Dec 20, 2022, 3:36:43 PM12/20/22
to vim...@googlegroups.com

On 21-Dec-2022 07:02, Bram Moolenaar wrote:
> Patch 9.0.1084
> Problem: Code handling low level MS-Windows events cannot be tested.
> Solution: Add test_mswin_event() and tests using it. (Christopher Plewright,
> closes #11622)
> Files: runtime/doc/builtin.txt, runtime/doc/testing.txt,
> runtime/doc/usr_41.txt, src/evalfunc.c, src/gui_w32.c,
> src/os_win32.c, src/proto/gui_w32.pro, src/proto/os_win32.pro,
> src/proto/testing.pro, src/term.c, src/testing.c,
> src/testdir/Make_all.mak, src/testdir/mouse.vim,
> src/testdir/test_gui.vim, src/testdir/test_mswin_event.vim,
> src/testdir/test_termcodes.vim
>
>
After this patch, msys64 (clang 15.0.5) reports these warnings:
<snip>
clang -c -I. -Iproto -DWIN32 -DWINVER=0x0603 -D_WIN32_WINNT=0x0603
-DHAVE_PATHDEF -DFEAT_NORMAL -DHAVE_STDINT_H -D__USE_MINGW_ANSI_STDIO
-pipe -Wall -O3 -fomit-frame-pointer -fpie -fPIE  os_win32.c -o
objx86-64/os_win32.o
os_win32.c:1760:9: warning: variable 'mods' is used uninitialized
whenever 'if' condition is true [-Wsometimes-uninitialized]
    if (move)
        ^~~~
os_win32.c:1826:9: note: uninitialized use occurs here
    if (mods != 0)
        ^~~~
os_win32.c:1760:5: note: remove the 'if' if its condition is always false
    if (move)
    ^~~~~~~~~
os_win32.c:1738:15: note: initialize the variable 'mods' to silence this
warning
    int_u       mods;
                    ^
                     = 0
os_win32.c:1883:1: warning: unused function 'peek_input_record_buffer'
[-Wunused-function]
peek_input_record_buffer(INPUT_RECORD* irEvents, int nMaxLength)
^
2 warnings generated.
</snip>

The attached patch tries to fix both warnings.

It seems that the function peek_input_record_buffer() is not used
anywhere. So the patch removes it.

Cheers
John
os_win32.c.9.0.1084.patch

Bram Moolenaar

unread,
Dec 20, 2022, 3:48:25 PM12/20/22
to vim...@googlegroups.com, John Marriott
Thanks, I'll include the patch.

--
DENNIS: Listen -- strange women lying in ponds distributing swords is no
basis for a system of government. Supreme executive power derives
from a mandate from the masses, not from some farcical aquatic
ceremony.
The Quest for the Holy Grail (Monty Python)

Christopher Plewright

unread,
Dec 20, 2022, 6:42:18 PM12/20/22
to vim...@googlegroups.com


>  The attached patch tries to fix both warnings.

Thanks.



> It seems that the function peek_input_record_buffer() is not used anywhere. So the patch removes it.

Yeah, it’s not used now.   

The existing static s_irCache is inner function scoped, so I couldn’t directly inject records to it for testing.   Initially, I thought it would make sense for the new input_record_buffer to replace s_irCache.   It seems redundant having both – and the code could be simplified.  However, the existing “peek” functionality has some weird behaviour.  There is a scenario where peek could actually pop a record off the buffer, and I wasn’t sure if that’s a bug or intentional, and so Bram suggested to leave it if I’m not sure that it’s a bug.   I thought I might be able to come back to this later, but for now peek_input_record_buffer() is not used, so doesn’t need to be there.

Cheers,

Chris Plewright

 

Reply all
Reply to author
Forward
0 new messages