]> granicus.if.org Git - vim/commitdiff
patch 9.0.1084: code handling low level MS-Windows events cannot be tested v9.0.1084
authorChristopher Plewright <chris@createng.com>
Tue, 20 Dec 2022 20:01:58 +0000 (20:01 +0000)
committerBram Moolenaar <Bram@vim.org>
Tue, 20 Dec 2022 20:01:58 +0000 (20:01 +0000)
Problem:    Code handling low level MS-Windows events cannot be tested.
Solution:   Add test_mswin_event() and tests using it. (Christopher Plewright,
            closes #11622)

17 files changed:
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/testdir/Make_all.mak
src/testdir/mouse.vim
src/testdir/test_gui.vim
src/testdir/test_mswin_event.vim [new file with mode: 0644]
src/testdir/test_termcodes.vim
src/testing.c
src/version.c

index 7ff969d7cb16fca11062315b34ccc7eaee82de39..05e8d701f8262b8d3f619c3b3c5a872ba48c251e 100644 (file)
@@ -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
index 558553f5acc9e06dfab0dcf1b8e8c9bbb99a078d..b03726572b4f437862e7c43fac8fb70c0b276336 100644 (file)
@@ -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.
index aa51fe3e96927e2016a4bd48cdd4a7a9fcdcc821..c67bd99ef2aae63bba481184ad06b2a5b8492584 100644 (file)
@@ -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
index 3db4bb7b24b8c0fb7fb1a6104f7bc187caee89f1..b96fc472c3321c3dfd9eb419fb2cb4016ae4c92b 100644 (file)
@@ -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);
index 6733ac062463ed6ceb5b37f7170a3c1b245e9355..b4a33d98689ddc5658ab2c958b31ac4018a0558c 100644 (file)
@@ -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
index 334d9735eec9ecd922c46ca3d2dc2a1ce5af93ce..f32d486a52d3fe11765f24fede619c13b8800f6f 100644 (file)
@@ -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
 /*
index cab3343249682931e90c664904690100dd78630b..c5c6585dbc8b94224b3fd9e1acbfedbd421dd898 100644 (file)
@@ -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 : */
index a6bbd33526220b0cb879bd716bafd69fe3e56383..9f8b9695838dbb6f79adf48cb00a962b88cc2d8f 100644 (file)
@@ -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);
index 2192e91ec7154f563dc42c423ea4111a61d00b0a..dea4f753525964f517fcc2ace9c159dad42d969d 100644 (file)
@@ -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 : */
index cd6b3b9cf62ba3d9e5be0ebb20d0c0242084231d..a91813d7aa4d20c47d6f41f6bf939e5c1ef795d2 100644 (file)
@@ -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.
  */
index 886bb08ad864b3ade78aa21c1c55122026c66df2..fdca9f41939ce3c3c10048a59cb01a36a8e152c5 100644 (file)
@@ -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 \
index d59ad0eab6bcc795a6ad5ff7ac704b0decd79968..e2979b771c5f4429e7be743664873bf2310195f0 100644 (file)
@@ -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 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,7 +149,12 @@ func MouseCtrlLeftClickCode(row, col)
 endfunc
 
 func MouseCtrlLeftClick(row, col)
-  call feedkeys(MouseCtrlLeftClickCode(a:row, a:col), 'Lx!')
+  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,7 +163,12 @@ func MouseCtrlRightClickCode(row, col)
 endfunc
 
 func MouseCtrlRightClick(row, col)
-  call feedkeys(MouseCtrlRightClickCode(a:row, a:col), 'Lx!')
+  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,7 +177,12 @@ func MouseAltLeftClickCode(row, col)
 endfunc
 
 func MouseAltLeftClick(row, col)
-  call feedkeys(MouseAltLeftClickCode(a:row, a:col), 'Lx!')
+  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,7 +191,12 @@ func MouseAltRightClickCode(row, col)
 endfunc
 
 func MouseAltRightClick(row, col)
-  call feedkeys(MouseAltRightClickCode(a:row, a:col), 'Lx!')
+  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,7 +210,11 @@ func MouseLeftReleaseCode(row, col)
 endfunc
 
 func MouseLeftRelease(row, col)
