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


*** ../vim-9.0.1083/runtime/doc/builtin.txt     2022-12-05 13:50:49.718052362 
+0000
--- runtime/doc/builtin.txt     2022-12-20 19:22:31.969188032 +0000
***************
*** 666,671 ****
--- 666,673 ----
  test_getvalue({string})               any     get value of an internal 
variable
  test_gui_event({event}, {args})       bool    generate a GUI event for testing
  test_ignore_error({expr})     none    ignore a specific error
+ test_mswin_event({event}, {args})
+                               bool    generate MS-Windows event for testing
  test_null_blob()              Blob    null value for testing
  test_null_channel()           Channel null value for testing
  test_null_dict()              Dict    null value for testing
*** ../vim-9.0.1083/runtime/doc/testing.txt     2022-07-23 05:04:07.580839529 
+0100
--- runtime/doc/testing.txt     2022-12-20 19:27:27.353314009 +0000
***************
*** 94,100 ****
                    "findrepl"  search and replace text.
                    "mouse"     mouse button click event.
                    "scrollbar" move or drag the scrollbar.
!                   "sendevent" send a low-level GUI event.
                    "tabline"   select a tab page by mouse click.
                    "tabmenu"   select a tabline menu entry.
  
--- 94,100 ----
                    "findrepl"  search and replace text.
                    "mouse"     mouse button click event.
                    "scrollbar" move or drag the scrollbar.
!                   "key"       send a low-level keyboard event.
                    "tabline"   select a tab page by mouse click.
                    "tabmenu"   select a tabline menu entry.
  
***************
*** 135,143 ****
                  Inject either a mouse button click, or a mouse move, event.
                  The supported items in {args} are:
                    button:     mouse button.  The supported values are:
!                                   0   right mouse button
                                    1   middle mouse button
!                                   2   left mouse button
                                    3   mouse button release
                                    4   scroll wheel down
                                    5   scroll wheel up
--- 135,143 ----
                  Inject either a mouse button click, or a mouse move, event.
                  The supported items in {args} are:
                    button:     mouse button.  The supported values are:
!                                   0   left mouse button
                                    1   middle mouse button
!                                   2   right mouse button
                                    3   mouse button release
                                    4   scroll wheel down
                                    5   scroll wheel up
***************
*** 178,191 ****
                    dragging:   1 to drag the scrollbar and 0 to click in the
                                scrollbar.
  
!               "sendevent":
!                 Send a low-level GUI event (e.g. key-up or down).
                  Currently only supported on MS-Windows.
                  The supported items in {args} are:
                    event:      The supported string values are:
                                    keyup   generate a keyup event
                                    keydown generate a keydown event
                    keycode:    Keycode to use for a keyup or a keydown event.
  
                "tabline":
                  Inject a mouse click event on the tabline to select a
--- 178,192 ----
                    dragging:   1 to drag the scrollbar and 0 to click in the
                                scrollbar.
  
!               "key":
!                 Send a low-level keyboard event (e.g. key-up or down).
                  Currently only supported on MS-Windows.
                  The supported items in {args} are:
                    event:      The supported string values are:
                                    keyup   generate a keyup event
                                    keydown generate a keydown event
                    keycode:    Keycode to use for a keyup or a keydown event.
+                                                               *E1291*
  
                "tabline":
                  Inject a mouse click event on the tabline to select a
***************
*** 222,227 ****
--- 223,294 ----
                Can also be used as a |method|: >
                        GetErrorText()->test_ignore_error()
  
+                               
+ test_mswin_event({event}, {args})             *test_mswin_event()*
+               Generate a low-level MS-Windows {event} with arguments {args}
+               for testing Vim functionality.  It works for MS-Windows GUI 
+               and for the console.
+               
+               {event} is a String and the supported values are:
+                   "mouse"     mouse event.
+                   "key"       keyboard event.
+ 
+               "mouse":
+                 Inject either a mouse button click, or a mouse move, event.
+                 The supported items in {args} are:
+                   button:     mouse button.  The supported values are:
+                                   0   right mouse button
+                                   1   middle mouse button
+                                   2   left mouse button
+                                   3   mouse button release
+                                   4   scroll wheel down
+                                   5   scroll wheel up
+                                   6   scroll wheel left
+                                   7   scroll wheel right
+                   row:        mouse click row number.  The first row of the
+                               Vim window is 1 and the last row is 'lines'.
+                   col:        mouse click column number.  The maximum value
+                               of {col} is 'columns'.
+                               Note: row and col are always interpreted as
+                               screen cells for the console application.
+                               But, they may be interpreted as pixels
+                               for the GUI, depending on "cell".
+                   multiclick: set to 1 to inject a double-click mouse event.
+                   modifiers:  key modifiers.  The supported values are:
+                                   4   shift is pressed
+                                   8   alt is pressed
+                                  16   ctrl is pressed
+                   move:       Optional; if used and TRUE then a mouse move
+                               event can be generated.
+                               Only {args} row: and col: are used and
+                               required.
+                               Only results in an event when 'mousemoveevent'
+                               is set or a popup uses mouse move events.
+                   cell:       Optional for the GUI: when present and TRUE
+                               then "move" uses screen cells instead of pixel
+                               positions.  Not used by the console.
+ 
+               "key":
+                 Send a low-level keyboard event (e.g. keyup or keydown).
+                 The supported items in {args} are:
+                   event:      The supported string values are:
+                                   keyup   generate a keyup event
+                                   keydown generate a keydown event
+                   keycode:    Keycode to use for a keyup or a keydown event.
+                   modifiers:  Optional; key modifiers.
+                               The supported values are:
+                                   2   shift is pressed
+                                   4   ctrl is pressed
+                                   8   alt is pressed
+                               Note: These values are different from the
+                               mouse modifiers.
+                                                               *E1291*
+               Returns TRUE if the event is successfully added, FALSE if
+               there is a failure.
+ 
+               Can also be used as a |method|: >
+                       GetEvent()->test_mswin_event({args})
+ <
  
  test_null_blob()                                      *test_null_blob()*
                Return a |Blob| that is null. Only useful for testing.
*** ../vim-9.0.1083/runtime/doc/usr_41.txt      2022-12-05 13:50:49.718052362 
+0000
--- runtime/doc/usr_41.txt      2022-12-20 19:27:44.801319311 +0000
***************
*** 1185,1190 ****
--- 1186,1192 ----
        test_getvalue()         get value of an internal variable
        test_gui_event()        generate a GUI event for testing
        test_ignore_error()     ignore a specific error message
+       test_mswin_event()      generate an MS-Windows event
        test_null_blob()        return a null Blob
        test_null_channel()     return a null Channel
        test_null_dict()        return a null Dict
*** ../vim-9.0.1083/src/evalfunc.c      2022-12-08 15:32:11.079034172 +0000
--- src/evalfunc.c      2022-12-20 19:28:32.093332756 +0000
***************
*** 2694,2699 ****
--- 2694,2701 ----
                        ret_bool,           f_test_gui_event},
      {"test_ignore_error", 1, 1, FEARG_1,    arg1_string,
                        ret_void,           f_test_ignore_error},
