mirror of
https://github.com/keymanapp/keyman.git
synced 2026-10-07 22:47:29 +00:00
Merge pull request #1621 from keymanapp/windows-serialization-of-altgr-1620
[Windows] AltGr causes sticky Left Control when some Windows European layouts are installed.
This commit is contained in:
commit
43eff6410c
2 changed files with 83 additions and 14 deletions
|
|
@ -33,6 +33,7 @@
|
|||
<map value="0x4" message="$(string.Map.LALT)"/>
|
||||
<map value="0x5" message="$(string.Map.LCTRL_LALT)"/>
|
||||
<map value="0x8" message="$(string.Map.RALT)"/>
|
||||
<map value="0x9" message="$(string.Map.LCTRL_RALT)"/> <!-- Because Windows simulates this for AltGr in European keyboards -->
|
||||
<map value="0xA" message="$(string.Map.RCTRL_RALT)"/>
|
||||
|
||||
<map value="0x10" message="$(string.Map.SHIFT)"/>
|
||||
|
|
@ -210,6 +211,7 @@
|
|||
<string id="Map.RCTRL" value="RCTRL" />
|
||||
<string id="Map.LALT" value="LALT" />
|
||||
<string id="Map.LCTRL_LALT" value="LCTRL+LALT" />
|
||||
<string id="Map.LCTRL_RALT" value="LCTRL+RALT" />
|
||||
<string id="Map.RALT" value="RALT" />
|
||||
<string id="Map.RCTRL_RALT" value="RCTRL+RALT" />
|
||||
|
||||
|
|
|
|||
|
|
@ -415,23 +415,90 @@ private:
|
|||
*/
|
||||
|
||||
if (msg == wm_keyman_keyevent && flag_ShouldSerializeInput /*&& _td->lpActiveKeyboard*/) {
|
||||
INPUT input;
|
||||
input.type = INPUT_KEYBOARD;
|
||||
input.ki.wVk = (WORD)wParam;
|
||||
input.ki.wScan = (lParam & 0xFF0000) >> 16;
|
||||
input.ki.time = GetMessageTime();
|
||||
input.ki.dwExtraInfo = EXTRAINFO_FLAG_SERIALIZED_USER_KEY_EVENT;
|
||||
input.ki.dwFlags = lParam & 0xFFFF;
|
||||
|
||||
if (!SendInput(1, &input, sizeof(INPUT))) {
|
||||
DebugLastError("SendInput");
|
||||
if (wParam == VK_RMENU && (lParam & (KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP)) == (KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP) && GetKeyState(VK_LCONTROL) < 0) {
|
||||
/*
|
||||
When Windows has a European layout that uses AltGr installed, it can emit an additional LCtrl down via software
|
||||
when RAlt is pressed. However, the corresponding LCtrl up is never received, seemingly because when Keyman
|
||||
re-emits the LCtrl+RAlt, there are subtle differences in the event flags which we cannot duplicate -- specifically
|
||||
the flag that emits WM_SYSKEYDOWN for the VK_LCONTROL, even though it is received before the VK_RALT event. It
|
||||
appears that Windows figures this out by giving this VK_LCONTROL the scan code 0x21D instead of 0x1D. But we
|
||||
are unable to emit that scan code: Windows truncates the scan code sent through SendInput so that we can only
|
||||
send 0x1D.
|
||||
|
||||
So we simulate the release of the Left Control key ourselves when the release of the Right Alt is received,
|
||||
using VK_CONTROL and scan 0x1D, and hope for the best.
|
||||
|
||||
The full Windows sequence is:
|
||||
|
||||
WM_SYSKEYDOWN VK_CONTROL 0x21D
|
||||
WM_SYSKEYDOWN VK_MENU 0x38 EXTENDED_BIT
|
||||
...
|
||||
WM_KEYUP VK_CONTROL 0x21D
|
||||
WM_KEYUP VK_MENU 0x38 EXTENDED_BIT
|
||||
|
||||
The best Keyman can do is:
|
||||
|
||||
WM_KEYDOWN VK_CONTROL 0x1D
|
||||
WM_KEYDOWN VK_MENU 0x38 EXTENDED_BIT
|
||||
...
|
||||
WM_KEYUP VK_CONTROL 0x1D
|
||||
WM_KEYUP VK_MENU 0x38 EXTENDED_BIT
|
||||
|
||||
There is a possibility that some apps may try and sniff that 0x21D scan code and get confused because Keyman
|
||||
doesn't emit it. Hopefully this is rare.
|
||||
*/
|
||||
INPUT input[2];
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = VK_CONTROL;
|
||||
input[0].ki.wScan = 0x21D; // Yeah, Windows chops this to 0x1D. Such is life.
|
||||
input[0].ki.time = GetMessageTime();
|
||||
input[0].ki.dwExtraInfo = EXTRAINFO_FLAG_SERIALIZED_USER_KEY_EVENT;
|
||||
input[0].ki.dwFlags = KEYEVENTF_KEYUP;
|
||||
|
||||
input[1].type = INPUT_KEYBOARD;
|
||||
input[1].ki.wVk = (WORD)wParam;
|
||||
input[1].ki.wScan = (lParam & 0xFFF0000) >> 16;
|
||||
input[1].ki.time = GetMessageTime();
|
||||
input[1].ki.dwExtraInfo = EXTRAINFO_FLAG_SERIALIZED_USER_KEY_EVENT;
|
||||
input[1].ki.dwFlags = lParam & 0xFFFF;
|
||||
|
||||
if (!SendInput(2, input, sizeof(INPUT))) {
|
||||
DebugLastError("SendInput");
|
||||
}
|
||||
|
||||
UpdateLocalModifierState(
|
||||
(BYTE)input[0].ki.wVk,
|
||||
input[0].ki.dwFlags & KEYEVENTF_EXTENDEDKEY ? TRUE : FALSE,
|
||||
(BYTE)input[0].ki.wScan,
|
||||
input[0].ki.dwFlags & KEYEVENTF_KEYUP ? TRUE : FALSE);
|
||||
|
||||
UpdateLocalModifierState(
|
||||
(BYTE)input[1].ki.wVk,
|
||||
input[1].ki.dwFlags & KEYEVENTF_EXTENDEDKEY ? TRUE : FALSE,
|
||||
(BYTE)input[1].ki.wScan,
|
||||
input[1].ki.dwFlags & KEYEVENTF_KEYUP ? TRUE : FALSE);
|
||||
}
|
||||
else {
|
||||
INPUT input;
|
||||
input.type = INPUT_KEYBOARD;
|
||||
input.ki.wVk = (WORD)wParam;
|
||||
input.ki.wScan = (lParam & 0xFFF0000) >> 16;
|
||||
input.ki.time = GetMessageTime();
|
||||
input.ki.dwExtraInfo = EXTRAINFO_FLAG_SERIALIZED_USER_KEY_EVENT;
|
||||
input.ki.dwFlags = lParam & 0xFFFF;
|
||||
|
||||
if (!SendInput(1, &input, sizeof(INPUT))) {
|
||||
DebugLastError("SendInput");
|
||||
}
|
||||
|
||||
UpdateLocalModifierState(
|
||||
(BYTE)input.ki.wVk,
|
||||
input.ki.dwFlags & KEYEVENTF_EXTENDEDKEY ? TRUE : FALSE,
|
||||
(BYTE)input.ki.wScan,
|
||||
input.ki.dwFlags & KEYEVENTF_KEYUP ? TRUE : FALSE);
|
||||
}
|
||||
|
||||
UpdateLocalModifierState(
|
||||
(BYTE) input.ki.wVk,
|
||||
input.ki.dwFlags & KEYEVENTF_EXTENDEDKEY ? TRUE : FALSE,
|
||||
(BYTE) input.ki.wScan,
|
||||
input.ki.dwFlags & KEYEVENTF_KEYUP ? TRUE : FALSE);
|
||||
}
|
||||
|
||||
return DefWindowProc(hwnd, msg, wParam, lParam);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue