/****************************** 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++; }