/* Name: keybd_shift Copyright: Copyright (C) SIL International. Documentation: Description: Create Date: 11 Dec 2009 Modified Date: 9 Aug 2015 Authors: mcdurdin Related Files: Dependencies: Bugs: Todo: Notes: History: 11 Dec 2009 - mcdurdin - I934 - x64 - Initial version 12 Mar 2010 - mcdurdin - I934 - x64 - Complete 12 Mar 2010 - mcdurdin - I2229 - Remove hints and warnings 31 Dec 2014 - mcdurdin - I4548 - V9.0 - When Alt is down, release of Ctrl, Shift is not detectable within TIP in some languages 09 Aug 2015 - mcdurdin - I4844 - Tidy up PostDummyKeyEvent calls */ #include "pch.h" #include "kbd.h" /** do_keybd_event adds a keyboard event into the keyboard event queue. Parameters: pInputs array of INPUT structures which we will fill with our key events. n pointer to current index into pInput, which we increment for each key event we add This function mimics the keybd_event Windows API function, but instead of posting the event to the system keyboard event queue, it appends it to our internal event queue in pInputs. This allows us to send an atomic set of key events with SendInput. In this function, we map back the extended modifier keys. Now, the history of the PC keyboard means that this is a little weird. The original PC XT keyboard had Left and Right Shift keys, but only a single Ctrl and a single Alt key. This means, that the shift keys were each assigned their own scan code. However, Windows maps both keys to the same virtual key code (VK_SHIFT) for most purposes. When the Right Ctrl and Right Alt keys were introduced, they were differentiated from the original keys not by separate scan codes, but by an extended prefix byte (E0), together with the original scan code. Again, Windows does not differentiate with virtual keys (VK_CONTROL and VK_MENU). This means, to check for Left vs Right Shift, you need to test the scan code (2A and 36) respectively. However, to test for Left vs Right Ctrl/Alt, you need to test the Extended bit in the flags for the message. There are a few places where VK_LSHIFT, VK_RSHIFT, VK_LCONTROL, VK_RCONTROL, VK_LMENU, and VK_RMENU can be used, such as GetKeyState or MapVirtualKey. However, these virtual key codes should never be emitted and will never be received as a key event. To add an additional wrinkle to an already wrinkly story, Keyman (ab)uses the scan code property of key events to identify key events that it has synthesized, as opposed to those generated by the user or by another app (e.g. Remote Desktop scenarios). Instead of using the correct scan code, it will stuff the key event with a 0xFF scan code, and replace that in its GetMessage so the end application gets a look at the correct scan code. However, this will obviously not work with Right Shift, which is differentiated from Left Shift _only_ by scan code. Fortunately, we can afford to not care about this -- it means a little extra flag setting behind the scenes as the right shift key is released and re-pressed during key events, but nothing too dramatic. */ void do_keybd_event(LPINPUT pInputs, int *n, BYTE vk, BYTE scan, DWORD flags, ULONG_PTR extraInfo) { // I4548 INPUT input; input.type = INPUT_KEYBOARD; switch(vk) { case VK_RCONTROL: flags |= KEYEVENTF_EXTENDEDKEY; case VK_LCONTROL: vk = VK_CONTROL; break; case VK_RMENU: flags |= KEYEVENTF_EXTENDEDKEY; case VK_LMENU: vk = VK_MENU; break; case VK_RSHIFT: scan = SCANCODE_RSHIFT; // from kbd.h case VK_LSHIFT: vk = VK_SHIFT; break; } input.ki.wVk = vk; input.ki.wScan = scan; input.ki.dwFlags = flags; input.ki.time = 0; input.ki.dwExtraInfo = extraInfo; SendDebugMessageFormat(0, sdmAIDefault, 0, "do_keybd_event(n=%d, vk=%s, scan=%x, flags=%x)", *n, Debug_VirtualKey(vk), scan, flags); pInputs[*n] = input; (*n)++; } /** keybd_sendprefix pushes a dummy keystroke into the keyboard event queue. Parameters: pInputs array of INPUT structures which we will fill with our key events. n pointer to current index into pInput, which we increment for each key event we add The keybd_sendprefix call is needed, for example, if the user presses Alt+F which matches a rule in the current Keyman keyboard, because without the dummy prefix key, faking the release of the Alt key will activate the mainmenu of the current application. */ void keybd_sendprefix(LPINPUT pInputs, int *n) { SendDebugMessageFormat(0, sdmAIDefault, 0, "keybd_sendprefix: sending prefix down+up"); do_keybd_event(pInputs, n, (BYTE) Globals::get_vk_prefix(), SCAN_FLAG_KEYMAN_KEY_EVENT, 0, 0); // I4548 // I4844 do_keybd_event(pInputs, n, (BYTE) Globals::get_vk_prefix(), SCAN_FLAG_KEYMAN_KEY_EVENT, KEYEVENTF_KEYUP, 0); // I4548 // I4844 } /** keybd_shift_release records the current keyboard state and then releases any modifier keys. If a modifier key must be released, it first sends a dummy prefix key to prevent isolated modifier key actions such as Alt opening up a menu. Parameters: pInputs array of INPUT structures which we will fill with our key events. n pointer to current index into pInput, which we increment for each key event we add kbd pointer to keyboard state (256 byte array), in which we will store the initial modifier state for later restoration by keybd_shift_reset */ void keybd_shift_release(LPINPUT pInputs, int *n, LPBYTE const kbd) { const BYTE modifiers[6] = { VK_LMENU, VK_RMENU, VK_LCONTROL, VK_RCONTROL, VK_LSHIFT, VK_RSHIFT }; BOOL hasSentPrefix = FALSE; for (int i = 0; i < _countof(modifiers); i++) { if (kbd[modifiers[i]] & 0x80) { if (!hasSentPrefix) { keybd_sendprefix(pInputs, n); hasSentPrefix = TRUE; } SendDebugMessageFormat(0, sdmAIDefault, 0, "keybd_shift_release: sending keyup vkey=%s", Debug_VirtualKey(modifiers[i])); do_keybd_event(pInputs, n, modifiers[i], SCAN_FLAG_KEYMAN_KEY_EVENT, KEYEVENTF_KEYUP, 0); } } } /** keybd_shift_reset returns the modifiers to their original pressed state and, if any modifier key presses are emitted, emits also a dummy 'prefix' keystroke in order to prevent default modifier actions such as Alt opening up a menu. Parameters: pInputs array of INPUT structures which we will fill with our key events. n pointer to current index into pInput, which we increment for each key event we add kbd pointer to keyboard state (256 byte array), previously set by keybd_shift_release */ void keybd_shift_reset(LPINPUT pInputs, int *n, LPBYTE const kbd) { const BYTE modifiers[6] = { VK_LMENU, VK_RMENU, VK_LCONTROL, VK_RCONTROL, VK_LSHIFT, VK_RSHIFT }; BOOL needsPrefix = FALSE; for (int i = 0; i < _countof(modifiers); i++) { if (kbd[modifiers[i]] & 0x80) { SendDebugMessageFormat(0, sdmAIDefault, 0, "keybd_shift_reset: sending keydown vkey=%s", Debug_VirtualKey(modifiers[i])); do_keybd_event(pInputs, n, modifiers[i], SCAN_FLAG_KEYMAN_KEY_EVENT, 0, 0); needsPrefix = TRUE; } } if (needsPrefix) { keybd_sendprefix(pInputs, n); } } /** keybd_shift evaluates the current keyboard modifier state and queues key events in order to initially set modifiers to "up" and, after the output key events are queued, resets the modifiers to their initial state. Parameters: pInputs array of INPUT structures which we will fill with our key events. n pointer to current index into pInput, which we increment for each key event we add isReset are we clearing or resetting the modifier state? kbd pointer to keyboard state (256 byte array) that owner must maintain but that we will fill There must be enough space in pInputs to contain 6 x up + 6 x down + 2 prefix-down + 2 prefix-up event = 16 events, to support both the clear and reset calls. */ void keybd_shift(LPINPUT pInputs, int *n, BOOL isReset, LPBYTE const kbd) { if (isReset) { keybd_shift_reset(pInputs, n, kbd); } else { keybd_shift_release(pInputs, n, kbd); } }