+     {"test_mswin_event", 2, 2, FEARG_1,     arg2_string_dict,
+                       ret_number,         f_test_mswin_event},
      {"test_null_blob",        0, 0, 0,            NULL,
                        ret_blob,           f_test_null_blob},
      {"test_null_channel", 0, 0, 0,        NULL,
***************
*** 4387,4393 ****
  
      if (*keys != NUL || execute)
      {
!       if (lowlevel)
        {
  #ifdef USE_INPUT_BUF
            ch_log(NULL, "feedkeys() lowlevel: %s", keys);
--- 4389,4400 ----
  
      if (*keys != NUL || execute)
      {
!       if (lowlevel
! #ifdef FEAT_VTP
!               && (!is_term_win32()
!                   || (keys[0] == 3 && ctrl_c_interrupts && typed))
! #endif
!          )
        {
  #ifdef USE_INPUT_BUF
            ch_log(NULL, "feedkeys() lowlevel: %s", keys);
*** ../vim-9.0.1083/src/gui_w32.c       2022-11-16 12:02:24.861138121 +0000
--- src/gui_w32.c       2022-12-20 19:45:48.112743098 +0000
***************
*** 8643,8683 ****
  #endif
  
  #if defined(FEAT_EVAL) || defined(PROTO)
!     int
! test_gui_w32_sendevent(dict_T *args)
  {
!     char_u    *event;
!     INPUT     inputs[1];
  
!     event = dict_get_string(args, "event", TRUE);
!     if (event == NULL)
        return FALSE;
  
!     ZeroMemory(inputs, sizeof(inputs));
  
!     if (STRICMP(event, "keydown") == 0 || STRICMP(event, "keyup") == 0)
      {
!       WORD        vkCode;
  
!       vkCode = dict_get_number_def(args, "keycode", 0);
        if (vkCode <= 0 || vkCode >= 0xFF)
        {
            semsg(_(e_invalid_argument_nr), (long)vkCode);
            return FALSE;
        }
  
        inputs[0].type = INPUT_KEYBOARD;
        inputs[0].ki.wVk = vkCode;
        if (STRICMP(event, "keyup") == 0)
            inputs[0].ki.dwFlags = KEYEVENTF_KEYUP;
        (void)SetForegroundWindow(s_hwnd);
        SendInput(ARRAYSIZE(inputs), inputs, sizeof(INPUT));
!     }
!     else
!       semsg(_(e_invalid_argument_str), event);
  
!     vim_free(event);
  
      return TRUE;
  }
  #endif
--- 8643,8818 ----
  #endif
  
  #if defined(FEAT_EVAL) || defined(PROTO)
! 
! // TODO: at the moment, this is just a copy of test_gui_mouse_event.
! // But, we could instead generate actual Win32 mouse event messages,
! // ie. to make it consistent wih test_gui_w32_sendevent_keyboard.
!     static int
! test_gui_w32_sendevent_mouse(dict_T *args)
  {
!     if (!dict_has_key(args, "row") || !dict_has_key(args, "col"))
!       return FALSE;
  
!     // Note: "move" is optional, requires fewer arguments
!     int move = (int)dict_get_bool(args, "move", FALSE);
! 
!     if (!move && (!dict_has_key(args, "button")
!           || !dict_has_key(args, "multiclick")
!           || !dict_has_key(args, "modifiers")))
        return FALSE;
  
!     int row = (int)dict_get_number(args, "row");
!     int col = (int)dict_get_number(args, "col");
  
!     if (move)
      {
!       // the "move" argument expects row and col coordnates to be in pixels,
!       // unless "cell" is specified and is TRUE.
!       if (dict_get_bool(args, "cell", FALSE))
!       {
!           // calculate the middle of the character cell
!           // Note: Cell coordinates are 1-based from vimscript
!           int pY = (row - 1) * gui.char_height + gui.char_height / 2;
!           int pX = (col - 1) * gui.char_width + gui.char_width / 2;
!           gui_mouse_moved(pX, pY);
!       }
!       else
!           gui_mouse_moved(col, row);
!     }
!     else
!     {
!       int button = (int)dict_get_number(args, "button");
!       int repeated_click = (int)dict_get_number(args, "multiclick");
!       int_u mods = (int)dict_get_number(args, "modifiers");
! 
!       // Reset the scroll values to known values.
!       // XXX: Remove this when/if the scroll step is made configurable.
!       mouse_set_hor_scroll_step(6);
!       mouse_set_vert_scroll_step(3);
! 
!       gui_send_mouse_event(button, TEXT_X(col - 1), TEXT_Y(row - 1),
!                                                       repeated_click, mods);
!     }
!     return TRUE;
! }
! 
!     static int
! test_gui_w32_sendevent_keyboard(dict_T *args)
! {
!     INPUT inputs[1];
!     INPUT modkeys[3];
!     SecureZeroMemory(inputs, sizeof(INPUT));
!     SecureZeroMemory(modkeys, 3 * sizeof(INPUT));
! 
!     char_u *event = dict_get_string(args, "event", TRUE);
  
!     if (event && (STRICMP(event, "keydown") == 0
!                                     || STRICMP(event, "keyup") == 0))
!     {
!       WORD vkCode = dict_get_number_def(args, "keycode", 0);
        if (vkCode <= 0 || vkCode >= 0xFF)
        {
            semsg(_(e_invalid_argument_nr), (long)vkCode);
            return FALSE;
        }
  
+       BOOL isModKey = (vkCode == VK_SHIFT || vkCode == VK_CONTROL
+           || vkCode == VK_MENU || vkCode == VK_LSHIFT || vkCode == VK_RSHIFT
+           || vkCode == VK_LCONTROL || vkCode == VK_RCONTROL
+           || vkCode == VK_LMENU || vkCode == VK_RMENU );
+ 
+       BOOL unwrapMods = FALSE;
+       int mods = (int)dict_get_number(args, "modifiers");
+ 
+       // If there are modifiers in the args, and it is not a keyup event and
+       // vkCode is not a modifier key, then we generate virtual modifier key
+       // messages before sending the actual key message.
+       if(mods && STRICMP(event, "keydown") == 0 && !isModKey)
+       {
+           int n = 0;
+           if (mods & MOD_MASK_SHIFT)
+           {
+               modkeys[n].type = INPUT_KEYBOARD;
+               modkeys[n].ki.wVk = VK_LSHIFT;
+               n++;
+           }
+           if (mods & MOD_MASK_CTRL)
+           {
+               modkeys[n].type = INPUT_KEYBOARD;
+               modkeys[n].ki.wVk = VK_LCONTROL;
+               n++;
+           }
+           if (mods & MOD_MASK_ALT)
+           {
+               modkeys[n].type = INPUT_KEYBOARD;
+               modkeys[n].ki.wVk = VK_LMENU;
+               n++;
+           }
+           if (n)
+           {
+               (void)SetForegroundWindow(s_hwnd);
+               SendInput(n, modkeys, sizeof(INPUT));
+           }
+       }
+ 
        inputs[0].type = INPUT_KEYBOARD;
        inputs[0].ki.wVk = vkCode;
        if (STRICMP(event, "keyup") == 0)
+       {
            inputs[0].ki.dwFlags = KEYEVENTF_KEYUP;
+           if(!isModKey)
+               unwrapMods = TRUE;
+       }
+ 
        (void)SetForegroundWindow(s_hwnd);
        SendInput(ARRAYSIZE(inputs), inputs, sizeof(INPUT));
!       vim_free(event);
  
!       if (unwrapMods)
!       {
!           modkeys[0].type = INPUT_KEYBOARD;
!           modkeys[0].ki.wVk = VK_LSHIFT;
!           modkeys[0].ki.dwFlags = KEYEVENTF_KEYUP;
! 
!           modkeys[1].type = INPUT_KEYBOARD;
!           modkeys[1].ki.wVk = VK_LCONTROL;
!           modkeys[1].ki.dwFlags = KEYEVENTF_KEYUP;
! 
!           modkeys[2].type = INPUT_KEYBOARD;
!           modkeys[2].ki.wVk = VK_LMENU;
!           modkeys[2].ki.dwFlags = KEYEVENTF_KEYUP;
  
+           (void)SetForegroundWindow(s_hwnd);
+           SendInput(3, modkeys, sizeof(INPUT));
+       }
+     }
+     else
+     {
+       if (event == NULL)
+       {
+           semsg(_(e_missing_argument_str), "event");
+       }
+       else
+       {
+           semsg(_(e_invalid_value_for_argument_str_str), "event", event);
+           vim_free(event);
+       }
+       return FALSE;
+     }
      return TRUE;
  }
+ 
+     int
+ test_gui_w32_sendevent(char_u *event, dict_T *args)
+ {
+     if (STRICMP(event, "key") == 0)
+       return test_gui_w32_sendevent_keyboard(args);
+     else if (STRICMP(event, "mouse") == 0)
+       return test_gui_w32_sendevent_mouse(args);
+     else
+     {
+       semsg(_(e_invalid_value_for_argument_str_str), "event", event);
+       return FALSE;
+     }
+ }
  #endif
*** ../vim-9.0.1083/src/os_win32.c      2022-11-30 18:11:52.690904297 +0000
--- src/os_win32.c      2022-12-20 19:43:38.056916734 +0000
***************
*** 177,182 ****
--- 177,201 ----
  static void standout(void);
  static int s_cursor_visible = TRUE;
  static int did_create_conin = FALSE;
+ // The 'input_record_buffer' is an internal dynamic fifo queue of MS-Windows
+ // console INPUT_RECORD events that are normally read from the console input
+ // buffer.  This provides an injection point for testing the low-level 
handling
+ // of INPUT_RECORDs.
+ typedef struct input_record_buffer_node_S
+ {
+     INPUT_RECORD ir;
+     struct input_record_buffer_node_S *next;
+ } input_record_buffer_node_T;
+ typedef struct input_record_buffer_S
+ {
+     input_record_buffer_node_T *head;
+     input_record_buffer_node_T *tail;
+     int length;
+ } input_record_buffer_T;
+ static input_record_buffer_T input_record_buffer;
+ static int peek_input_record_buffer(INPUT_RECORD* irEvents, int nMaxLength);
+ static int read_input_record_buffer(INPUT_RECORD* irEvents, int nMaxLength);
+ static int write_input_record_buffer(INPUT_RECORD* irEvents, int nLength);
  #endif
  #ifdef FEAT_GUI_MSWIN
  static int s_dont_use_vimrun = TRUE;
***************
*** 224,230 ****
  static void set_console_color_rgb(void);
  static void reset_console_color_rgb(void);
  static void restore_console_color_rgb(void);
! #endif
  
  // This flag is newly created from Windows 10
  #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
--- 243,249 ----
  static void set_console_color_rgb(void);
  static void reset_console_color_rgb(void);
  static void restore_console_color_rgb(void);
! #endif  // !FEAT_GUI_MSWIN || VIMDLL
  
  // This flag is newly created from Windows 10
  #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
***************
*** 319,324 ****
--- 338,350 ----
      int i;
      static INPUT_RECORD s_irPseudo;
  
+     if (s_dwMax == 0 && input_record_buffer.length > 0)
+     {
+       dwEvents = read_input_record_buffer(s_irCache, IRSIZE);
+       s_dwIndex = 0;
+       s_dwMax = dwEvents;
+     }
+ 
      if (nLength == -2)
        return (s_dwMax > 0) ? TRUE : FALSE;
  
***************
*** 431,437 ****
      return WaitForSingleObject(hHandle, dwMilliseconds);
  }
  # endif
! #endif
  
      static void
  get_exe_name(void)
--- 457,463 ----
      return WaitForSingleObject(hHandle, dwMilliseconds);
  }
  # endif
! #endif   // !FEAT_GUI_MSWIN || VIMDLL
  
      static void
  get_exe_name(void)
***************
*** 1014,1020 ****
        return 1;
      }
  
!     if (pker->uChar.UnicodeChar != 0)
        return 1;
  
      CLEAR_FIELD(abKeystate);
--- 1040,1046 ----
        return 1;
      }
  
!     if (pker->uChar.UnicodeChar > 0 && pker->uChar.UnicodeChar < 0xfffd)
        return 1;
  
      CLEAR_FIELD(abKeystate);
***************
*** 1080,1086 ****
  
      // special cases
      if ((nModifs & CTRL) != 0 && (nModifs & ~CTRL) == 0
!                                           && pker->uChar.UnicodeChar == NUL)
      {
        // Ctrl-6 is Ctrl-^
        if (pker->wVirtualKeyCode == '6')
--- 1106,1113 ----
  
      // special cases
      if ((nModifs & CTRL) != 0 && (nModifs & ~CTRL) == 0
!                                       && (pker->uChar.UnicodeChar == NUL
!                                       || pker->uChar.UnicodeChar == 0xfffd))
      {
        // Ctrl-6 is Ctrl-^
        if (pker->wVirtualKeyCode == '6')
***************
*** 1168,1174 ****
      return (*pch != NUL);
  }
  
! #endif // FEAT_GUI_MSWIN
  
  
  /*
--- 1195,1307 ----
      return (*pch != NUL);
  }
  
! # if defined(FEAT_EVAL)
!     static int
! encode_key_event(dict_T *args, INPUT_RECORD *ir)
! {
!     static int s_dwMods = 0;
! 
!     char_u *event = dict_get_string(args, "event", TRUE);
!     if (event && (STRICMP(event, "keydown") == 0
!                                       || STRICMP(event, "keyup") == 0))
!     {
!       WORD vkCode = dict_get_number_def(args, "keycode", 0);
!       if (vkCode <= 0 || vkCode >= 0xFF)
!       {
!           semsg(_(e_invalid_argument_nr), (long)vkCode);
!           return FALSE;
!       }
! 
!       ir->EventType = KEY_EVENT;
!       KEY_EVENT_RECORD ker;
!       ZeroMemory(&ker, sizeof(ker));
!       ker.bKeyDown = STRICMP(event, "keydown") == 0;
!       ker.wRepeatCount = 1;
!       ker.wVirtualScanCode = 0;
!       ker.dwControlKeyState = 0;
!       int mods = (int)dict_get_number(args, "modifiers");
!       // Encode the win32 console key modifiers from Vim keyboard modifiers.
!       if (mods)
!       {
!           // If "modifiers" is explicitly set in the args, then we reset any
!           // remembered modifer key state that may have been set from earlier
!           // mod-key-down events, even if they are not yet unset by earlier
!           // mod-key-up events.
!           s_dwMods = 0;
!           if (mods & MOD_MASK_SHIFT)
!               ker.dwControlKeyState |= SHIFT_PRESSED;
!           if (mods & MOD_MASK_CTRL)
!               ker.dwControlKeyState |= LEFT_CTRL_PRESSED;
!           if (mods & MOD_MASK_ALT)
!               ker.dwControlKeyState |= LEFT_ALT_PRESSED;
!       }
! 
!       if (vkCode == VK_LSHIFT || vkCode == VK_RSHIFT || vkCode == VK_SHIFT)
!       {
!           if (STRICMP(event, "keydown") == 0)
!               s_dwMods |= SHIFT_PRESSED;
!           else
!               s_dwMods &= ~SHIFT_PRESSED;
!       }
!       else if (vkCode == VK_LCONTROL || vkCode == VK_CONTROL)
!       {
!           if (STRICMP(event, "keydown") == 0)
!               s_dwMods |= LEFT_CTRL_PRESSED;
!           else
!               s_dwMods &= ~LEFT_CTRL_PRESSED;
!       }
!       else if (vkCode == VK_RCONTROL)
!       {
!           if (STRICMP(event, "keydown") == 0)
!               s_dwMods |= RIGHT_CTRL_PRESSED;
!           else
!               s_dwMods &= ~RIGHT_CTRL_PRESSED;
!       }
!       else if (vkCode == VK_LMENU || vkCode == VK_MENU)
!       {
!           if (STRICMP(event, "keydown") == 0)
!               s_dwMods |= LEFT_ALT_PRESSED;
!           else
!               s_dwMods &= ~LEFT_ALT_PRESSED;
!       }
!       else if (vkCode == VK_RMENU)
!       {
!           if (STRICMP(event, "keydown") == 0)
!               s_dwMods |= RIGHT_ALT_PRESSED;
!           else
!               s_dwMods &= ~RIGHT_ALT_PRESSED;
!       }
!       ker.dwControlKeyState |= s_dwMods;
!       ker.wVirtualKeyCode = vkCode;
!       win32_kbd_patch_key(&ker);
! 
!       for (int i = ARRAY_LENGTH(VirtKeyMap);
!            --i >= 0 && !ker.uChar.UnicodeChar; )
!       {
!           if (VirtKeyMap[i].wVirtKey == vkCode)
!               ker.uChar.UnicodeChar = 0xfffd;  // REPLACEMENT CHARACTER
!       }
! 
!       ir->Event.KeyEvent = ker;
!       vim_free(event);
!     }
!     else
!     {
!       if (event == NULL)
!       {
!           semsg(_(e_missing_argument_str), "event");
!       }
!       else
!       {
!           semsg(_(e_invalid_value_for_argument_str_str), "event", event);
!           vim_free(event);
!       }
!       return FALSE;
!     }
!     return TRUE;
! }
! # endif  // FEAT_EVAL
! #endif // !FEAT_GUI_MSWIN || VIMDLL
  
  
  /*
***************
*** 1179,1185 ****
  mch_setmouse(int on UNUSED)
  {
  }
! #else
  static int g_fMouseAvail = FALSE;   // mouse present
  static int g_fMouseActive = FALSE;  // mouse enabled
  static int g_nMouseClick = -1;            // mouse status
--- 1312,1318 ----
  mch_setmouse(int on UNUSED)
  {
  }
! #else  // !FEAT_GUI_MSWIN || VIMDLL
  static int g_fMouseAvail = FALSE;   // mouse present
  static int g_fMouseActive = FALSE;  // mouse enabled
  static int g_nMouseClick = -1;            // mouse status
***************
*** 1234,1254 ****
  
  /*
   * Win32 console mouse scroll event handler.
!  * Loosely based on the _OnMouseWheel() function in gui_w32.c
   *
   * This encodes the mouse scroll direction and keyboard modifiers into
   * g_nMouseClick, and the mouse position into g_xMouse and g_yMouse
   *
   * The direction of the scroll is decoded from two fields of the win32 console
   * mouse event record;
!  *    1. The axis - vertical or horizontal flag - from dwEventFlags, and
   *    2. The sign - positive or negative (aka delta flag) - from dwButtonState
   *
!  * When scroll axis is HORIZONTAL
   *    -  If the high word of the dwButtonState member contains a positive
   *     value, the wheel was rotated to the right.
   *    -  Otherwise, the wheel was rotated to the left.
!  * When scroll axis is VERTICAL
   *    -  If the high word of the dwButtonState member contains a positive 
value,
   *       the wheel was rotated forward, away from the user.
   *    -  Otherwise, the wheel was rotated backward, toward the user.
--- 1367,1387 ----
  
  /*
   * Win32 console mouse scroll event handler.
!  * Console version of the _OnMouseWheel() function in gui_w32.c
   *
   * This encodes the mouse scroll direction and keyboard modifiers into
   * g_nMouseClick, and the mouse position into g_xMouse and g_yMouse
   *
   * The direction of the scroll is decoded from two fields of the win32 console
   * mouse event record;
!  *    1. The orientation - vertical or horizontal flag - from dwEventFlags
   *    2. The sign - positive or negative (aka delta flag) - from dwButtonState
   *
!  * When scroll orientation is HORIZONTAL
   *    -  If the high word of the dwButtonState member contains a positive
   *     value, the wheel was rotated to the right.
   *    -  Otherwise, the wheel was rotated to the left.
!  * When scroll orientation is VERTICAL
   *    -  If the high word of the dwButtonState member contains a positive 
value,
   *       the wheel was rotated forward, away from the user.
   *    -  Otherwise, the wheel was rotated backward, toward the user.
***************
*** 1594,1601 ****
      return TRUE;
  }
  
! #endif // FEAT_GUI_MSWIN
  
  
  #ifdef MCH_CURSOR_SHAPE
  /*
--- 1727,1957 ----
      return TRUE;
  }
  
! # ifdef FEAT_EVAL
!     static int
! encode_mouse_event(dict_T *args, INPUT_RECORD *ir)
! {
!     int               button;
!     int               row;
!     int               col;
!     int               repeated_click;
!     int_u     mods;
!     int               move;
! 
!     if (!dict_has_key(args, "row") || !dict_has_key(args, "col"))
!       return FALSE;
! 
!     // Note: "move" is optional, requires fewer arguments
!     move = (int)dict_get_bool(args, "move", FALSE);
!     if (!move && (!dict_has_key(args, "button")
!           || !dict_has_key(args, "multiclick")
!           || !dict_has_key(args, "modifiers")))
!       return FALSE;
! 
!     row = (int)dict_get_number(args, "row") - 1;
!     col = (int)dict_get_number(args, "col") - 1;
! 
!     ir->EventType = MOUSE_EVENT;
!     MOUSE_EVENT_RECORD mer;
!     ZeroMemory(&mer, sizeof(mer));
!     mer.dwMousePosition.X  = col;
!     mer.dwMousePosition.Y  = row;
! 
!     if (move)
!     {
!       mer.dwButtonState = 0;
!       mer.dwEventFlags = MOUSE_MOVED;
!     }
!     else
!     {
!       button = (int)dict_get_number(args, "button");
!       repeated_click = (int)dict_get_number(args, "multiclick");
!       mods = (int)dict_get_number(args, "modifiers");
!       // Reset the scroll values to known values.
!       // XXX: Remove this when/if the scroll step is made configurable.
!       mouse_set_hor_scroll_step(6);
!       mouse_set_vert_scroll_step(3);
! 
!       switch (button)
!       {
!           case MOUSE_LEFT:
!               mer.dwButtonState = FROM_LEFT_1ST_BUTTON_PRESSED;
!               mer.dwEventFlags = repeated_click == 1 ? DOUBLE_CLICK : 0;
!               break;
!           case MOUSE_MIDDLE:
!               mer.dwButtonState = FROM_LEFT_2ND_BUTTON_PRESSED;
!               mer.dwEventFlags = repeated_click == 1 ? DOUBLE_CLICK : 0;
!               break;
!           case MOUSE_RIGHT:
!               mer.dwButtonState = RIGHTMOST_BUTTON_PRESSED;
!               mer.dwEventFlags = repeated_click == 1 ? DOUBLE_CLICK : 0;
!               break;
!           case MOUSE_RELEASE:
!               // umm?  Assume Left Release?
!               mer.dwEventFlags = 0;
! 
!           case MOUSE_MOVE:
!               mer.dwButtonState = 0;
!               mer.dwEventFlags = MOUSE_MOVED;
!               break;
!           case MOUSE_X1:
!               mer.dwButtonState = FROM_LEFT_3RD_BUTTON_PRESSED;
!               break;
!           case MOUSE_X2:
!               mer.dwButtonState = FROM_LEFT_4TH_BUTTON_PRESSED;
!               break;
!           case MOUSE_4:  // KE_MOUSEDOWN;
!               mer.dwButtonState = -1;
!               mer.dwEventFlags = MOUSE_WHEELED;
!               break;
!           case MOUSE_5:  // KE_MOUSEUP;
!               mer.dwButtonState = +1;
!               mer.dwEventFlags = MOUSE_WHEELED;
!               break;
!           case MOUSE_6:  // KE_MOUSELEFT;
!               mer.dwButtonState = -1;
!               mer.dwEventFlags = MOUSE_HWHEELED;
!               break;
!           case MOUSE_7:  // KE_MOUSERIGHT;
!               mer.dwButtonState = +1;
!               mer.dwEventFlags = MOUSE_HWHEELED;
!               break;
!           default:
!               semsg(_(e_invalid_argument_str), "button");
!               return FALSE;
!       }
!     }
! 
!     mer.dwControlKeyState = 0;
!     if (mods != 0)
!     {
!       // Encode the win32 console key modifiers from Vim MOUSE modifiers.
!       if (mods & MOUSE_SHIFT)
!           mer.dwControlKeyState |= SHIFT_PRESSED;
!       if (mods & MOUSE_CTRL)
!           mer.dwControlKeyState |= LEFT_CTRL_PRESSED;
!       if (mods & MOUSE_ALT)
!           mer.dwControlKeyState |= LEFT_ALT_PRESSED;
!     }
!     ir->Event.MouseEvent = mer;
!     return TRUE;
! }
! # endif  // FEAT_EVAL
! 
!     static int
! write_input_record_buffer(INPUT_RECORD* irEvents, int nLength)
! {
!     int nCount = 0;
!     while (nCount < nLength)
!     {
!       input_record_buffer.length++;
!       input_record_buffer_node_T *event_node =
!                                   malloc(sizeof(input_record_buffer_node_T));
!       event_node->ir = irEvents[nCount++];
!       event_node->next = NULL;
!       if (input_record_buffer.tail == NULL)
!       {
!           input_record_buffer.head = event_node;
!           input_record_buffer.tail = event_node;
!       }
!       else
!       {
!           input_record_buffer.tail->next = event_node;
!           input_record_buffer.tail = input_record_buffer.tail->next;
!       }
!     }
!     return nCount;
! }
! 
!     static int
! read_input_record_buffer(INPUT_RECORD* irEvents, int nMaxLength)
! {
!     int nCount = 0;
!     while (nCount < nMaxLength && input_record_buffer.head != NULL)
!     {
!       input_record_buffer.length--;
!       input_record_buffer_node_T *pop_head = input_record_buffer.head;
!       irEvents[nCount++] = pop_head->ir;
!       input_record_buffer.head = pop_head->next;
!       vim_free(pop_head);
!       if (input_record_buffer.length == 0)
!           input_record_buffer.tail = NULL;
!     }
!     return nCount;
! }
!     static int
! peek_input_record_buffer(INPUT_RECORD* irEvents, int nMaxLength)
! {
!     int nCount = 0;
!     input_record_buffer_node_T *temp =  input_record_buffer.head;
!     while (nCount < nMaxLength && temp != NULL)
!     {
!       irEvents[nCount++] = temp->ir;
!       temp = temp->next;
!     }
!     return nCount;
! }
! #endif // !FEAT_GUI_MSWIN || VIMDLL
! 
! #ifdef FEAT_EVAL
! /*
!  * The 'test_mswin_event' function is for testing Vim's low-level handling of
!  * user input events.  ie, this manages the encoding of INPUT_RECORD events
!  * so that we have a way to test how Vim decodes INPUT_RECORD events in 
Windows
!  * consoles.
!  *
!  * The 'test_mswin_event' function is based on 'test_gui_event'.  In fact, 
when
!  * the Windows GUI is running, the arguments; 'event' and 'args', are the 
same.
!  * So, it acts as an alias for 'test_gui_event' for the Windows GUI.
!  *
!  * When the Windows console is running, the arguments; 'event' and 'args', are
!  * a subset of what 'test_gui_event' handles, ie, only "key" and "mouse"
!  * events are encoded as INPUT_RECORD events.
!  *
!  * Note: INPUT_RECORDs are only used by the Windows console, not the GUI.  The
!  * GUI sends MSG structs instead.
!  */
!     int
! test_mswin_event(char_u *event, dict_T *args)
! {
!     int lpEventsWritten = 0;
! 
! # if defined(VIMDLL) || defined(FEAT_GUI_MSWIN)
!     if (gui.in_use)
!       return test_gui_w32_sendevent(event, args);
! # endif
! 
! # if defined(VIMDLL) || !defined(FEAT_GUI_MSWIN)
! 
! // Currently implemented event record types are; KEY_EVENT and MOUSE_EVENT
! // Potentially could also implement: FOCUS_EVENT and WINDOW_BUFFER_SIZE_EVENT
! // Maybe also:  MENU_EVENT
! 
!     INPUT_RECORD ir;
!     BOOL input_encoded = FALSE;
!     if (STRCMP(event, "key") == 0)
!       input_encoded = encode_key_event(args, &ir);
!     else if (STRCMP(event, "mouse") == 0)
!       input_encoded = encode_mouse_event(args, &ir);
!     else
!     {
!       semsg(_(e_invalid_value_for_argument_str_str), "event", event);
!       return FALSE;
!     }
! 
!     // Ideally, WriteConsoleInput would be used to inject these low-level
!     // events.  But, this doesnt work well in the CI test environment.  So
!     // implementing an input_record_buffer instead.
!     if (input_encoded)
!       lpEventsWritten = write_input_record_buffer(&ir, 1);
  
+     if (STRCMP(event, "mouse") == 0)
+       exec_normal(TRUE, TRUE, TRUE);
+ 
+ # endif
+     return lpEventsWritten;
+ }
+ #endif // FEAT_EVAL
  
  #ifdef MCH_CURSOR_SHAPE
  /*
*** ../vim-9.0.1083/src/proto/gui_w32.pro       2022-07-23 05:04:07.580839529 
+0100
--- src/proto/gui_w32.pro       2022-12-20 19:45:50.336740416 +0000
***************
*** 96,100 ****
  BalloonEval *gui_mch_create_beval_area(void *target, char_u *mesg, void 
(*mesgCB)(BalloonEval *, int), void *clientData);
  void gui_mch_destroy_beval_area(BalloonEval *beval);
  void netbeans_draw_multisign_indicator(int row);
! int test_gui_w32_sendevent(dict_T *args);
  /* vim: set ft=c : */
--- 96,100 ----
  BalloonEval *gui_mch_create_beval_area(void *target, char_u *mesg, void 
(*mesgCB)(BalloonEval *, int), void *clientData);
  void gui_mch_destroy_beval_area(BalloonEval *beval);
  void netbeans_draw_multisign_indicator(int row);
! int test_gui_w32_sendevent(char_u *event, dict_T *args);
  /* vim: set ft=c : */
*** ../vim-9.0.1083/src/proto/os_win32.pro      2022-11-12 18:45:59.818782978 
+0000
--- src/proto/os_win32.pro      2022-12-20 19:46:12.692713979 +0000
***************
*** 9,14 ****
--- 9,15 ----
  void PlatformId(void);
  void mch_setmouse(int on);
  void mch_bevalterm_changed(void);
+ int test_mswin_event(char_u *event, dict_T *args);
  void mch_update_cursor(void);
  int mch_char_avail(void);
  int mch_check_messages(void);
*** ../vim-9.0.1083/src/proto/testing.pro       2022-06-27 23:15:25.000000000 
+0100
--- src/proto/testing.pro       2022-12-20 19:47:23.100636236 +0000
***************
*** 33,38 ****
--- 33,39 ----
  void f_test_unknown(typval_T *argvars, typval_T *rettv);
  void f_test_void(typval_T *argvars, typval_T *rettv);
  void f_test_setmouse(typval_T *argvars, typval_T *rettv);
+ void f_test_mswin_event(typval_T *argvars, typval_T *rettv);
  void f_test_gui_event(typval_T *argvars, typval_T *rettv);
  void f_test_settime(typval_T *argvars, typval_T *rettv);
  /* vim: set ft=c : */
*** ../vim-9.0.1083/src/term.c  2022-12-19 18:56:44.169594372 +0000
--- src/term.c  2022-12-20 19:22:31.977188037 +0000
***************
*** 827,833 ****
  };
  
  /*
!  * These codes are valid for the Win32 Console .  The entries that start with
   * ESC | are translated into console calls in os_win32.c.  The function keys
   * are also translated in os_win32.c.
   */
--- 827,833 ----
  };
  
  /*
!  * These codes are valid for the Win32 Console.  The entries that start with
   * ESC | are translated into console calls in os_win32.c.  The function keys
   * are also translated in os_win32.c.
   */
