# HG changeset patch # User Bram Moolenaar # Date 1672856103 -3600 # Node ID 238f424acc6cc5f84f31686730f91d9a757c7f13 # Parent ad0edaf0f764ec3b33f4e2ecd9fff1389455787d patch 9.0.1146: MS-Windows: various special keys/modifiers are not mappable Commit: https://github.com/vim/vim/commit/c8b204952f0452fe1276d30ae6a5d38a3f421b95 Author: Christopher Plewright Date: Wed Jan 4 18:06:00 2023 +0000 patch 9.0.1146: MS-Windows: various special keys/modifiers are not mappable Problem: MS-Windows: various special keys and modifiers are not mappable. Solution: Adjust the handling of keys with modifiers. (Christian Plewright, closes #11768) diff --git a/.appveyor.yml b/.appveyor.yml --- a/.appveyor.yml +++ b/.appveyor.yml @@ -1,5 +1,7 @@ version: "{build}" +image: Visual Studio 2015 + skip_tags: true environment: diff --git a/nsis/README.txt b/nsis/README.txt --- a/nsis/README.txt +++ b/nsis/README.txt @@ -29,12 +29,12 @@ 3. Go to the GvimExt directory and buil 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, + "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 "vim82" +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 diff --git a/src/os_win32.c b/src/os_win32.c --- a/src/os_win32.c +++ b/src/os_win32.c @@ -1042,7 +1042,8 @@ win32_kbd_patch_key( return 1; } - if (pker->uChar.UnicodeChar > 0 && pker->uChar.UnicodeChar < 0xfffd) + // check if it already has a valid unicode character. + if (pker->uChar.UnicodeChar > 0 && pker->uChar.UnicodeChar < 0xFFFD) return 1; CLEAR_FIELD(abKeystate); @@ -1118,13 +1119,12 @@ decode_key_event( { if (VirtKeyMap[i].wVirtKey == pker->wVirtualKeyCode) { - if (nModifs == 0) - *pch = VirtKeyMap[i].chAlone; - else if ((nModifs & SHIFT) != 0 && (nModifs & ~SHIFT) == 0) + *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 && (nModifs & ~ALT) == 0) + else if ((nModifs & ALT) != 0) *pch = VirtKeyMap[i].chAlt; if (*pch != 0) @@ -1133,6 +1133,74 @@ decode_key_event( { *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,10 +1246,11 @@ encode_key_event(dict_T *args, INPUT_REC { static int s_dwMods = 0; - char_u *event = dict_get_string(args, "event", TRUE); - if (event && (STRICMP(event, "keydown") == 0 - || STRICMP(event, "keyup") == 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,7 +1261,7 @@ encode_key_event(dict_T *args, INPUT_REC ir->EventType = KEY_EVENT; KEY_EVENT_RECORD ker; ZeroMemory(&ker, sizeof(ker)); - ker.bKeyDown = STRICMP(event, "keydown") == 0; + ker.bKeyDown = isKeyDown; ker.wRepeatCount = 1; ker.wVirtualScanCode = 0; ker.dwControlKeyState = 0; @@ -1215,73 +1284,55 @@ encode_key_event(dict_T *args, INPUT_REC if (vkCode == VK_LSHIFT || vkCode == VK_RSHIFT || vkCode == VK_SHIFT) { - if (STRICMP(event, "keydown") == 0) + if (isKeyDown) s_dwMods |= SHIFT_PRESSED; else s_dwMods &= ~SHIFT_PRESSED; } else if (vkCode == VK_LCONTROL || vkCode == VK_CONTROL) { - if (STRICMP(event, "keydown") == 0) + if (isKeyDown) s_dwMods |= LEFT_CTRL_PRESSED; else s_dwMods &= ~LEFT_CTRL_PRESSED; } else if (vkCode == VK_RCONTROL) { - if (STRICMP(event, "keydown") == 0) + if (isKeyDown) s_dwMods |= RIGHT_CTRL_PRESSED; else s_dwMods &= ~RIGHT_CTRL_PRESSED; } else if (vkCode == VK_LMENU || vkCode == VK_MENU) { - if (STRICMP(event, "keydown") == 0) + if (isKeyDown) s_dwMods |= LEFT_ALT_PRESSED; else s_dwMods &= ~LEFT_ALT_PRESSED; } else if (vkCode == VK_RMENU) { - if (STRICMP(event, "keydown") == 0) + if (isKeyDown) 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 - + ker.uChar.UnicodeChar = 0xFFFD; // UNICODE REPLACEMENT CHARACTER ir->Event.KeyEvent = ker; - vim_free(event); + vim_free(action); } else { - if (event == NULL) + if (action == NULL) { semsg(_(e_missing_argument_str), "event"); } else { - semsg(_(e_invalid_value_for_argument_str_str), "event", event); - vim_free(event); + semsg(_(e_invalid_value_for_argument_str_str), "event", action); + vim_free(action); } return FALSE; } @@ -2432,6 +2483,8 @@ mch_inchar( 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); diff --git a/src/testdir/test_mswin_event.vim b/src/testdir/test_mswin_event.vim --- a/src/testdir/test_mswin_event.vim +++ b/src/testdir/test_mswin_event.vim @@ -3,7 +3,6 @@ source check.vim CheckMSWindows - source mouse.vim " Helper function for sending a grouped sequence of low level key presses @@ -54,7 +53,8 @@ func ExecuteBufferedKeys() 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,11 +296,9 @@ let s:VK = { \ [[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], \ ] -" 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], '±'], @@ -342,7 +340,7 @@ let s:test_extra_key_chars = [ \ ] func s:LoopTestKeyArray(arr) -" flush out any garbage left in the buffer + " flush out anything in the typeahead buffer while getchar(0) endwhile @@ -351,7 +349,7 @@ func s:LoopTestKeyArray(arr) 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 + 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,7 +387,7 @@ func s:LoopTestKeyArray(arr) call assert_equal(0, mod_mask, $"key = {kstr}") endfor - " flush out any garbage left in the buffer + " flush out anything in the typeahead buffer while getchar(0) endwhile @@ -489,29 +487,23 @@ func Test_mswin_key_event() endfor endif - " Windows intercepts some of these keys in the GUI + " 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") - " 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 .. '>"') + " 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}") + 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,14 +511,12 @@ func Test_mswin_key_event() 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}") + 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], @@ -586,14 +576,13 @@ func Test_mswin_key_event() \ [[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}") + call assert_equal(keycode, ch, $"key = {kstr}") + call assert_equal(kmod, mod, $"mod = {kmod} key = {kstr}") endfor bw! @@ -634,8 +623,6 @@ func Test_QWERTY_Ctrl_minus() call ExecuteBufferedKeys() call assert_equal('BILBO', getline('$')) - - imapclear bw! endfunc @@ -953,7 +940,7 @@ func Test_mswin_event_error_handling() call assert_fails("sandbox call test_mswin_event('key', {'event': 'keydown', 'keycode': 61 })", 'E48:') - " flush out any garbage left in the buffer. + " flush out anything in the typeahead buffer while getchar(0) endwhile endfunc diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -696,6 +696,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1146, +/**/ 1145, /**/ 1144,