You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
423 lines
12 KiB
423 lines
12 KiB
/****************************** Module Header ******************************\
|
|
* Module Name: keyboard.c
|
|
*
|
|
* Copyright (c) 1985 - 1999, Microsoft Corporation
|
|
*
|
|
* History:
|
|
* 11-11-90 DavidPe Created.
|
|
* 13-Feb-1991 mikeke Added Revalidation code (None)
|
|
\***************************************************************************/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
|
|
|
|
/***************************************************************************\
|
|
* _GetKeyState (API)
|
|
*
|
|
* This API returns the up/down and toggle state of the specified VK based
|
|
* on the input synchronized keystate in the current queue. The toggle state
|
|
* is mainly for 'state' keys like Caps-Lock that are toggled each time you
|
|
* press them.
|
|
*
|
|
* History:
|
|
* 11-11-90 DavidPe Created.
|
|
\***************************************************************************/
|
|
|
|
SHORT _GetKeyState(
|
|
int vk)
|
|
{
|
|
UINT wKeyState;
|
|
PTHREADINFO pti;
|
|
|
|
if ((UINT)vk >= CVKKEYSTATE) {
|
|
RIPERR1(ERROR_INVALID_PARAMETER,
|
|
RIP_WARNING,
|
|
"Invalid parameter \"vk\" (%ld) to _GetKeyState",
|
|
vk);
|
|
|
|
return 0;
|
|
}
|
|
|
|
pti = PtiCurrentShared();
|
|
|
|
#ifdef LATER
|
|
//
|
|
// note - anything that accesses the pq structure is a bad idea since it
|
|
// can be changed between any two instructions.
|
|
//
|
|
#endif
|
|
|
|
wKeyState = 0;
|
|
|
|
/*
|
|
* Set the toggle bit.
|
|
*/
|
|
if (TestKeyStateToggle(pti->pq, vk))
|
|
wKeyState = 0x0001;
|
|
|
|
/*
|
|
* Set the keyup/down bit.
|
|
*/
|
|
if (TestKeyStateDown(pti->pq, vk)) {
|
|
/*
|
|
* Used to be wKeyState|= 0x8000.Fix for bug 28820; Ctrl-Enter
|
|
* accelerator doesn't work on Nestscape Navigator Mail 2.0
|
|
*/
|
|
wKeyState |= 0xff80; // This is what 3.1 returned!!!!
|
|
}
|
|
|
|
return (SHORT)wKeyState;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* _GetAsyncKeyState (API)
|
|
*
|
|
* This function is similar to GetKeyState except it returns what could be
|
|
* considered the 'hardware' keystate or what state the key is in at the
|
|
* moment the function is called, rather than based on what key events the
|
|
* application has processed. Also, rather than returning the toggle bit,
|
|
* it has a bit telling whether the key was pressed since the last call to
|
|
* GetAsyncKeyState().
|
|
*
|
|
* History:
|
|
* 11-11-90 DavidPe Created.
|
|
\***************************************************************************/
|
|
|
|
SHORT _GetAsyncKeyState(
|
|
int vk)
|
|
{
|
|
SHORT sKeyState;
|
|
|
|
if ((UINT)vk >= CVKKEYSTATE) {
|
|
RIPERR1(ERROR_INVALID_PARAMETER,
|
|
RIP_WARNING,
|
|
"Invalid parameter \"vk\" (%ld) to _GetAsyncKeyState",
|
|
vk);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* See if this key went down since the last time state for it was
|
|
* read. Clear the flag if so.
|
|
*/
|
|
sKeyState = 0;
|
|
if (TestAsyncKeyStateRecentDown(vk)) {
|
|
ClearAsyncKeyStateRecentDown(vk);
|
|
sKeyState = 1;
|
|
}
|
|
|
|
/*
|
|
* Set the keyup/down bit.
|
|
*/
|
|
if (TestAsyncKeyStateDown(vk))
|
|
sKeyState |= 0x8000;
|
|
|
|
/*
|
|
* Don't return the toggle bit since it's a new bit and might
|
|
* cause compatibility problems.
|
|
*/
|
|
return sKeyState;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* _SetKeyboardState (API)
|
|
*
|
|
* This function allows the app to set the current keystate. This is mainly
|
|
* useful for setting the toggle bit, particularly for the keys associated
|
|
* with the LEDs on the physical keyboard.
|
|
*
|
|
* History:
|
|
* 11-11-90 DavidPe Created.
|
|
* 16-May-1991 mikeke Changed to return BOOL
|
|
\***************************************************************************/
|
|
|
|
BOOL _SetKeyboardState(
|
|
CONST BYTE *pb)
|
|
{
|
|
int i;
|
|
PQ pq;
|
|
PTHREADINFO ptiCurrent = PtiCurrent();
|
|
|
|
pq = ptiCurrent->pq;
|
|
|
|
/*
|
|
* Copy in the new state table.
|
|
*/
|
|
for (i = 0; i < 256; i++, pb++) {
|
|
if (*pb & 0x80) {
|
|
SetKeyStateDown(pq, i);
|
|
} else {
|
|
ClearKeyStateDown(pq, i);
|
|
}
|
|
|
|
if (*pb & 0x01) {
|
|
SetKeyStateToggle(pq, i);
|
|
} else {
|
|
ClearKeyStateToggle(pq, i);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Update the key cache index.
|
|
*/
|
|
gpsi->dwKeyCache++;
|
|
|
|
#ifdef LATER
|
|
// scottlu 6-9-91
|
|
// I don't think we ought to do this unless someone really complains. This
|
|
// could have bad side affects, especially considering that terminal
|
|
// apps will want to do this, and terminal apps could easily not respond
|
|
// to input for awhile, causing this state to change unexpectedly while
|
|
// a user is using some other application. - scottlu.
|
|
|
|
/* DavidPe 02/05/92
|
|
* How about if we only do it when the calling app is foreground?
|
|
*/
|
|
|
|
/*
|
|
* Propagate the toggle bits for the keylight keys to the
|
|
* async keystate table and update the keylights.
|
|
*
|
|
* THIS could be evil in a de-synced environment, but to do this
|
|
* in a totally "synchronous" way is hard.
|
|
*/
|
|
if (pb[VK_CAPITAL] & 0x01) {
|
|
SetAsyncKeyStateToggle(VK_CAPITAL);
|
|
} else {
|
|
ClearAsyncKeyStateToggle(VK_CAPITAL);
|
|
}
|
|
|
|
if (pb[VK_NUMLOCK] & 0x01) {
|
|
SetAsyncKeyStateToggle(VK_NUMLOCK);
|
|
} else {
|
|
ClearAsyncKeyStateToggle(VK_NUMLOCK);
|
|
}
|
|
|
|
if (pb[VK_SCROLL] & 0x01) {
|
|
SetAsyncKeyStateToggle(VK_SCROLL);
|
|
} else {
|
|
ClearAsyncKeyStateToggle(VK_SCROLL);
|
|
}
|
|
|
|
UpdateKeyLights(TRUE);
|
|
#endif
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* RegisterPerUserKeyboardIndicators
|
|
*
|
|
* Saves the current keyboard indicators in the user's profile.
|
|
*
|
|
* ASSUMPTIONS:
|
|
*
|
|
* 10-14-92 IanJa Created.
|
|
\***************************************************************************/
|
|
|
|
static CONST WCHAR wszInitialKeyboardIndicators[] = L"InitialKeyboardIndicators";
|
|
|
|
VOID
|
|
RegisterPerUserKeyboardIndicators(PUNICODE_STRING pProfileUserName)
|
|
{
|
|
WCHAR wszInitKbdInd[2] = L"0";
|
|
|
|
/*
|
|
* Initial Keyboard state (Num-Lock only)
|
|
*/
|
|
|
|
/*
|
|
* For HYDRA we do not want to save this.
|
|
*/
|
|
if (gbRemoteSession) {
|
|
return;
|
|
}
|
|
|
|
wszInitKbdInd[0] += TestAsyncKeyStateToggle(VK_NUMLOCK) ? 2 : 0;
|
|
FastWriteProfileStringW(pProfileUserName,
|
|
PMAP_KEYBOARD,
|
|
wszInitialKeyboardIndicators,
|
|
wszInitKbdInd);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* UpdatePerUserKeyboardIndicators
|
|
*
|
|
* Sets the initial keyboard indicators according to the user's profile.
|
|
*
|
|
* ASSUMPTIONS:
|
|
*
|
|
* 10-14-92 IanJa Created.
|
|
\***************************************************************************/
|
|
VOID
|
|
UpdatePerUserKeyboardIndicators(PUNICODE_STRING pProfileUserName)
|
|
{
|
|
DWORD dw = 0x80000000;
|
|
PQ pq;
|
|
PTHREADINFO ptiCurrent = PtiCurrent();
|
|
pq = ptiCurrent->pq;
|
|
|
|
/*
|
|
* For terminal server, the client is responsible for synchronizing the
|
|
* keyboard state.
|
|
*/
|
|
|
|
if (IsRemoteConnection()) {
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Initial Keyboard state (Num-Lock only)
|
|
*/
|
|
FastGetProfileIntW(pProfileUserName,
|
|
PMAP_KEYBOARD,
|
|
wszInitialKeyboardIndicators,
|
|
0x80000000,
|
|
&dw,
|
|
0);
|
|
|
|
dw &= 0x80000002;
|
|
|
|
|
|
/*
|
|
* The special value 0x80000000 in the registry indicates that the BIOS
|
|
* settings are to be used as the initial LED state. (This is undocumented)
|
|
*/
|
|
if (dw == 0x80000000) {
|
|
dw = gklpBootTime.LedFlags;
|
|
}
|
|
if (dw & KEYBOARD_NUM_LOCK_ON) {
|
|
SetKeyStateToggle(pq, VK_NUMLOCK);
|
|
SetAsyncKeyStateToggle(VK_NUMLOCK);
|
|
SetRawKeyToggle(VK_NUMLOCK);
|
|
} else {
|
|
ClearKeyStateToggle(pq, VK_NUMLOCK);
|
|
ClearAsyncKeyStateToggle(VK_NUMLOCK);
|
|
ClearRawKeyToggle(VK_NUMLOCK);
|
|
}
|
|
|
|
/*
|
|
* Initialize KANA Toggle status
|
|
*/
|
|
gfKanaToggle = FALSE;
|
|
ClearKeyStateToggle(pq, VK_KANA);
|
|
ClearAsyncKeyStateToggle(VK_KANA);
|
|
ClearRawKeyToggle(VK_KANA);
|
|
|
|
UpdateKeyLights(FALSE);
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* UpdateAsyncKeyState
|
|
*
|
|
* Based on a VK and a make/break flag, this function will update the async
|
|
* keystate table.
|
|
*
|
|
* History:
|
|
* 06-09-91 ScottLu Added keystate synchronization across threads.
|
|
* 11-12-90 DavidPe Created.
|
|
\***************************************************************************/
|
|
|
|
void UpdateAsyncKeyState(
|
|
PQ pqOwner,
|
|
UINT wVK,
|
|
BOOL fBreak)
|
|
{
|
|
PQ pqT;
|
|
PLIST_ENTRY pHead, pEntry;
|
|
PTHREADINFO pti;
|
|
|
|
CheckCritIn();
|
|
|
|
/*
|
|
* First check to see if the queue this key is going to has a pending
|
|
* key state event. If it does, post it because we need to copy the
|
|
* async key state into this event as it is before we modify
|
|
* this key's state, or else we'll generate a key state event with
|
|
* the wrong key state in it.
|
|
*/
|
|
if (pqOwner != NULL && pqOwner->QF_flags & QF_UPDATEKEYSTATE) {
|
|
PostUpdateKeyStateEvent(pqOwner);
|
|
}
|
|
|
|
if (!fBreak) {
|
|
/*
|
|
* This key has gone down - update the "recent down" bit in the
|
|
* async key state table.
|
|
*/
|
|
SetAsyncKeyStateRecentDown(wVK);
|
|
|
|
/*
|
|
* This is a key make. If the key was not already down, update the
|
|
* toggle bit.
|
|
*/
|
|
if (!TestAsyncKeyStateDown(wVK)) {
|
|
if (TestAsyncKeyStateToggle(wVK)) {
|
|
ClearAsyncKeyStateToggle(wVK);
|
|
} else {
|
|
SetAsyncKeyStateToggle(wVK);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* This is a make, so turn on the key down bit.
|
|
*/
|
|
SetAsyncKeyStateDown(wVK);
|
|
|
|
} else {
|
|
/*
|
|
* This is a break, so turn off the key down bit.
|
|
*/
|
|
ClearAsyncKeyStateDown(wVK);
|
|
}
|
|
|
|
/*
|
|
* If this is one of the keys we cache, update the async key cache index.
|
|
*/
|
|
if (wVK < CVKASYNCKEYCACHE) {
|
|
gpsi->dwAsyncKeyCache++;
|
|
}
|
|
|
|
/*
|
|
* A key has changed state. Update all queues not receiving this input so
|
|
* they know that this key has changed state. This lets us know which keys to
|
|
* update in the thread specific key state table to keep it in sync
|
|
* with the user. Walking down the thread list may mean that an
|
|
* individual queue may by updated more than once, but it is cheaper
|
|
* than maintaining a list of queues on the desktop.
|
|
*/
|
|
UserAssert(grpdeskRitInput != NULL);
|
|
|
|
pHead = &grpdeskRitInput->PtiList;
|
|
for (pEntry = pHead->Flink; pEntry != pHead; pEntry = pEntry->Flink) {
|
|
pti = CONTAINING_RECORD(pEntry, THREADINFO, PtiLink);
|
|
|
|
/*
|
|
* Don't update the queue this message is going to - it'll be
|
|
* in sync because it is receiving this message.
|
|
*/
|
|
pqT = pti->pq;
|
|
if (pqT == pqOwner)
|
|
continue;
|
|
|
|
/*
|
|
* Set the "recent down" bit. In this case this doesn't really mean
|
|
* "recent down", it means "recent change" (since the last time
|
|
* we synced this queue), either up or down. This tells us which
|
|
* keys went down since the last time this thread synced with key
|
|
* state. Set the "update key state" flag so we know that later
|
|
* we need to sync with these keys.
|
|
*/
|
|
SetKeyRecentDownBit(pqT->afKeyRecentDown, wVK);
|
|
pqT->QF_flags |= QF_UPDATEKEYSTATE;
|
|
}
|
|
|
|
/*
|
|
* Update the key cache index.
|
|
*/
|
|
gpsi->dwKeyCache++;
|
|
}
|