Patch 9.0.1146
Problem:    MS-Windows: various special keys and modifiers are not mappable.
Solution:   Adjust the handling of keys with modifiers. (Christian Plewright,
            closes #11768)
Files:      .appveyor.yml, nsis/README.txt, src/os_win32.c,
            src/testdir/test_mswin_event.vim


*** ../vim-9.0.1145/.appveyor.yml       2022-12-30 17:28:08.409002765 +0000
--- .appveyor.yml       2023-01-04 17:50:22.581409157 +0000
***************
*** 1,5 ****
--- 1,7 ----
  version: "{build}"
  
+ image: Visual Studio 2015
+ 
  skip_tags: true
  
  environment:
*** ../vim-9.0.1145/nsis/README.txt     2022-10-08 17:12:16.735701562 +0100
--- nsis/README.txt     2023-01-04 17:50:22.581409157 +0000
***************
*** 29,40 ****
  
  4.  Get a "diff.exe" program.  If you skip this the built-in diff will always
      be used (which is fine for most users).  If you do have your own
!     "diff.exe" put it in the "../.." directory (above the "vim82" directory,
      it's the same for all Vim versions).
      You can find one in previous Vim versions or in this archive:
                http://www.mossbayeng.com/~ron/vim/diffutils.tar.gz
  
! 5   Also put winpty32.dll and winpty-agent.exe in "../.." (above the "vim82"
      directory).  This is required for the terminal window.
  
  6.  Do "make uganda.nsis.txt" in runtime/doc.  This requires sed, you may have
--- 29,40 ----
  
  4.  Get a "diff.exe" program.  If you skip this the built-in diff will always
      be used (which is fine for most users).  If you do have your own
!     "diff.exe" put it in the "../.." directory (above the "vim90" directory,
      it's the same for all Vim versions).
      You can find one in previous Vim versions or in this archive:
                http://www.mossbayeng.com/~ron/vim/diffutils.tar.gz
  
! 5   Also put winpty32.dll and winpty-agent.exe in "../.." (above the "vim90"
      directory).  This is required for the terminal window.
  
  6.  Do "make uganda.nsis.txt" in runtime/doc.  This requires sed, you may have
*** ../vim-9.0.1145/src/os_win32.c      2022-12-30 16:54:53.452987924 +0000
--- src/os_win32.c      2023-01-04 18:02:16.868357513 +0000
***************
*** 1042,1048 ****
        return 1;
      }
  
!     if (pker->uChar.UnicodeChar > 0 && pker->uChar.UnicodeChar < 0xfffd)
        return 1;
  
      CLEAR_FIELD(abKeystate);
--- 1042,1049 ----
        return 1;
      }
  
!     // check if it already has a valid unicode character.
!     if (pker->uChar.UnicodeChar > 0 && pker->uChar.UnicodeChar < 0xFFFD)
        return 1;
  
      CLEAR_FIELD(abKeystate);
***************
*** 1118,1130 ****
      {
        if (VirtKeyMap[i].wVirtKey == pker->wVirtualKeyCode)
        {
!           if (nModifs == 0)
!               *pch = VirtKeyMap[i].chAlone;
!           else if ((nModifs & SHIFT) != 0 && (nModifs & ~SHIFT) == 0)
                *pch = VirtKeyMap[i].chShift;
            else if ((nModifs & CTRL) != 0 && (nModifs & ~CTRL) == 0)
                *pch = VirtKeyMap[i].chCtrl;
!           else if ((nModifs & ALT) != 0 && (nModifs & ~ALT) == 0)
                *pch = VirtKeyMap[i].chAlt;
  
            if (*pch != 0)
--- 1119,1130 ----
      {
        if (VirtKeyMap[i].wVirtKey == pker->wVirtualKeyCode)
        {
!           *pch = VirtKeyMap[i].chAlone;
!           if ((nModifs & SHIFT) != 0)
                *pch = VirtKeyMap[i].chShift;
            else if ((nModifs & CTRL) != 0 && (nModifs & ~CTRL) == 0)
                *pch = VirtKeyMap[i].chCtrl;
!           else if ((nModifs & ALT) != 0)
                *pch = VirtKeyMap[i].chAlt;
  
            if (*pch != 0)
***************
*** 1133,1138 ****
--- 1133,1206 ----
                {
                    *pch2 = *pch;
                    *pch = K_NUL;
+                   if (pmodifiers)
+                   {
+                       if (pker->wVirtualKeyCode >= VK_F1
+                           && pker->wVirtualKeyCode <= VK_F12)
+                       {
+                           if ((nModifs & ALT) != 0)
+                           {
+                               *pmodifiers |= MOD_MASK_ALT;
+                               if ((nModifs & SHIFT) == 0)
+                                   *pch2 = VirtKeyMap[i].chAlone;
+                           }
+                           if ((nModifs & CTRL) != 0)
+                           {
+                               *pmodifiers |= MOD_MASK_CTRL;
+                               if ((nModifs & SHIFT) == 0)
+                                   *pch2 = VirtKeyMap[i].chAlone;
+                           }
+                       }
+                       else if (pker->wVirtualKeyCode >= VK_END
+                               && pker->wVirtualKeyCode <= VK_DOWN)
+                       {
+                           // VK_END   0x23
+                           // VK_HOME  0x24
+                           // VK_LEFT  0x25
+                           // VK_UP    0x26
+                           // VK_RIGHT 0x27
+                           // VK_DOWN  0x28
+                           *pmodifiers = 0;
+                           *pch2 = VirtKeyMap[i].chAlone;
+                           if ((nModifs & SHIFT) != 0
+                                                   && (nModifs & ~SHIFT) == 0)
+                           {
+                               *pch2 = VirtKeyMap[i].chShift;
+                           }
+                           else if ((nModifs & CTRL) != 0
+                                                    && (nModifs & ~CTRL) == 0)
+                           {
+                               *pch2 = VirtKeyMap[i].chCtrl;
+                               if (pker->wVirtualKeyCode == VK_UP
+                                   || pker->wVirtualKeyCode == VK_DOWN)
+                               {
+                                   *pmodifiers |= MOD_MASK_CTRL;
+                                   *pch2 = VirtKeyMap[i].chAlone;
+                               }
+                           }
+                           else if ((nModifs & ALT) != 0
+                                                     && (nModifs & ~ALT) == 0)
+                           {
+                               *pch2 = VirtKeyMap[i].chAlt;
+                           }
+                           else if ((nModifs & SHIFT) != 0
+                                                     && (nModifs & CTRL) != 0)
+                           {
+                               *pmodifiers |= MOD_MASK_CTRL;
+                               *pch2 = VirtKeyMap[i].chShift;
+                           }
+                       }
+                       else
+                       {
+                           *pch2 = VirtKeyMap[i].chAlone;
+                           if ((nModifs & SHIFT) != 0)
+                               *pmodifiers |= MOD_MASK_SHIFT;
+                           if ((nModifs & CTRL) != 0)
+                               *pmodifiers |= MOD_MASK_CTRL;
+                           if ((nModifs & ALT) != 0)
+                               *pmodifiers |= MOD_MASK_ALT;
+                       }
+                   }
                }
  
                return TRUE;
***************
*** 1178,1187 ****
  {
      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)
        {
--- 1246,1256 ----
  {
      static int s_dwMods = 0;
  
!     char_u *action = dict_get_string(args, "event", TRUE);
!     if (action && (STRICMP(action, "keydown") == 0
!                                       || STRICMP(action, "keyup") == 0))
      {
+       BOOL isKeyDown = STRICMP(action, "keydown") == 0;
        WORD vkCode = dict_get_number_def(args, "keycode", 0);
        if (vkCode <= 0 || vkCode >= 0xFF)
        {
***************
*** 1192,1198 ****
        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;
--- 1261,1267 ----
        ir->EventType = KEY_EVENT;
        KEY_EVENT_RECORD ker;
        ZeroMemory(&ker, sizeof(ker));
!       ker.bKeyDown = isKeyDown;
        ker.wRepeatCount = 1;
        ker.wVirtualScanCode = 0;
        ker.dwControlKeyState = 0;
***************
*** 1215,1287 ****
  
        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; --i)
!       {
!           if (VirtKeyMap[i].wVirtKey == vkCode)
!           {
!               ker.uChar.UnicodeChar = 0xfffd;  // REPLACEMENT CHARACTER
!               break;
!           }
!       }
! 
!       // The following are treated specially in Vim.
!       // Ctrl-6 is Ctrl-^
!       // Ctrl-2 is Ctrl-@
!       // Ctrl-- is Ctrl-_
!       if ((vkCode == 0xBD || vkCode == '2' || vkCode == '6')
!                                            && (ker.dwControlKeyState & CTRL))
!           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;
      }
--- 1284,1338 ----
  
        if (vkCode == VK_LSHIFT || vkCode == VK_RSHIFT || vkCode == VK_SHIFT)
        {
!           if (isKeyDown)
                s_dwMods |= SHIFT_PRESSED;
            else
                s_dwMods &= ~SHIFT_PRESSED;
        }
        else if (vkCode == VK_LCONTROL || vkCode == VK_CONTROL)
        {
!           if (isKeyDown)
                s_dwMods |= LEFT_CTRL_PRESSED;
            else
                s_dwMods &= ~LEFT_CTRL_PRESSED;
        }
        else if (vkCode == VK_RCONTROL)
        {
!           if (isKeyDown)
                s_dwMods |= RIGHT_CTRL_PRESSED;
            else
                s_dwMods &= ~RIGHT_CTRL_PRESSED;
        }
        else if (vkCode == VK_LMENU || vkCode == VK_MENU)
        {
!           if (isKeyDown)
                s_dwMods |= LEFT_ALT_PRESSED;
            else
                s_dwMods &= ~LEFT_ALT_PRESSED;
        }
        else if (vkCode == VK_RMENU)
        {
!           if (isKeyDown)
                s_dwMods |= RIGHT_ALT_PRESSED;
            else
                s_dwMods &= ~RIGHT_ALT_PRESSED;
        }
        ker.dwControlKeyState |= s_dwMods;
        ker.wVirtualKeyCode = vkCode;
!       ker.uChar.UnicodeChar = 0xFFFD;  // UNICODE REPLACEMENT CHARACTER
        ir->Event.KeyEvent = ker;
!       vim_free(action);
      }
      else
      {
!       if (action == NULL)
        {
            semsg(_(e_missing_argument_str), "event");
        }
        else
        {
!           semsg(_(e_invalid_value_for_argument_str_str), "event", action);
!           vim_free(action);
        }
        return FALSE;
      }
***************
*** 2432,2437 ****
--- 2483,2490 ----
  
            c = tgetch(&modifiers, &ch2);
  
+           c = simplify_key(c, &modifiers);
+ 
            // Some chars need adjustment when the Ctrl modifier is used.
            ++no_reduce_keys;
            c = may_adjust_key_for_ctrl(modifiers, c);
*** ../vim-9.0.1145/src/testdir/test_mswin_event.vim    2022-12-30 
16:54:53.456987927 +0000
--- src/testdir/test_mswin_event.vim    2023-01-04 18:03:22.788504616 +0000
***************
*** 3,9 ****
  
  source check.vim
  CheckMSWindows
- 
  source mouse.vim
  
  " Helper function for sending a grouped sequence of low level key presses
--- 3,8 ----
***************
*** 54,60 ****
    endif
  endfunc
  
! 
  let s:VK = {
      \ 'ENTER'      : 0x0D,
      \ 'SPACE'      : 0x20,
--- 53,60 ----
    endif
  endfunc
  
! " Refer to the following page for the virtual key codes:
! " https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes
  let s:VK = {
      \ 'ENTER'      : 0x0D,
      \ 'SPACE'      : 0x20,
***************
*** 296,306 ****
      \ [[s:VK.CONTROL, s:VK.OEM_4], 0x1B],
      \ [[s:VK.CONTROL, s:VK.OEM_5], 0x1C],
      \ [[s:VK.CONTROL, s:VK.OEM_6], 0x1D],
      \ ]
- " The following non-printable ascii chars fail in the GUI, but work in the 
- " console. 0x1e [^^] Record separator (RS), and 0x1f [^_] Unit separator (US)
- "      \ [[s:VK.CONTROL, s:VK.SHIFT, s:VK.KEY_6], 0x1E],
- "      \ [[s:VK.CONTROL, s:VK.SHIFT, s:VK.OEM_MINUS], 0x1F],
  
  let s:test_extra_key_chars = [
      \ [[s:VK.ALT, s:VK.KEY_1], '±'],
--- 296,304 ----
      \ [[s:VK.CONTROL, s:VK.OEM_4], 0x1B],
      \ [[s:VK.CONTROL, s:VK.OEM_5], 0x1C],
      \ [[s:VK.CONTROL, s:VK.OEM_6], 0x1D],
+     \ [[s:VK.CONTROL, s:VK.KEY_6], 0x1E],
+     \ [[s:VK.CONTROL, s:VK.OEM_MINUS], 0x1F],
      \ ]
  
  let s:test_extra_key_chars = [
      \ [[s:VK.ALT, s:VK.KEY_1], '±'],
***************
*** 342,348 ****
      \ ]
  
  func s:LoopTestKeyArray(arr)
! " flush out any garbage left in the buffer
    while getchar(0)
    endwhile
  
--- 340,346 ----
      \ ]
  
  func s:LoopTestKeyArray(arr)
!   " flush out anything in the typeahead buffer
    while getchar(0)
    endwhile
  
***************
*** 351,357 ****
      call SendKeyGroup(kcodes)
      let ch = getcharstr(0)
      " need to deal a bit differently with the non-printable ascii chars < 0x20
!     if kstr < 0x20 && index([s:VK.CONTROL, s:VK.LCONTROL, s:VK.RCONTROL],  
kcodes[0]) >= 0
        call assert_equal(nr2char(kstr), $"{ch}")
      else
        call assert_equal(kstr, $"{ch}")
--- 349,355 ----
      call SendKeyGroup(kcodes)
      let ch = getcharstr(0)
      " need to deal a bit differently with the non-printable ascii chars < 0x20
!     if kstr < 0x20 && index([s:VK.CONTROL, s:VK.LCONTROL, s:VK.RCONTROL], 
kcodes[0]) >= 0
        call assert_equal(nr2char(kstr), $"{ch}")
      else
        call assert_equal(kstr, $"{ch}")
***************
*** 389,395 ****
      call assert_equal(0, mod_mask, $"key = {kstr}")
    endfor
  
!   " flush out any garbage left in the buffer
    while getchar(0)
    endwhile
  
--- 387,393 ----
      call assert_equal(0, mod_mask, $"key = {kstr}")
    endfor
  
!   " flush out anything in the typeahead buffer
    while getchar(0)
    endwhile
  
***************
*** 489,517 ****
      endfor
    endif
  
!   " Windows intercepts some of these keys in the GUI
    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 SendKey(111+n)
-       let ch = getcharstr(0)
-       call assert_equal(keycode, $"{ch}", $"key = <{kstr}>")
-     endfor
-     "  NOTE: mod + Fn Keys not working in CI Testing!?
-     " Test for Function Keys 'F1' to 'F12'
-     " VK codes 112(0x70) - 123(0x7B)
-     " With ALL permutatios of modifiers; Shift, Ctrl & Alt
      for [mod_str, vim_mod_mask, mod_keycodes] in s:vim_key_modifiers
        for n in range(1, 12)
          let kstr = $"{mod_str}F{n}"
          let keycode = eval('"\<' .. kstr .. '>"')
          " call SendKeyGroup(mod_keycodes + [111+n])
          call SendKeyWithModifiers(111+n, vim_mod_mask)
          let ch = getcharstr(0)
          let mod_mask = getcharmod()
!         """"""  call assert_equal(keycode, $"{ch}", $"key = {kstr}")
          " workaround for the virtual termcap maps changing the character 
instead
          " of sending Shift
          for mod_key in mod_keycodes
--- 487,509 ----
      endfor
    endif
  
!   " Test for Function Keys 'F1' to 'F12'
!   " VK codes 112(0x70) - 123(0x7B)
!   " Also with ALL permutatios of modifiers; Shift, Ctrl & Alt
!   " NOTE: Windows intercepts some of these keys in the GUI
    if !has("gui_running")
      for [mod_str, vim_mod_mask, mod_keycodes] in s:vim_key_modifiers
        for n in range(1, 12)
          let kstr = $"{mod_str}F{n}"
          let keycode = eval('"\<' .. kstr .. '>"')
+         " flush out anything in the typeahead buffer
+         while getchar(0)
+         endwhile
          " call SendKeyGroup(mod_keycodes + [111+n])
          call SendKeyWithModifiers(111+n, vim_mod_mask)
          let ch = getcharstr(0)
          let mod_mask = getcharmod()
!         call assert_equal(keycode, $"{ch}", $"key = {kstr}")
          " workaround for the virtual termcap maps changing the character 
instead
          " of sending Shift
          for mod_key in mod_keycodes
***************
*** 519,532 ****
              let mod_mask = mod_mask + s:vim_MOD_MASK_SHIFT
            endif
          endfor
!         """"""call assert_equal(vim_mod_mask, mod_mask, $"mod = 
{vim_mod_mask} for key = {kstr}")
        endfor
      endfor
    endif
  
    " Test for the various Ctrl and Shift key combinations.
-   " Refer to the following page for the virtual key codes:
-   " https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes
    let keytests = [
      \ [[s:VK.SHIFT,    s:VK.PRIOR], "S-Pageup", 2],
      \ [[s:VK.LSHIFT,   s:VK.PRIOR], "S-Pageup", 2],
--- 511,522 ----
              let mod_mask = mod_mask + s:vim_MOD_MASK_SHIFT
            endif
          endfor
!         call assert_equal(vim_mod_mask, mod_mask, $"mod = {vim_mod_mask} for 
key = {kstr}")
        endfor
      endfor
    endif
  
    " Test for the various Ctrl and Shift key combinations.
    let keytests = [
      \ [[s:VK.SHIFT,    s:VK.PRIOR], "S-Pageup", 2],
      \ [[s:VK.LSHIFT,   s:VK.PRIOR], "S-Pageup", 2],
***************
*** 586,599 ****
      \ [[s:VK.CONTROL,  s:VK.OEM_MINUS], "C-_", 0]
      \ ]
  
-   " Not working in CI Testing yet!?
    for [kcodes, kstr, kmod] in keytests
      call SendKeyGroup(kcodes)
      let ch = getcharstr(0)
      let mod = getcharmod()
      let keycode = eval('"\<' .. kstr .. '>"')
! "      call assert_equal(keycode, ch, $"key = {kstr}")
! "      call assert_equal(kmod, mod, $"mod = {kmod} key = {kstr}")
    endfor
  
    bw!
--- 576,588 ----
      \ [[s:VK.CONTROL,  s:VK.OEM_MINUS], "C-_", 0]
      \ ]
  
    for [kcodes, kstr, kmod] in keytests
      call SendKeyGroup(kcodes)
      let ch = getcharstr(0)
      let mod = getcharmod()
      let keycode = eval('"\<' .. kstr .. '>"')
!     call assert_equal(keycode, ch, $"key = {kstr}")
!     call assert_equal(kmod, mod, $"mod = {kmod} key = {kstr}")
    endfor
  
    bw!
***************
*** 634,641 ****
    call ExecuteBufferedKeys()
    call assert_equal('BILBO', getline('$'))
  
- 
- 
    imapclear
    bw!
  endfunc
--- 623,628 ----
***************
*** 953,959 ****
  
    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
--- 940,946 ----
  
    call assert_fails("sandbox call test_mswin_event('key', {'event': 
'keydown', 'keycode': 61 })", 'E48:')
  
!   " flush out anything in the typeahead buffer
    while getchar(0)
    endwhile
  endfunc
*** ../vim-9.0.1145/src/version.c       2023-01-04 17:17:49.121005598 +0000
--- src/version.c       2023-01-04 17:50:52.893615244 +0000
***************
*** 697,698 ****
--- 697,700 ----
  {   /* Add new patch number below this line */
+ /**/
+     1146,
  /**/

-- 
SOLDIER: Where did you get the coconuts?
ARTHUR:  Through ... We found them.
SOLDIER: Found them?  In Mercea.  The coconut's tropical!
                 "Monty Python and the Holy Grail" PYTHON (MONTY) PICTURES LTD

 /// Bram Moolenaar -- [email protected] -- 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 [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/20230104180637.695491C0865%40moolenaar.net.

Raspunde prin e-mail lui