mirror of
https://github.com/keymanapp/keyman.git
synced 2026-09-10 17:47:42 +00:00
Just refactoring minor bits and pieces on this PR. Esp: removed the unnecessary interrogation of TSF (yay!) and cached the debug flag so it isn't checked on every single keystroke.
193 lines
8.4 KiB
C++
193 lines
8.4 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 "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);
|
|
}
|
|
}
|
|
|