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)
This commit is contained in:
Christopher Plewright 2023-01-04 18:06:00 +00:00 committed by Bram Moolenaar
parent 3ac1d97a1d
commit c8b204952f
5 changed files with 115 additions and 71 deletions

View File

@ -1,5 +1,7 @@
version: "{build}"
image: Visual Studio 2015
skip_tags: true
environment:

View File

@ -29,12 +29,12 @@ To build the installable .exe:
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

View File

@ -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_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))
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_RECORD *ir)
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_RECORD *ir)
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);

View File

@ -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

View File

@ -695,6 +695,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
1146,
/**/
1145,
/**/