patch 9.0.1084: code handling low level MS-Windows events cannot be tested

Problem:    Code handling low level MS-Windows events cannot be tested.
Solution:   Add test_mswin_event() and tests using it. (Christopher Plewright,
            closes #11622)
diff --git a/src/testdir/Make_all.mak b/src/testdir/Make_all.mak
index 886bb08..fdca9f4 100644
--- a/src/testdir/Make_all.mak
+++ b/src/testdir/Make_all.mak
@@ -210,6 +210,7 @@
 	test_modeless \
 	test_modeline \
 	test_move \
+	test_mswin_event \
 	test_mzscheme \
 	test_nested_function \
 	test_netbeans \
@@ -454,6 +455,7 @@
 	test_mksession.res \
 	test_modeless.res \
 	test_modeline.res \
+	test_mswin_event.res \
 	test_mzscheme.res \
 	test_nested_function.res \
 	test_netbeans.res \
diff --git a/src/testdir/mouse.vim b/src/testdir/mouse.vim
index d59ad0e..e2979b7 100644
--- a/src/testdir/mouse.vim
+++ b/src/testdir/mouse.vim
@@ -20,6 +20,27 @@
   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 @@
     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 @@
 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 @@
 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 @@
 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 @@
 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 @@
 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 @@
 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 @@
 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 @@
 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 @@
 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 @@
 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 @@
 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 @@
 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 @@
 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 @@
 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 @@
 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
diff --git a/src/testdir/test_gui.vim b/src/testdir/test_gui.vim
index 3c7cfb9..0c1e0a5 100644
--- a/src/testdir/test_gui.vim
+++ b/src/testdir/test_gui.vim
@@ -1281,7 +1281,7 @@
     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 @@
 " 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
index 0000000..4e392a2
--- /dev/null
+++ b/src/testdir/test_mswin_event.vim
@@ -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
diff --git a/src/testdir/test_termcodes.vim b/src/testdir/test_termcodes.vim
index dd5280f..d3ab99c 100644
--- a/src/testdir/test_termcodes.vim
+++ b/src/testdir/test_termcodes.vim
@@ -437,25 +437,22 @@
     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(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 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(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 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