summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--runtime/doc/builtin.txt2
-rw-r--r--runtime/doc/testing.txt72
-rw-r--r--runtime/doc/usr_41.txt1
-rw-r--r--src/evalfunc.c9
-rw-r--r--src/gui_w32.c163
-rw-r--r--src/os_win32.c378
-rw-r--r--src/proto/gui_w32.pro2
-rw-r--r--src/proto/os_win32.pro1
-rw-r--r--src/proto/testing.pro1
-rw-r--r--src/term.c2
-rw-r--r--src/testdir/Make_all.mak2
-rw-r--r--src/testdir/mouse.vim196
-rw-r--r--src/testdir/test_gui.vim6
-rw-r--r--src/testdir/test_mswin_event.vim651
-rw-r--r--src/testdir/test_termcodes.vim35
-rw-r--r--src/testing.c45
-rw-r--r--src/version.c2
17 files changed, 1492 insertions, 76 deletions
diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt
index 7ff969d7cb..05e8d701f8 100644
--- a/runtime/doc/builtin.txt
+++ b/runtime/doc/builtin.txt
@@ -666,6 +666,8 @@ test_garbagecollect_soon() none free memory soon for testing
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
diff --git a/runtime/doc/testing.txt b/runtime/doc/testing.txt
index 558553f5ac..b03726572b 100644
--- a/runtime/doc/testing.txt
+++ b/runtime/doc/testing.txt
@@ -94,7 +94,7 @@ test_gui_event({event}, {args})
"findrepl" search and replace text.
"mouse" mouse button click event.
"scrollbar" move or drag the scrollbar.
- "sendevent" send a low-level GUI event.
+ "key" send a low-level keyboard event.
"tabline" select a tab page by mouse click.
"tabmenu" select a tabline menu entry.
@@ -178,8 +178,8 @@ test_gui_event({event}, {args})
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).
+ "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:
@@ -223,6 +223,72 @@ test_ignore_error({expr}) *test_ignore_error()*
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.
diff --git a/runtime/doc/usr_41.txt b/runtime/doc/usr_41.txt
index aa51fe3e96..c67bd99ef2 100644
--- a/runtime/doc/usr_41.txt
+++ b/runtime/doc/usr_41.txt
@@ -1186,6 +1186,7 @@ Testing: *test-functions*
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
diff --git a/src/evalfunc.c b/src/evalfunc.c
index 3db4bb7b24..b96fc472c3 100644
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -2694,6 +2694,8 @@ static funcentry_T global_functions[] =
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,7 +4389,12 @@ f_feedkeys(typval_T *argvars, typval_T *rettv UNUSED)
if (*keys != NUL || execute)
{
- if (lowlevel)
+ 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);
diff --git a/src/gui_w32.c b/src/gui_w32.c
index 6733ac0624..b4a33d9868 100644
--- a/src/gui_w32.c
+++ b/src/gui_w32.c
@@ -8643,41 +8643,176 @@ netbeans_draw_multisign_indicator(int row)
#endif
#if defined(FEAT_EVAL) || defined(PROTO)
- int
-test_gui_w32_sendevent(dict_T *args)
+
+// 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)
{
- char_u *event;
- INPUT inputs[1];
+ if (!dict_has_key(args, "row") || !dict_has_key(args, "col"))
+ return FALSE;
- event = dict_get_string(args, "event", TRUE);
- if (event == NULL)
+ // 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;
- ZeroMemory(inputs, sizeof(inputs));
+ int row = (int)dict_get_number(args, "row");
+ int col = (int)dict_get_number(args, "col");
- if (STRICMP(event, "keydown") == 0 || STRICMP(event, "keyup") == 0)
+ 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
{
- WORD vkCode;
+ 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;
+}
- vkCode = dict_get_number_def(args, "keycode", 0);
+ 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));
- }
- else
- semsg(_(e_invalid_argument_str), event);
+ vim_free(event);
- 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
diff --git a/src/os_win32.c b/src/os_win32.c
index 334d9735ee..f32d486a52 100644
--- a/src/os_win32.c
+++ b/src/os_win32.c
@@ -177,6 +177,25 @@ static void gotoxy(unsigned x, unsigned y);
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,7 +243,7 @@ static int default_console_color_fg = 0xc0c0c0; // white
static void set_console_color_rgb(void);
static void reset_console_color_rgb(void);
static void restore_console_color_rgb(void);
-#endif
+#endif // !FEAT_GUI_MSWIN || VIMDLL
// This flag is newly created from Windows 10
#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
@@ -319,6 +338,13 @@ read_console_input(
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,7 +457,7 @@ wait_for_single_object(
return WaitForSingleObject(hHandle, dwMilliseconds);
}
# endif
-#endif
+#endif // !FEAT_GUI_MSWIN || VIMDLL
static void
get_exe_name(void)
@@ -1014,7 +1040,7 @@ win32_kbd_patch_key(
return 1;
}
- if (pker->uChar.UnicodeChar != 0)
+ if (pker->uChar.UnicodeChar > 0 && pker->uChar.UnicodeChar < 0xfffd)
return 1;
CLEAR_FIELD(abKeystate);
@@ -1080,7 +1106,8 @@ decode_key_event(
// special cases
if ((nModifs & CTRL) != 0 && (nModifs & ~CTRL) == 0
- && pker->uChar.UnicodeChar == NUL)
+ && (pker->uChar.UnicodeChar == NUL
+ || pker->uChar.UnicodeChar == 0xfffd))
{
// Ctrl-6 is Ctrl-^
if (pker->wVirtualKeyCode == '6')
@@ -1168,7 +1195,113 @@ decode_key_event(
return (*pch != NUL);
}
-#endif // FEAT_GUI_MSWIN
+# 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,7 +1312,7 @@ decode_key_event(
mch_setmouse(int on UNUSED)
{
}
-#else
+#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,21 +1367,21 @@ mch_bevalterm_changed(void)
/*
* Win32 console mouse scroll event handler.
- * Loosely based on the _OnMouseWheel() function in gui_w32.c
+ * 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 axis - vertical or horizontal flag - from dwEventFlags, and
+ * 1. The orientation - vertical or horizontal flag - from dwEventFlags
* 2. The sign - positive or negative (aka delta flag) - from dwButtonState
*
- * When scroll axis is HORIZONTAL
+ * 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 axis is VERTICAL
+ * 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,8 +1727,231 @@ decode_mouse_event(
return TRUE;
}
-#endif // FEAT_GUI_MSWIN
+# 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
/*
diff --git a/src/proto/gui_w32.pro b/src/proto/gui_w32.pro
index cab3343249..c5c6585dbc 100644
--- a/src/proto/gui_w32.pro
+++ b/src/proto/gui_w32.pro
@@ -96,5 +96,5 @@ void gui_mch_post_balloon(BalloonEval *beval, char_u *mesg);
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);
+int test_gui_w32_sendevent(char_u *event, dict_T *args);
/* vim: set ft=c : */
diff --git a/src/proto/os_win32.pro b/src/proto/os_win32.pro
index a6bbd33526..9f8b969583 100644
--- a/src/proto/os_win32.pro
+++ b/src/proto/os_win32.pro
@@ -9,6 +9,7 @@ void dyn_libintl_end(void);
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);
diff --git a/src/proto/testing.pro b/src/proto/testing.pro
index 2192e91ec7..dea4f75352 100644
--- a/src/proto/testing.pro
+++ b/src/proto/testing.pro
@@ -33,6 +33,7 @@ void f_test_null_string(typval_T *argvars, typval_T *rettv);
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 : */
diff --git a/src/term.c b/src/term.c
index cd6b3b9cf6..a91813d7aa 100644
--- a/src/term.c
+++ b/src/term.c
@@ -827,7 +827,7 @@ static tcap_entry_T builtin_pcansi[] = {
};
/*
- * These codes are valid for the Win32 Console . The entries that start with
+ * 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.
*/
diff --git a/src/testdir/Make_all.mak b/src/testdir/Make_all.mak
index 886bb08ad8..fdca9f4193 100644
--- a/src/testdir/Make_all.mak
+++ b/src/testdir/Make_all.mak
@@ -210,6 +210,7 @@ NEW_TESTS = \
test_modeless \
test_modeline \
test_move \
+ test_mswin_event \
test_mzscheme \
test_nested_function \
test_netbeans \
@@ -454,6 +455,7 @@ NEW_TESTS_RES = \
test_mksession.res \
test_modeless.res \
test_modeline.res \
+ test_mswin_event.res \
test_mzscheme.res \
test_nested_function.res \
test_netbeans.res \
diff --git a/src/testdir/mouse.vim b/src/testdir/mouse.vim
index d59ad0eab6..e2979b771c 100644
--- a/src/testdir/mouse.vim
+++ b/src/testdir/mouse.vim
@@ -20,6 +20,27 @@ else
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,6 +68,31 @@ func NettermEscapeCode(row, col)
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,7 +104,11 @@ func MouseLeftClickCode(row, col)
endfunc
func MouseLeftClick(row, col)
- call feedkeys(MouseLeftClickCode(a:row, a:col), 'Lx!')
+ 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,7 +120,11 @@ func MouseMiddleClickCode(row, col)
endfunc
func MouseMiddleClick(row, col)
- call feedkeys(MouseMiddleClickCode(a:row, a:col), 'Lx!')
+ 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,7 +136,11 @@ func MouseRightClickCode(row, col)
endfunc
func MouseRightClick(row, col)
- call feedkeys(MouseRightClickCode(a:row, a:col), 'Lx!')
+ if has('win32')
+ call MSWinMou