*** ../vim-9.0.1083/src/testing.c       2022-12-08 15:32:11.083034191 +0000
--- src/testing.c       2022-12-20 19:22:31.977188037 +0000
***************
*** 1388,1400 ****
  
      if (move)
      {
        if (dict_get_bool(args, "cell", FALSE))
        {
!           // click in the middle of the character cell
!           row = row * gui.char_height + gui.char_height / 2;
!           col = col * gui.char_width + gui.char_width / 2;
        }
!       gui_mouse_moved(col, row);
      }
      else
      {
--- 1388,1405 ----
  
      if (move)
      {
+       int pY = row;
+       int pX = col;
+       // the "move" argument expects row and col coordnates to be in pixels,
+       // unless "cell" is specified and is TRUE.
        if (dict_get_bool(args, "cell", FALSE))
        {
!           // calculate the middle of the character cell
!           // Note: Cell coordinates are 1-based from vimscript
!           pY = (row - 1) * gui.char_height + gui.char_height / 2;
!           pX = (col - 1) * gui.char_width + gui.char_width / 2;
        }
!       gui_mouse_moved(pX, pY);
      }
      else
      {
***************
*** 1489,1494 ****
--- 1494,1523 ----
  # endif
  
      void
+ f_test_mswin_event(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
+ {
+ # ifdef MSWIN
+     rettv->v_type = VAR_BOOL;
+     rettv->vval.v_number = FALSE;
+ 
+     if (sandbox != 0)
+     {
+       emsg(_(e_not_allowed_in_sandbox));
+       return;
+     }
+ 
+     if (check_for_string_arg(argvars, 0) == FAIL
+           || check_for_dict_arg(argvars, 1) == FAIL
+           || argvars[1].vval.v_dict == NULL)
+       return;
+ 
+     char_u *event = tv_get_string(&argvars[0]);
+     rettv->vval.v_number = test_mswin_event(event, argvars[1].vval.v_dict);
+ 
+ # endif
+ }
+ 
+     void
  f_test_gui_event(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
  {
  # ifdef FEAT_GUI
***************
*** 1515,1520 ****
--- 1544,1553 ----
      else if (STRCMP(event, "findrepl") == 0)
        rettv->vval.v_number = test_gui_find_repl(argvars[1].vval.v_dict);
  #  endif
+ #  ifdef MSWIN
+     else if (STRCMP(event, "key") == 0 || STRCMP(event, "mouse") == 0)
+       rettv->vval.v_number = test_mswin_event(event, argvars[1].vval.v_dict);
+ #  endif
      else if (STRCMP(event, "mouse") == 0)
        rettv->vval.v_number = test_gui_mouse_event(argvars[1].vval.v_dict);
      else if (STRCMP(event, "scrollbar") == 0)
***************
*** 1523,1532 ****
        rettv->vval.v_number = test_gui_tabline_event(argvars[1].vval.v_dict);
      else if (STRCMP(event, "tabmenu") == 0)
        rettv->vval.v_number = test_gui_tabmenu_event(argvars[1].vval.v_dict);
- #  ifdef FEAT_GUI_MSWIN
-     else if (STRCMP(event, "sendevent") == 0)
-       rettv->vval.v_number = test_gui_w32_sendevent(argvars[1].vval.v_dict);
- #  endif
      else
      {
        semsg(_(e_invalid_argument_str), event);
--- 1556,1561 ----
*** ../vim-9.0.1083/src/testdir/Make_all.mak    2022-12-08 15:32:11.087034211 
+0000
--- src/testdir/Make_all.mak    2022-12-20 19:22:31.977188037 +0000
***************
*** 210,215 ****
--- 210,216 ----
        test_modeless \
        test_modeline \
        test_move \
+       test_mswin_event \
        test_mzscheme \
        test_nested_function \
        test_netbeans \
***************
*** 454,459 ****
--- 455,461 ----
        test_mksession.res \
        test_modeless.res \
        test_modeline.res \
+       test_mswin_event.res \
        test_mzscheme.res \
        test_nested_function.res \
        test_netbeans.res \
*** ../vim-9.0.1083/src/testdir/mouse.vim       2020-07-18 11:57:17.000000000 
+0100
--- src/testdir/mouse.vim       2022-12-20 19:49:05.844535922 +0000
***************
*** 20,25 ****
--- 20,46 ----
    let g:Ttymouse_netterm = []
  endif
  
+ " Vim Mouse Codes.
+ " Used by the GUI and by MS-Windows Consoles.
+ " Keep these in sync with vim.h
+ let s:MOUSE_CODE = {
+   \ 'BTN_LEFT'    :  0x00,
+   \ 'BTN_MIDDLE'  :  0x01,
+   \ 'BTN_RIGHT'   :  0x02,
+   \ 'BTN_RELEASE' :  0x03,
+   \ 'BTN_X1'      : 0x300,
+   \ 'BTN_X2'      : 0x400,
+   \ 'SCRL_DOWN'   : 0x100,
+   \ 'SCRL_UP'     : 0x200,
+   \ 'SCRL_LEFT'   : 0x500,
+   \ 'SCRL_RIGHT'  : 0x600,
+   \ 'MOVE'        : 0x700,
+   \ 'MOD_SHIFT'   :  0x04,
+   \ 'MOD_ALT'     :  0x08,
+   \ 'MOD_CTRL'    :  0x10,
+   \ }
+ 
+ 
  " Helper function to emit a terminal escape code.
  func TerminalEscapeCode(code, row, col, m)
    if &ttymouse ==# 'xterm2'
***************
*** 47,52 ****
--- 68,98 ----
      return printf("\<Esc>}%d,%d\r", a:row, a:col)
  endfunc
  
+ " Send low level mouse event to MS-Windows consoles or GUI
+ func MSWinMouseEvent(button, row, col, move, multiclick, modifiers)
+     let args = { }
+     let args.button = a:button
+     " Scroll directions are inverted in the GUI, no idea why.
+     if has('gui_running')
+       if a:button == s:MOUSE_CODE.SCRL_UP
+         let args.button = s:MOUSE_CODE.SCRL_DOWN
+       elseif a:button == s:MOUSE_CODE.SCRL_DOWN
+         let args.button = s:MOUSE_CODE.SCRL_UP
+       elseif a:button == s:MOUSE_CODE.SCRL_LEFT
+         let args.button = s:MOUSE_CODE.SCRL_RIGHT
+       elseif a:button == s:MOUSE_CODE.SCRL_RIGHT
+         let args.button = s:MOUSE_CODE.SCRL_LEFT
+       endif
+     endif
+     let args.row = a:row
+     let args.col = a:col
+     let args.move = a:move
+     let args.multiclick = a:multiclick
+     let args.modifiers = a:modifiers
+     call test_mswin_event("mouse", args)
+     unlet args
+ endfunc
+ 
  func MouseLeftClickCode(row, col)
    if &ttymouse ==# 'dec'
      return DecEscapeCode(2, 4, a:row, a:col)
***************
*** 58,64 ****
  endfunc
  
  func MouseLeftClick(row, col)
!   call feedkeys(MouseLeftClickCode(a:row, a:col), 'Lx!')
  endfunc
  
  func MouseMiddleClickCode(row, col)
--- 104,114 ----
  endfunc
  
  func MouseLeftClick(row, col)
!   if has('win32')
!     call MSWinMouseEvent(s:MOUSE_CODE.BTN_LEFT, a:row, a:col, 0, 0, 0)
!   else
!     call feedkeys(MouseLeftClickCode(a:row, a:col), 'Lx!')
!   endif
  endfunc
  
  func MouseMiddleClickCode(row, col)
***************
*** 70,76 ****
  endfunc
  
  func MouseMiddleClick(row, col)
!   call feedkeys(MouseMiddleClickCode(a:row, a:col), 'Lx!')
  endfunc
  
  func MouseRightClickCode(row, col)
--- 120,130 ----
  endfunc
  
  func MouseMiddleClick(row, col)
!   if has('win32')
!     call MSWinMouseEvent(s:MOUSE_CODE.BTN_MIDDLE, a:row, a:col, 0, 0, 0)
!   else
!     call feedkeys(MouseMiddleClickCode(a:row, a:col), 'Lx!')
!   endif
  endfunc
  
  func MouseRightClickCode(row, col)
***************
*** 82,88 ****
  endfunc
  
  func MouseRightClick(row, col)
!   call feedkeys(MouseRightClickCode(a:row, a:col), 'Lx!')
  endfunc
  
  func MouseCtrlLeftClickCode(row, col)
--- 136,146 ----
  endfunc
  
  func MouseRightClick(row, col)
!   if has('win32')
!     call MSWinMouseEvent(s:MOUSE_CODE.BTN_RIGHT, a:row, a:col, 0, 0, 0)
!   else
!     call feedkeys(MouseRightClickCode(a:row, a:col), 'Lx!')
!   endif
  endfunc
  
  func MouseCtrlLeftClickCode(row, col)
***************
*** 91,97 ****
  endfunc
  
  func MouseCtrlLeftClick(row, col)
!   call feedkeys(MouseCtrlLeftClickCode(a:row, a:col), 'Lx!')
  endfunc
  
  func MouseCtrlRightClickCode(row, col)
--- 149,160 ----
  endfunc
  
  func MouseCtrlLeftClick(row, col)
!   if has('win32')
!     call MSWinMouseEvent(s:MOUSE_CODE.BTN_LEFT, a:row, a:col, 0, 0,
!                                                          \ 
s:MOUSE_CODE.MOD_CTRL)
!   else
!     call feedkeys(MouseCtrlLeftClickCode(a:row, a:col), 'Lx!')
!   endif
  endfunc
  
  func MouseCtrlRightClickCode(row, col)
***************
*** 100,106 ****
  endfunc
  
  func MouseCtrlRightClick(row, col)
!   call feedkeys(MouseCtrlRightClickCode(a:row, a:col), 'Lx!')
  endfunc
  
  func MouseAltLeftClickCode(row, col)
--- 163,174 ----
  endfunc
  
  func MouseCtrlRightClick(row, col)
!   if has('win32')
!     call MSWinMouseEvent(s:MOUSE_CODE.BTN_RIGHT, a:row, a:col, 0, 0,
!                                                        \ 
s:MOUSE_CODE.MOD_CTRL)
!   else
!     call feedkeys(MouseCtrlRightClickCode(a:row, a:col), 'Lx!')
!   endif
  endfunc
  
  func MouseAltLeftClickCode(row, col)
***************
*** 109,115 ****
  endfunc
  
  func MouseAltLeftClick(row, col)
!   call feedkeys(MouseAltLeftClickCode(a:row, a:col), 'Lx!')
  endfunc
  
  func MouseAltRightClickCode(row, col)
--- 177,188 ----
  endfunc
  
  func MouseAltLeftClick(row, col)
!   if has('win32')
!     call MSWinMouseEvent(s:MOUSE_CODE.BTN_LEFT, a:row, a:col, 0, 0,
!                                                        \ s:MOUSE_CODE.MOD_ALT)
!   else
!     call feedkeys(MouseAltLeftClickCode(a:row, a:col), 'Lx!')
!   endif
  endfunc
  
  func MouseAltRightClickCode(row, col)
***************
*** 118,124 ****
  endfunc
  
  func MouseAltRightClick(row, col)
!   call feedkeys(MouseAltRightClickCode(a:row, a:col), 'Lx!')
  endfunc
  
  func MouseLeftReleaseCode(row, col)
--- 191,202 ----
  endfunc
  
  func MouseAltRightClick(row, col)
!   if has('win32')
!     call MSWinMouseEvent(s:MOUSE_CODE.BTN_RIGHT, a:row, a:col, 0, 0,
!                                                        \ s:MOUSE_CODE.MOD_ALT)
!   else
!     call feedkeys(MouseAltRightClickCode(a:row, a:col), 'Lx!')
!   endif
  endfunc
  
  func MouseLeftReleaseCode(row, col)
***************
*** 132,138 ****
  endfunc
  
  func MouseLeftRelease(row, col)
!   call feedkeys(MouseLeftReleaseCode(a:row, a:col), 'Lx!')
  endfunc
  
  func MouseMiddleReleaseCode(row, col)
--- 210,220 ----
  endfunc
  
  func MouseLeftRelease(row, col)
!   if has('win32')
!     call MSWinMouseEvent(s:MOUSE_CODE.BTN_RELEASE, a:row, a:col, 0, 0, 0)
!   else
!     call feedkeys(MouseLeftReleaseCode(a:row, a:col), 'Lx!')
!   endif
  endfunc
  
  func MouseMiddleReleaseCode(row, col)
***************
*** 144,150 ****
  endfunc
  
  func MouseMiddleRelease(row, col)
!   call feedkeys(MouseMiddleReleaseCode(a:row, a:col), 'Lx!')
  endfunc
  
  func MouseRightReleaseCode(row, col)
--- 226,236 ----
  endfunc
  
  func MouseMiddleRelease(row, col)
!   if has('win32')
!     call MSWinMouseEvent(s:MOUSE_CODE.BTN_RELEASE, a:row, a:col, 0, 0, 0)
!   else
!     call feedkeys(MouseMiddleReleaseCode(a:row, a:col), 'Lx!')
!   endif
  endfunc
  
  func MouseRightReleaseCode(row, col)
***************
*** 156,162 ****
  endfunc
  
  func MouseRightRelease(row, col)
!   call feedkeys(MouseRightReleaseCode(a:row, a:col), 'Lx!')
  endfunc
  
  func MouseLeftDragCode(row, col)
--- 242,252 ----
  endfunc
  
  func MouseRightRelease(row, col)
!   if has('win32')
!     call MSWinMouseEvent(s:MOUSE_CODE.BTN_RELEASE, a:row, a:col, 0, 0, 0)
!   else
!     call feedkeys(MouseRightReleaseCode(a:row, a:col), 'Lx!')
!   endif
  endfunc
  
  func MouseLeftDragCode(row, col)
***************
*** 168,174 ****
  endfunc
  
  func MouseLeftDrag(row, col)
!   call feedkeys(MouseLeftDragCode(a:row, a:col), 'Lx!')
  endfunc
  
  func MouseWheelUpCode(row, col)
--- 258,268 ----
  endfunc
  
  func MouseLeftDrag(row, col)
!   if has('win32')
!     call MSWinMouseEvent(s:MOUSE_CODE.BTN_LEFT, a:row, a:col, 1, 0, 0)
!   else
!     call feedkeys(MouseLeftDragCode(a:row, a:col), 'Lx!')
!   endif
  endfunc
  
  func MouseWheelUpCode(row, col)
***************
*** 176,182 ****
  endfunc
  
  func MouseWheelUp(row, col)
!   call feedkeys(MouseWheelUpCode(a:row, a:col), 'Lx!')
  endfunc
  
  func MouseWheelDownCode(row, col)
--- 270,280 ----
  endfunc
  
  func MouseWheelUp(row, col)
!   if has('win32')
!     call MSWinMouseEvent(s:MOUSE_CODE.SCRL_UP, a:row, a:col, 0, 0, 0)
!   else
!     call feedkeys(MouseWheelUpCode(a:row, a:col), 'Lx!')
!   endif
  endfunc
  
  func MouseWheelDownCode(row, col)
***************
*** 184,190 ****
  endfunc
  
  func MouseWheelDown(row, col)
!   call feedkeys(MouseWheelDownCode(a:row, a:col), 'Lx!')
  endfunc
  
  func MouseWheelLeftCode(row, col)
--- 282,292 ----
  endfunc
  
  func MouseWheelDown(row, col)
!   if has('win32')
!     call MSWinMouseEvent(s:MOUSE_CODE.SCRL_DOWN, a:row, a:col, 0, 0, 0)
!   else
!     call feedkeys(MouseWheelDownCode(a:row, a:col), 'Lx!')
!   endif
  endfunc
  
  func MouseWheelLeftCode(row, col)
***************
*** 192,198 ****
  endfunc
  
  func MouseWheelLeft(row, col)
!   call feedkeys(MouseWheelLeftCode(a:row, a:col), 'Lx!')
  endfunc
  
  func MouseWheelRightCode(row, col)
--- 294,304 ----
  endfunc
  
  func MouseWheelLeft(row, col)
!   if has('win32')
!     call MSWinMouseEvent(s:MOUSE_CODE.SCRL_LEFT, a:row, a:col, 0, 0, 0)
!   else
!     call feedkeys(MouseWheelLeftCode(a:row, a:col), 'Lx!')
!   endif
  endfunc
  
  func MouseWheelRightCode(row, col)
***************
*** 200,206 ****
  endfunc
  
  func MouseWheelRight(row, col)
!   call feedkeys(MouseWheelRightCode(a:row, a:col), 'Lx!')
  endfunc
  
  " vim: shiftwidth=2 sts=2 expandtab
--- 306,372 ----
  endfunc
  
  func MouseWheelRight(row, col)
!   if has('win32')
!     call MSWinMouseEvent(s:MOUSE_CODE.SCRL_RIGHT, a:row, a:col, 0, 0, 0)
!   else
!     call feedkeys(MouseWheelRightCode(a:row, a:col), 'Lx!')
!   endif
! endfunc
! 
! func MouseShiftWheelUpCode(row, col)
!   " todo feed shift mod.
!   return TerminalEscapeCode(0x40, a:row, a:col, 'M')
! endfunc
! 
! func MouseShiftWheelUp(row, col)
!   if has('win32')
!     call MSWinMouseEvent(s:MOUSE_CODE.SCRL_UP, a:row, a:col, 0, 0,
!                                                       \ 
s:MOUSE_CODE.MOD_SHIFT)
!   else
!     call feedkeys(MouseShiftWheelUpCode(a:row, a:col), 'Lx!')
!   endif
! endfunc
! 
! func MouseShiftWheelDownCode(row, col)
!   " todo feed shift mod.
!   return TerminalEscapeCode(0x41, a:row, a:col, 'M')
! endfunc
! 
! func MouseShiftWheelDown(row, col)
!   if has('win32')
!     call MSWinMouseEvent(s:MOUSE_CODE.SCRL_DOWN, a:row, a:col, 0, 0,
!                                                       \ 
s:MOUSE_CODE.MOD_SHIFT)
!   else
!     call feedkeys(MouseShiftWheelDownCode(a:row, a:col), 'Lx!')
!   endif
! endfunc
! 
! func MouseShiftWheelLeftCode(row, col)
!   " todo feed shift mod.
!   return TerminalEscapeCode(0x42, a:row, a:col, 'M')
! endfunc
! 
! func MouseShiftWheelLeft(row, col)
!   if has('win32')
!     call MSWinMouseEvent(s:MOUSE_CODE.SCRL_LEFT, a:row, a:col, 0, 0,
!                                                       \ 
s:MOUSE_CODE.MOD_SHIFT)
!   else
!     call feedkeys(MouseShiftWheelLeftCode(a:row, a:col), 'Lx!')
!   endif
! endfunc
! 
! func MouseShiftWheelRightCode(row, col)
!       " todo feed shift mod.
!   return TerminalEscapeCode(0x43, a:row, a:col, 'M')
! endfunc
! 
! func MouseShiftWheelRight(row, col)
!   if has('win32')
!     call MSWinMouseEvent(s:MOUSE_CODE.SCRL_RIGHT, a:row, a:col, 0, 0,
!                                                       \ 
s:MOUSE_CODE.MOD_SHIFT)
!   else
!     call feedkeys(MouseShiftWheelRightCode(a:row, a:col), 'Lx!')
!   endif
  endfunc
  
  " vim: shiftwidth=2 sts=2 expandtab
*** ../vim-9.0.1083/src/testdir/test_gui.vim    2022-11-28 21:20:44.416909580 
+0000
--- src/testdir/test_gui.vim    2022-12-20 19:22:31.977188037 +0000
***************
*** 1281,1287 ****
      let g:eventlist = g:eventlist[1 : ]
    endif
  
!   call assert_equal([#{row: 4, col: 31}, #{row: 11, col: 31}], g:eventlist)
  
    " wiggle the mouse around within a screen cell, shouldn't trigger events
    call extend(args, #{cell: v:false})
--- 1281,1287 ----
      let g:eventlist = g:eventlist[1 : ]
    endif
  
!   call assert_equal([#{row: 3, col: 30}, #{row: 10, col: 30}], g:eventlist)
  
    " wiggle the mouse around within a screen cell, shouldn't trigger events
    call extend(args, #{cell: v:false})
***************
*** 1638,1647 ****
  " Test for sending low level key presses
  func SendKeys(keylist)
    for k in a:keylist
!     call test_gui_event("sendevent", #{event: "keydown", keycode: k})
    endfor
    for k in reverse(a:keylist)
!     call test_gui_event("sendevent", #{event: "keyup", keycode: k})
    endfor
  endfunc
  
--- 1638,1647 ----
  " Test for sending low level key presses
  func SendKeys(keylist)
    for k in a:keylist
!     call test_gui_event("key", #{event: "keydown", keycode: k})
    endfor
    for k in reverse(a:keylist)
!     call test_gui_event("key", #{event: "keyup", keycode: k})
    endfor
  endfunc
  
*** ../vim-9.0.1083/src/testdir/test_mswin_event.vim    2022-12-20 
19:59:53.400124626 +0000
--- src/testdir/test_mswin_event.vim    2022-12-20 19:57:01.584208587 +0000
***************
*** 0 ****
--- 1,651 ----
+ " Test MS-Windows console event handling.
+ 
+ source check.vim
+ CheckMSWindows
+ " The mswin events should also work in gui
+ 
+ source mouse.vim
+ 
+ " Helper function for sending a sequence of low level key presses
+ " The modifer key(s) can be included as normal key presses in the sequence
+ func SendKeys(keylist)
+   for k in a:keylist
+     call test_mswin_event("key", #{event: "keydown", keycode: k})
+   endfor
+   for k in reverse(copy(a:keylist))
+     call test_mswin_event("key", #{event: "keyup", keycode: k})
+   endfor
+ endfunc
+ 
+ " Send an individual key press
+ " the modifers for the key press can be specified in the modifiers arg.
+ func SendKey(key, modifiers)
+   let args = { }
+   let args.keycode = a:key
+   let args.modifiers = a:modifiers
+   let args.event = "keydown"
+   call test_mswin_event("key", args)
+   let args.event = "keyup"
+   call test_mswin_event("key", args)
+   unlet args
+ endfunc
+ 
+ " Test MS-Windows console key events
+ func Test_mswin_key_event()
+   CheckMSWindows
+   new
+ 
+   " flush out any garbage left in the buffer
+   while getchar(0)
+   endwhile
+ 
+   let VK = #{
+       \ SPACE      : 0x20,
+       \ SHIFT      : 0x10,
+       \ LSHIFT     : 0xA0,
+       \ RSHIFT     : 0xA1,
+       \ CONTROL    : 0x11,
+       \ LCONTROL   : 0xA2,
+       \ RCONTROL   : 0xA3,
+       \ MENU       : 0x12,
+       \ ALT        : 0x12,
+       \ LMENU      : 0xA4,
+       \ LALT       : 0xA4,
+       \ RMENU      : 0xA5,
+       \ RALT       : 0xA5,
+       \ OEM_1      : 0xBA,
+       \ OEM_2      : 0xBF,
+       \ OEM_3      : 0xC0,
+       \ OEM_4      : 0xDB,
+       \ OEM_5      : 0xDC,
+       \ OEM_6      : 0xDD,
+       \ OEM_7      : 0xDE,
+       \ OEM_PLUS   : 0xBB,
+       \ OEM_COMMA  : 0xBC,
+       \ OEM_MINUS  : 0xBD,
+       \ OEM_PERIOD : 0xBE,
+       \ PRIOR      : 0x21,
+       \ NEXT       : 0x22,
+       \ END        : 0x23,
+       \ HOME       : 0x24,
+       \ LEFT       : 0x25,
+       \ UP         : 0x26,
+       \ RIGHT      : 0x27,
+       \ DOWN       : 0x28,
+       \ KEY_0      : 0x30,
+       \ KEY_1      : 0x31,
+       \ KEY_2      : 0x32,
+       \ KEY_3      : 0x33,
+       \ KEY_4      : 0x34,
+       \ KEY_5      : 0x35,
+       \ KEY_6      : 0x36,
+       \ KEY_7      : 0x37,
+       \ KEY_8      : 0x38,
+       \ KEY_9      : 0x39,
+       \ NUMPAD0    : 0x60,
+       \ NUMPAD1    : 0x61,
+       \ NUMPAD2    : 0x62,
+       \ NUMPAD3    : 0x63,
+       \ NUMPAD4    : 0x64,
+       \ NUMPAD5    : 0x65,
+       \ NUMPAD6    : 0x66,
+       \ NUMPAD7    : 0x67,
+       \ NUMPAD8    : 0x68,
+       \ NUMPAD9    : 0x69,
+       \ MULTIPLY   : 0x6A,
+       \ ADD        : 0x6B,
+       \ SUBTRACT   : 0x6D,
+       \ F1         : 0x70,
+       \ F2         : 0x71,
+       \ F3         : 0x72,
+       \ F4         : 0x73,
+       \ F5         : 0x74,
+       \ F6         : 0x75,
+       \ F7         : 0x76,
+       \ F8         : 0x77,
+       \ F9         : 0x78,
+       \ F10        : 0x79,
+       \ F11        : 0x7A,
+       \ F12        : 0x7B,
+       \ KEY_A      : 0x41,
+       \ KEY_B      : 0x42,
+       \ KEY_C      : 0x43,
+       \ KEY_D      : 0x44,
+       \ KEY_E      : 0x45,
+       \ KEY_F      : 0x46,
+       \ KEY_G      : 0x47,
+       \ KEY_H      : 0x48,
+       \ KEY_I      : 0x49,
+       \ KEY_J      : 0x4A,
+       \ KEY_K      : 0x4B,
+       \ KEY_L      : 0x4C,
+       \ KEY_M      : 0x4D,
+       \ KEY_N      : 0x4E,
+       \ KEY_O      : 0x4F,
+       \ KEY_P      : 0x50,
+       \ KEY_Q      : 0x51,
+       \ KEY_R      : 0x52,
+       \ KEY_S      : 0x53,
+       \ KEY_T      : 0x54,
+       \ KEY_U      : 0x55,
+       \ KEY_V      : 0x56,
+       \ KEY_W      : 0x57,
+       \ KEY_X      : 0x58,
+       \ KEY_Y      : 0x59,
+       \ KEY_Z      : 0x5A     
+       \ }
+ 
+   let vim_MOD_MASK_SHIFT = 0x02
+   let vim_MOD_MASK_CTRL  = 0x04
+   let vim_MOD_MASK_ALT   = 0x08
+   
+   let vim_key_modifiers = [
+     \ ["",       0,   []],
+     \ ["S-",     2,   [VK.SHIFT]],
+     \ ["C-",     4,   [VK.CONTROL]],
+     \ ["C-S-",   6,   [VK.CONTROL, VK.SHIFT]],
+     \ ["A-",     8,   [VK.MENU]],
+     \ ["A-S-",   10,  [VK.MENU, VK.SHIFT]],
+     \ ["A-C-",   12,  [VK.MENU, VK.CONTROL]],
+     \ ["A-C-S-", 14,  [VK.MENU, VK.CONTROL, VK.SHIFT]],
+     \]
+ 
+   " Some punctuation characters
+   " Assuming Standard US PC Keyboard layout
+   let test_punctuation_keys = [
+       \ [[VK.SPACE], ' '],
+       \ [[VK.OEM_1], ';'],
+       \ [[VK.OEM_2], '/'],
+       \ [[VK.OEM_3], '`'],
+       \ [[VK.OEM_4], '['],
+       \ [[VK.OEM_5], '\'],
+       \ [[VK.OEM_6], ']'],
+       \ [[VK.OEM_7], ''''],
+       \ [[VK.OEM_PLUS], '='],
+       \ [[VK.OEM_COMMA], ','],
+       \ [[VK.OEM_MINUS], '-'],
+       \ [[VK.OEM_PERIOD], '.'],
+       \ [[VK.SHIFT, VK.OEM_1], ':'],
+       \ [[VK.SHIFT, VK.OEM_2], '?'],
+       \ [[VK.SHIFT, VK.OEM_3], '~'],
+       \ [[VK.SHIFT, VK.OEM_4], '{'],
+       \ [[VK.SHIFT, VK.OEM_5], '|'],
+       \ [[VK.SHIFT, VK.OEM_6], '}'],
+       \ [[VK.SHIFT, VK.OEM_7], '"'],
+       \ [[VK.SHIFT, VK.OEM_PLUS], '+'],
+       \ [[VK.SHIFT, VK.OEM_COMMA], '<'],
+       \ [[VK.SHIFT, VK.OEM_MINUS], '_'],
+       \ [[VK.SHIFT, VK.OEM_PERIOD], '>'],
+       \ [[VK.SHIFT, VK.KEY_1], '!'],
+       \ [[VK.SHIFT, VK.KEY_2], '@'],
+       \ [[VK.SHIFT, VK.KEY_3], '#'],
+       \ [[VK.SHIFT, VK.KEY_4], '$'],
+       \ [[VK.SHIFT, VK.KEY_5], '%'],
+       \ [[VK.SHIFT, VK.KEY_6], '^'],
+       \ [[VK.SHIFT, VK.KEY_7], '&'],
+       \ [[VK.SHIFT, VK.KEY_8], '*'],
+       \ [[VK.SHIFT, VK.KEY_9], '('],
+       \ [[VK.SHIFT, VK.KEY_0], ')'],
+       \ [[VK.LSHIFT, VK.KEY_9], '('],
+       \ [[VK.RSHIFT, VK.KEY_0], ')']
+       \ ]
+ 
+   for [kcodes, kstr] in test_punctuation_keys
+     call SendKeys(kcodes)
+     let ch = getcharstr(0)
+     call assert_equal($"{kstr}", $"{ch}")
+     let mod_mask = getcharmod()
+     " the mod_mask is zero when no modifiers are used
+     " and when the virtual termcap maps shift the character
+     call assert_equal(0, mod_mask, $"key = {kstr}")
+   endfor
+   
+   " flush out any garbage left in the buffer
+   while getchar(0)
+   endwhile
+ 
+   for [kcodes, kstr] in test_punctuation_keys
+     let modifiers = 0
+     let key = kcodes[0]
+ 
+     for key in kcodes
+       if index([VK.SHIFT, VK.LSHIFT, VK.RSHIFT], key) >= 0
+         let modifiers = modifiers + vim_MOD_MASK_SHIFT
+       endif
+       if index([VK.CONTROL, VK.LCONTROL, VK.RCONTROL], key) >= 0
+         let modifiers = modifiers + vim_MOD_MASK_CTRL
+       endif
+       if index([VK.ALT, VK.LALT, VK.RALT], key) >= 0
+         let modifiers = modifiers + vim_MOD_MASK_ALT
+       endif
+     endfor
+ 
+     call SendKey(key, modifiers)
+     let ch = getcharstr(0)
+     call assert_equal($"{kstr}", $"{ch}")
+     let mod_mask = getcharmod()
+     " workaround for the virtual termcap maps changing the character instead
+     " of sending Shift
+     if index([VK.SHIFT, VK.LSHIFT, VK.RSHIFT], kcodes[0]) >= 0
+       let modifiers = modifiers - vim_MOD_MASK_SHIFT
+     endif
+     call assert_equal(modifiers, mod_mask, $"key = {kstr}")
+   endfor
+ 
+   " flush out any garbage left in the buffer
+   while getchar(0)
+   endwhile
+ 
+ " Test keyboard codes for digits
+ " (0x30 - 0x39) : VK_0 - VK_9 are the same as ASCII '0' - '9'
+   for kc in range(48, 57)
+     call SendKeys([kc])
+     let ch = getcharstr(0)
+     call assert_equal(nr2char(kc), ch)
+     call SendKey(kc, 0)
+     let ch = getcharstr(0)
+     call assert_equal(nr2char(kc), ch)
+   endfor
+ 
+ " Test keyboard codes for Alt-0 to Alt-9
+ " Expect +128 from the digit char codes
+   for modkey in [VK.ALT, VK.LALT, VK.RALT]
+     for kc in range(48, 57)
+       call SendKeys([modkey, kc])
+       let ch = getchar(0)
+       call assert_equal(kc+128, ch)
+       call SendKey(kc, vim_MOD_MASK_ALT)
+       let ch = getchar(0)
+       call assert_equal(kc+128, ch)
+     endfor
+   endfor
+ 
+ " Test for lowercase 'a' to 'z', VK codes 65(0x41) - 90(0x5A)
+ " Note: VK_A-VK_Z virtual key codes coincide with uppercase ASCII codes A-Z.
+ " eg VK_A is 65, and the ASCII character code for uppercase 'A' is also 65.
+ " Caution: these are interpreted as lowercase when Shift is NOT pressed. 
+ " eg, sending VK_A (65) 'A' Key code without shift modifier, will produce 
ASCII
+ " char 'a' (91) as the output.  The ASCII codes for the lowercase letters are
+ " numbered 32 higher than their uppercase versions.
+   for kc in range(65, 90)
+     call SendKeys([kc])
+     let ch = getcharstr(0)
+     call assert_equal(nr2char(kc + 32), ch)
+     call SendKey(kc, 0)
+     let ch = getcharstr(0)
+     call assert_equal(nr2char(kc + 32), ch)
+   endfor
+ 
+ "  Test for Uppercase 'A' - 'Z' keys
+ "  ie. with VK_SHIFT, expect the keycode = character code.
+   for kc in range(65, 90)
+     call SendKeys([VK.SHIFT, kc])
+     let ch = getcharstr(0)
+     call assert_equal(nr2char(kc), ch)
+     call SendKey(kc, vim_MOD_MASK_SHIFT)
+     let ch = getcharstr(0)
+     call assert_equal(nr2char(kc), ch)
+   endfor
+ 
+   " Test for <Ctrl-A> to <Ctrl-Z> keys
+  "  Same as for lowercase, except with Ctrl Key
+  "  Expect the unicode characters 0x01 to 0x1A
+    for modkey in [VK.CONTROL, VK.LCONTROL, VK.RCONTROL]
+     for kc in range(65, 90)
+       call SendKeys([modkey, kc])
+       let ch = getcharstr(0)
+       call assert_equal(nr2char(kc - 64), ch)
+       call SendKey(kc, vim_MOD_MASK_CTRL)
+       let ch = getcharstr(0)
+       call assert_equal(nr2char(kc - 64), ch)
+     endfor
+   endfor
+ 
+   if !has("gui_running")
+   " Test for <Alt-A> to <Alt-Z> keys
+   "  Expect the unicode characters 0xE1 to 0xFA
+   "  ie. 160 higher than the lowercase equivalent
+     for kc in range(65, 90)
+       call SendKeys([VK.LMENU, kc])
+       let ch = getchar(0)
+       call assert_equal(kc+160, ch)
+       call SendKey(kc, vim_MOD_MASK_ALT)
+       let ch = getchar(0)
+       call assert_equal(kc+160, ch)
+     endfor
+   endif
+ 
+   if !has("gui_running")
+     " Test for Function Keys 'F1' to 'F12'
+     for n in range(1, 12)
+       let kstr = $"F{n}"
+       let keycode = eval('"\<' .. kstr .. '>"')
+       call SendKeys([111+n])
+       let ch = getcharstr(0)
+       call assert_equal(keycode, $"{ch}", $"key = <{kstr}>")
+     endfor
+   endif
+ 
+   bw!
+ endfunc
+ 
+ "  Test MS-Windows console mouse events
+ func Test_mswin_mouse_event()
+   CheckMSWindows
+   new
+ 
+   set mousemodel=extend
+   call test_override('no_query_mouse', 1)
+   call WaitForResponses()
+ 
+   let msg = ''
+ 
+   call setline(1, ['one two three', 'four five six'])
+ 
+   " Test mouse movement
+   " by default, no mouse move events are generated
+   " this setting enables it to generate move events
+   set mousemev
+ 
+   if !has('gui_running')
+     " console version needs a button pressed,
+     " otherwise it ignores mouse movements.
+     call MouseLeftClick(2, 3)
+   endif
+   call MSWinMouseEvent(0x700, 8, 13, 0, 0, 0)
+   if has('gui_running')
+     call feedkeys("\<Esc>", 'Lx!')
+   endif
+   let pos = getmousepos()
+   call assert_equal(8, pos.screenrow)
+   call assert_equal(13, pos.screencol)
+ 
+   if !has('gui_running')
+     call MouseLeftClick(2, 3)
+     call MSWinMouseEvent(0x700, 6, 4, 1, 0, 0)
+     let pos = getmousepos()
+     call assert_equal(6, pos.screenrow)
+     call assert_equal(4, pos.screencol)
+   endif
+ 
+   " test cells vs pixels
+   if has('gui_running')
+     let args = { }
+     let args.row = 9
+     let args.col = 7
+     let args.move = 1
+     let args.cell = 1
+     call test_mswin_event("mouse", args)
+     call feedkeys("\<Esc>", 'Lx!')
+     let pos = getmousepos()
+     call assert_equal(9, pos.screenrow)
+     call assert_equal(7, pos.screencol)
+ 
+     let args.cell = 0
+     call test_mswin_event("mouse", args)
+     call feedkeys("\<Esc>", 'Lx!')
+     let pos = getmousepos()
+     call assert_equal(1, pos.screenrow)
+     call assert_equal(1, pos.screencol)
+ 
+     unlet args
+   endif
+ 
+   " finish testing mouse movement
+   set mousemev&
+ 
+   " place the cursor using left click and release in normal mode
+   call MouseLeftClick(2, 4)
+   call MouseLeftRelease(2, 4)
+   if has('gui_running')
+     call feedkeys("\<Esc>", 'Lx!')
+   endif
+   call assert_equal([0, 2, 4, 0], getpos('.'))
+ 
+   " select and yank a word
+   let @" = ''
+   call MouseLeftClick(1, 9)
+   let args = #{button: 0, row: 1, col: 9, multiclick: 1, modifiers: 0}
+   call test_mswin_event('mouse', args)
+   call MouseLeftRelease(1, 9)
+   call feedkeys("y", 'Lx!')
+   call assert_equal('three', @")
+ 
+   " create visual selection using right click
+   let @" = ''
+ 
+   call MouseLeftClick(2 ,6)
+   call MouseLeftRelease(2, 6)
+   call MouseRightClick(2, 13)
+   call MouseRightRelease(2, 13)
+   call feedkeys("y", 'Lx!')
+   call assert_equal('five six', @")
+ 
+   " paste using middle mouse button
+   let @* = 'abc '
+   call feedkeys('""', 'Lx!')
+   call MouseMiddleClick(1, 9)
+   call MouseMiddleRelease(1, 9)
+   if has('gui_running')
+     call feedkeys("\<Esc>", 'Lx!')
+   endif
+   call assert_equal(['one two abc three', 'four five six'], getline(1, '$'))
+ 
+   " test mouse scrolling (aka touchpad scrolling.)
+   %d _
+   set scrolloff=0
+   call setline(1, range(1, 100))
+ 
+   " Scroll Down
+   call MouseWheelDown(2, 1)
+   call MouseWheelDown(2, 1)
+   call MouseWheelDown(2, 1)
+   call feedkeys("H", 'Lx!')
+   call assert_equal(10, line('.'))
+ 
+   " Scroll Up
+   call MouseWheelUp(2, 1)
+   call MouseWheelUp(2, 1)
+   call feedkeys("H", 'Lx!')
+   call assert_equal(4, line('.'))
+ 
+   " Shift Scroll Down
+   call MouseShiftWheelDown(2, 1)
+   call feedkeys("H", 'Lx!')
+   " should scroll from where it is (4) + visible buffer height - cmdheight
+   let shift_scroll_height = line('w$') - line('w0') - &cmdheight 
+   call assert_equal(4 + shift_scroll_height, line('.'))
+ 
+   " Shift Scroll Up
+   call MouseShiftWheelUp(2, 1)
+   call feedkeys("H", 'Lx!')
+   call assert_equal(4, line('.'))
+ 
+   if !has('gui_running')
+     " Shift Scroll Down (using MOD)
+     call MSWinMouseEvent(0x100, 2, 1, 0, 0, 0x04)
+     call feedkeys("H", 'Lx!')
+     " should scroll from where it is (4) + visible buffer height - cmdheight
+     let shift_scroll_height = line('w$') - line('w0') - &cmdheight 
+     call assert_equal(4 + shift_scroll_height, line('.'))
+ 
+     " Shift Scroll Up (using MOD)
+     call MSWinMouseEvent(0x200, 2, 1, 0, 0, 0x04)
+     call feedkeys("H", 'Lx!')
+     call assert_equal(4, line('.'))
+   endif
+ 
+   set scrolloff&
+ 
+   %d _
+   set nowrap
+   " make the buffer 500 wide.
+   call setline(1, range(10)->join('')->repeat(50))
+   " Scroll Right
+   call MouseWheelRight(1, 5)
+   call MouseWheelRight(1, 10)
+   call MouseWheelRight(1, 15)
+   call feedkeys('g0', 'Lx!')
+   call assert_equal(19, col('.'))
+ 
+   " Scroll Left
+   call MouseWheelLeft(1, 15)
+   call MouseWheelLeft(1, 10)
+   call feedkeys('g0', 'Lx!')
+   call assert_equal(7, col('.'))
+ 
+   " Shift Scroll Right
+   call MouseShiftWheelRight(1, 10)
+   call feedkeys('g0', 'Lx!')
+   " should scroll from where it is (7) + window width
+   call assert_equal(7 + winwidth(0), col('.'))
+  
+   " Shift Scroll Left
+   call MouseShiftWheelLeft(1, 50)
+   call feedkeys('g0', 'Lx!')
+   call assert_equal(7, col('.'))
+   set wrap&
+ 
+   %d _
+   call setline(1, repeat([repeat('a', 60)], 10))
+ 
+   " record various mouse events
+   let mouseEventNames = [
+         \ 'LeftMouse', 'LeftRelease', '2-LeftMouse', '3-LeftMouse',
+         \ 'S-LeftMouse', 'A-LeftMouse', 'C-LeftMouse', 'MiddleMouse',
+         \ 'MiddleRelease', '2-MiddleMouse', '3-MiddleMouse',
+         \ 'S-MiddleMouse', 'A-MiddleMouse', 'C-MiddleMouse',
+         \ 'RightMouse', 'RightRelease', '2-RightMouse',
+         \ '3-RightMouse', 'S-RightMouse', 'A-RightMouse', 'C-RightMouse',
+         \ ]
+   let mouseEventCodes = map(copy(mouseEventNames), "'<' .. v:val .. '>'")
+   let g:events = []
+   for e in mouseEventCodes
+     exe 'nnoremap ' .. e .. ' <Cmd>call add(g:events, "' ..
+           \ substitute(e, '[<>]', '', 'g') .. '")<CR>'
+   endfor
+ 
+   " Test various mouse buttons 
+   "(0 - Left, 1 - Middle, 2 - Right, 
+   " 0x300 - MOUSE_X1/FROM_LEFT_3RD_BUTTON,
+   " 0x400 - MOUSE_X2/FROM_LEFT_4TH_BUTTON)
+   for button in [0, 1, 2, 0x300, 0x400]
+     " Single click
+     let args = #{button: button, row: 2, col: 5, multiclick: 0, modifiers: 0}
+     call test_mswin_event('mouse', args)
+     let args.button = 3
+     call test_mswin_event('mouse', args)
+ 
+     " Double Click
+     let args.button = button
+     call test_mswin_event('mouse', args)
+     let args.multiclick = 1
+     call test_mswin_event('mouse', args)
+     let args.button = 3
+     let args.multiclick = 0
+     call test_mswin_event('mouse', args)
+ 
+     " Triple Click
+     let args.button = button
+     call test_mswin_event('mouse', args)
+     let args.multiclick = 1
+     call test_mswin_event('mouse', args)
+     call test_mswin_event('mouse', args)
+     let args.button = 3
+     let args.multiclick = 0
+     call test_mswin_event('mouse', args)
+ 
+     " Shift click
+     let args = #{button: button, row: 3, col: 7, multiclick: 0, modifiers: 4}
+     call test_mswin_event('mouse', args)
+     let args.button = 3
+     call test_mswin_event('mouse', args)
+ 
+     " Alt click
+     let args.button = button
+     let args.modifiers = 8
+     call test_mswin_event('mouse', args)
+     let args.button = 3
+     call test_mswin_event('mouse', args)
+ 
+     " Ctrl click
+     let args.button = button
+     let args.modifiers = 16
+     call test_mswin_event('mouse', args)
+     let args.button = 3
+     call test_mswin_event('mouse', args)
+ 
+     call feedkeys("\<Esc>", 'Lx!')
+   endfor
+ 
+   if has('gui_running')
+     call assert_equal(['LeftMouse', 'LeftRelease', 'LeftMouse',
+       \ '2-LeftMouse', 'LeftMouse', '2-LeftMouse', '3-LeftMouse',
+       \ 'S-LeftMouse', 'A-LeftMouse', 'C-LeftMouse', 'MiddleMouse',
+       \ 'MiddleRelease', 'MiddleMouse', '2-MiddleMouse', 'MiddleMouse',
+       \ '2-MiddleMouse', '3-MiddleMouse', 'S-MiddleMouse', 'A-MiddleMouse',
+       \ 'C-MiddleMouse', 'RightMouse', 'RightRelease', 'RightMouse',
+       \ '2-RightMouse', 'RightMouse', '2-RightMouse', '3-RightMouse',
+       \ 'S-RightMouse', 'A-RightMouse', 'C-RightMouse'],
+       \ g:events)
+   else
+     call assert_equal(['MiddleRelease', 'LeftMouse', '2-LeftMouse',
+       \ '3-LeftMouse', 'S-LeftMouse', 'MiddleMouse', '2-MiddleMouse',
+       \ '3-MiddleMouse', 'MiddleMouse', 'S-MiddleMouse', 'RightMouse',
+       \ '2-RightMouse', '3-RightMouse'],
+       \ g:events)
+   endif
+ 
+   for e in mouseEventCodes
+     exe 'nunmap ' .. e
+   endfor
+ 
+   bw!
+   call test_override('no_query_mouse', 0)
+   set mousemodel&
+ endfunc
+ 
+ 
+ "  Test MS-Windows test_mswin_event error handling
+ func Test_mswin_event_error_handling()
+ 
+   let args = #{button: 0xfff, row: 2, col: 4, move: 0, multiclick: 0, 
modifiers: 0}
+   if !has('gui_running')
+     call assert_fails("call test_mswin_event('mouse', args)",'E475:')
+   endif
+   let args = #{button: 0, row: 2, col: 4, move: 0, multiclick: 0, modifiers: 
0}
+   call assert_fails("call test_mswin_event('a1b2c3', args)", 'E475:')
+   call assert_fails("call test_mswin_event(test_null_string(), {})", 'E475:')
+   
+   call assert_fails("call test_mswin_event([], args)", 'E1174:')
+   call assert_fails("call test_mswin_event('abc', [])", 'E1206:')
+   
+   call assert_false(test_mswin_event('mouse', test_null_dict()))
+   let args = #{row: 2, col: 4, multiclick: 0, modifiers: 0}
+   call assert_false(test_mswin_event('mouse', args))
+   let args = #{button: 0, col: 4, multiclick: 0, modifiers: 0}
+   call assert_false(test_mswin_event('mouse', args))
+   let args = #{button: 0, row: 2, multiclick: 0, modifiers: 0}
+   call assert_false(test_mswin_event('mouse', args))
+   let args = #{button: 0, row: 2, col: 4, modifiers: 0}
+   call assert_false(test_mswin_event('mouse', args))
+   let args = #{button: 0, row: 2, col: 4, multiclick: 0}
+   call assert_false(test_mswin_event('mouse', args))
+ 
+   call assert_false(test_mswin_event('key', test_null_dict()))
+   call assert_fails("call test_mswin_event('key', [])", 'E1206:')
+   call assert_fails("call test_mswin_event('key', {'event': 'keydown', 
'keycode': 0x0})", 'E1291:')
+   call assert_fails("call test_mswin_event('key', {'event': 'keydown', 
'keycode': [15]})", 'E745:')
+   call assert_fails("call test_mswin_event('key', {'event': 'keys', 
'keycode': 0x41})", 'E475:')
+   call assert_fails("call test_mswin_event('key', {'keycode': 0x41})", 
'E417:')
+   call assert_fails("call test_mswin_event('key', {'event': 'keydown'})", 
'E1291:')
+ 
+   call assert_fails("sandbox call test_mswin_event('key', {'event': 
'keydown', 'keycode': 61 })", 'E48:')
+ 
+   " flush out any garbage left in the buffer.
+   while getchar(0)
+   endwhile
+ endfunc
+ 
+ 
+ " vim: shiftwidth=2 sts=2 expandtab
*** ../vim-9.0.1083/src/testdir/test_termcodes.vim      2022-12-05 
13:50:49.722052362 +0000
--- src/testdir/test_termcodes.vim      2022-12-20 19:22:31.977188037 +0000
***************
*** 437,461 ****
      call assert_equal(1, line('w0'), msg)
      call assert_equal([0, 7, 1, 0], getpos('.'), msg)
  
!     if has('gui')
!       " Horizontal wheel scrolling currently only works when vim is
!       " compiled with gui enabled.
!       call MouseWheelRight(1, 1)
!       call assert_equal(7, 1 + virtcol(".") - wincol(), msg)
!       call assert_equal([0, 7, 7, 0], getpos('.'), msg)
! 
!       call MouseWheelRight(1, 1)
!       call assert_equal(13, 1 + virtcol(".") - wincol(), msg)
!       call assert_equal([0, 7, 13, 0], getpos('.'), msg)
! 
!       call MouseWheelLeft(1, 1)
!       call assert_equal(7, 1 + virtcol(".") - wincol(), msg)
!       call assert_equal([0, 7, 13, 0], getpos('.'), msg)
! 
!       call MouseWheelLeft(1, 1)
!       call assert_equal(1, 1 + virtcol(".") - wincol(), msg)
!       call assert_equal([0, 7, 13, 0], getpos('.'), msg)
!     endif
    endfor
  
    let &mouse = save_mouse
--- 437,458 ----
      call assert_equal(1, line('w0'), msg)
      call assert_equal([0, 7, 1, 0], getpos('.'), msg)
  
!     call MouseWheelRight(1, 1)
!     call assert_equal(7, 1 + virtcol(".") - wincol(), msg)
!     call assert_equal([0, 7, 7, 0], getpos('.'), msg)
! 
!     call MouseWheelRight(1, 1)
!     call assert_equal(13, 1 + virtcol(".") - wincol(), msg)
!     call assert_equal([0, 7, 13, 0], getpos('.'), msg)
! 
!     call MouseWheelLeft(1, 1)
!     call assert_equal(7, 1 + virtcol(".") - wincol(), msg)
!     call assert_equal([0, 7, 13, 0], getpos('.'), msg)
! 
!     call MouseWheelLeft(1, 1)
!     call assert_equal(1, 1 + virtcol(".") - wincol(), msg)
!     call assert_equal([0, 7, 13, 0], getpos('.'), msg)
! 
    endfor
  
    let &mouse = save_mouse
*** ../vim-9.0.1083/src/version.c       2022-12-20 13:38:18.284419451 +0000
--- src/version.c       2022-12-20 19:23:31.093219669 +0000
***************
*** 697,698 ****
--- 697,700 ----
  {   /* Add new patch number below this line */
+ /**/
+     1084,
  /**/

-- 
A poem:                read aloud:

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

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

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

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to vim_dev+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/20221220200232.322881C06DC%40moolenaar.net.

Raspunde prin e-mail lui