spiegel-keyman/windows/src/engine/keyman32/keybd_shift.cpp

194 lines
8.3 KiB
C++

/*
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 "keyman64.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 = 0x36;
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 kbd) {
const BYTE modifiers[6] = { VK_LMENU, VK_RMENU, VK_LCONTROL, VK_RCONTROL, VK_LSHIFT, VK_RSHIFT };
BOOL hasSentPrefix = FALSE;
GetKeyboardState(kbd);
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 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 kbd) {
if (isReset) {
keybd_shift_reset(pInputs, n, kbd);
} else {
keybd_shift_release(pInputs, n, kbd);
}
}