-  call feedkeys(MouseLeftReleaseCode(a:row, a:col), 'Lx!')
+  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,7 +226,11 @@ func MouseMiddleReleaseCode(row, col)
 endfunc
 
 func MouseMiddleRelease(row, col)
-  call feedkeys(MouseMiddleReleaseCode(a:row, a:col), 'Lx!')
+  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,7 +242,11 @@ func MouseRightReleaseCode(row, col)
 endfunc
 
 func MouseRightRelease(row, col)
-  call feedkeys(MouseRightReleaseCode(a:row, a:col), 'Lx!')
+  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,7 +258,11 @@ func MouseLeftDragCode(row, col)
 endfunc
 
 func MouseLeftDrag(row, col)
-  call feedkeys(MouseLeftDragCode(a:row, a:col), 'Lx!')
+  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,7 +270,11 @@ func MouseWheelUpCode(row, col)
 endfunc
 
 func MouseWheelUp(row, col)
-  call feedkeys(MouseWheelUpCode(a:row, a:col), 'Lx!')
+  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,7 +282,11 @@ func MouseWheelDownCode(row, col)
 endfunc
 
 func MouseWheelDown(row, col)
-  call feedkeys(MouseWheelDownCode(a:row, a:col), 'Lx!')
+  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,7 +294,11 @@ func MouseWheelLeftCode(row, col)
 endfunc
 
 func MouseWheelLeft(row, col)
-  call feedkeys(MouseWheelLeftCode(a:row, a:col), 'Lx!')
+  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,7 +306,67 @@ func MouseWheelRightCode(row, col)
 endfunc
 
 func MouseWheelRight(row, col)
-  call feedkeys(MouseWheelRightCode(a:row, a:col), 'Lx!')
+  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
index 3c7cfb9a0ba1a8294d8403965aa9984b016def31..0c1e0a5771438a206d8578c6285c695f6cb1a539 100644 (file)
@@ -1281,7 +1281,7 @@ func Test_gui_mouse_move_event()
     let g:eventlist = g:eventlist[1 : ]
   endif
 
-  call assert_equal([#{row: 4, col: 31}, #{row: 11, col: 31}], g:eventlist)
+  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,10 +1638,10 @@ endfunc
 " Test for sending low level key presses
 func SendKeys(keylist)
   for k in a:keylist
-    call test_gui_event("sendevent", #{event: "keydown", keycode: k})
+    call test_gui_event("key", #{event: "keydown", keycode: k})
   endfor
   for k in reverse(a:keylist)
-    call test_gui_event("sendevent", #{event: "keyup", keycode: k})
+    call test_gui_event("key", #{event: "keyup", keycode: k})
   endfor
 endfunc
 
diff --git a/src/testdir/test_mswin_event.vim b/src/testdir/test_mswin_event.vim
new file mode 100644 (file)
index 0000000..4e392a2
--- /dev/null
@@ -0,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
index dd5280fba06996aa26e151ac373d1ffcc596833c..d3ab99cdb9593a567bbf3d553ad0e1e0cd009c40 100644 (file)
@@ -437,25 +437,22 @@ func Test_1xterm_mouse_wheel()
     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
+    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
index 74b6b0e81b29ca0917ade2ee5636988c727a3a2c..039ee0c9b0b6de53d52f6b68a360e371a295b17f 100644 (file)
@@ -1388,13 +1388,18 @@ test_gui_mouse_event(dict_T *args)
 
     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))
        {
-           // 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;
+           // 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(col, row);
+       gui_mouse_moved(pX, pY);
     }
     else
     {
@@ -1488,6 +1493,30 @@ test_gui_tabmenu_event(dict_T *args UNUSED)
 }
 # 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)
 {
@@ -1514,6 +1543,10 @@ f_test_gui_event(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
 #  if defined(FIND_REPLACE_DIALOG)
     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);
@@ -1523,10 +1556,6 @@ f_test_gui_event(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
        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);
index 877fe752da1725f39a22e85946cd74f856b8c222..c5f3848bbd37f07f135f76594a488812a620e19a 100644 (file)
@@ -695,6 +695,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1084,
 /**/
     1083,
 /**/