mirror of
				https://github.com/vim/vim.git
				synced 2025-10-30 09:47:20 -04:00 
			
		
		
		
	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:
		
				
					committed by
					
						 Bram Moolenaar
						Bram Moolenaar
					
				
			
			
				
	
			
			
			
						parent
						
							3ac1d97a1d
						
					
				
				
					commit
					c8b204952f
				
			| @@ -1,5 +1,7 @@ | ||||
| version: "{build}" | ||||
|  | ||||
| image: Visual Studio 2015 | ||||
|  | ||||
| skip_tags: true | ||||
|  | ||||
| environment: | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
							
								
								
									
										127
									
								
								src/os_win32.c
									
									
									
									
									
								
							
							
						
						
									
										127
									
								
								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_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); | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -695,6 +695,8 @@ static char *(features[]) = | ||||
|  | ||||
| static int included_patches[] = | ||||
| {   /* Add new patch number below this line */ | ||||
| /**/ | ||||
|     1146, | ||||
| /**/ | ||||
|     1145, | ||||
| /**/ | ||||
|   | ||||
		Reference in New Issue
	
	Block a user