-
Notifications
You must be signed in to change notification settings - Fork 8.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
VkKeyScanW(0) is a constant during runtime independent of keyboard layout changes #8871
Comments
Yikes. Thanks for taking a look at all this! |
CauseThanks to the awesome work from the ReactOS authors the origin of this issue is easy to find... The kernel code looks somewhat like this (source): SHORT VkKeyScanExW(WCHAR ch, HKL dwhkl) {
return (SHORT)NtUserVkKeyScanEx(ch, dwhkl, TRUE);
}
SHORT VkKeyScanW(WCHAR ch) {
return (SHORT)NtUserVkKeyScanEx(ch, 0, FALSE);
}
DWORD NtUserVkKeyScanEx(WCHAR wch, HKL dwhkl, BOOL bUsehKL) {
PKL pKl = NULL;
if (bUsehKL) {
// Use given keyboard layout
if (dwhkl)
pKl = UserHklToKbl(dwhkl);
} else {
// Use thread keyboard layout
pKl = ((PTHREADINFO)PsGetCurrentThreadWin32Thread())->KeyboardLayout;
}
// ...
}
SolutionThis kinda works: WORD VkKeyScan(wchar_t ch) {
const auto tid = GetWindowThreadProcessId(GetForegroundWindow(), 0);
const auto hkl = GetKeyboardLayout(tid);
return LOWORD(VkKeyScanExW(ch, hkl));
} We could intercept |
Ha now you can forget about ReactOS and see how it's implemented in actual Windows 😉 @lhecker |
Came here to report this in a slightly different manner. Terminal.Gui app developers would like to be able to change the keyboard layout while the app is running. We use the Win32 API We've discovered that WT is always using the keyboard layout the process started with, ignoring Here's the repo I was going to post in a new issue. Hopefully it helps y'all fix this:
I've tried using If anyone has a workaround we can implement until this issue is fixed, I'd love to hear it! |
Workaround: #if !WT_ISSUE_8871_FIXED // https://github.com/microsoft/terminal/issues/8871
/// <summary>
/// Translates (maps) a virtual-key code into a scan code or character value, or translates a scan code into a virtual-key code.
/// </summary>
/// <param name="vk"></param>
/// <param name="uMapType">
/// If MAPVK_VK_TO_CHAR (2) - The uCode parameter is a virtual-key code and is translated into an unshifted
/// character value in the low order word of the return value.
/// </param>
/// <returns>An unshifted character value in the low order word of the return value. Dead keys (diacritics)
/// are indicated by setting the top bit of the return value. If there is no translation,
/// the function returns 0. See Remarks.</returns>
[DllImport ("user32.dll", EntryPoint = "MapVirtualKeyExW", CharSet = CharSet.Unicode)]
extern static uint MapVirtualKeyEx (VK vk, uint uMapType, IntPtr dwhkl);
/// <summary>
/// Retrieves the active input locale identifier (formerly called the keyboard layout).
/// </summary>
/// <param name="idThread">0 for current thread</param>
/// <returns>The return value is the input locale identifier for the thread.
/// The low word contains a Language Identifier for the input language
/// and the high word contains a device handle to the physical layout of the keyboard.
/// </returns>
[DllImport ("user32.dll", EntryPoint = "GetKeyboardLayout", CharSet = CharSet.Unicode)]
extern static IntPtr GetKeyboardLayout (IntPtr idThread);
//[DllImport ("user32.dll", EntryPoint = "GetKeyboardLayoutNameW", CharSet = CharSet.Unicode)]
//extern static uint GetKeyboardLayoutName (uint idThread);
[DllImport ("user32.dll")]
extern static IntPtr GetForegroundWindow ();
[DllImport ("user32.dll")]
extern static IntPtr GetWindowThreadProcessId (IntPtr hWnd, IntPtr ProcessId);
uint MapVKtoChar (VK vk)
{
var tid = GetWindowThreadProcessId (GetForegroundWindow (), 0);
var hkl = GetKeyboardLayout (tid);
return MapVirtualKeyEx (vk, 2, hkl);
}
#else
/// <summary>
/// Translates (maps) a virtual-key code into a scan code or character value, or translates a scan code into a virtual-key code.
/// </summary>
/// <param name="vk"></param>
/// <param name="uMapType">
/// If MAPVK_VK_TO_CHAR (2) - The uCode parameter is a virtual-key code and is translated into an unshifted
/// character value in the low order word of the return value.
/// </param>
/// <returns>An unshifted character value in the low order word of the return value. Dead keys (diacritics)
/// are indicated by setting the top bit of the return value. If there is no translation,
/// the function returns 0. See Remarks.</returns>
[DllImport ("user32.dll", EntryPoint = "MapVirtualKeyW", CharSet = CharSet.Unicode)]
extern static uint MapVirtualKey (VK vk, uint uMapType = 2);
uint MapVKtoChar (VK vk) => MapVirtualKeyToCharEx (vk);
#endif |
Environment
Steps to reproduce
Expected behavior
VkKeyScanW(0)
prints0x32
if the US and0x40
if the UK layout is selected. The value changes during runtime if the layout is changed.Actual behavior
VkKeyScanW(0)
will continue to return its initial value and not change if the keyboard layout is changed during runtime.MapVirtualKeyW
appears similarly affected. (I haven't tried to reproduce this yet though.)This has far reaching implications due to the widespread use of these functions in this code base and manifests itself in key combinations either not working at all, or producing incorrect VT sequences. The only way to fix the issue is by restarting the application.
The text was updated successfully, but these errors were encountered: