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.
3173 lines
103 KiB
3173 lines
103 KiB
/****************************** Module Header ******************************\
|
|
* Module Name: access.c
|
|
*
|
|
* Copyright (c) 1985 - 1999, Microsoft Corporation
|
|
*
|
|
* This module contains the Access Pack functions.
|
|
*
|
|
* History:
|
|
* 11 Feb 93 GregoryW Created.
|
|
\***************************************************************************/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
CONST ACCESSIBILITYPROC aAccessibilityProc[] = {
|
|
HighContrastHotKey,
|
|
FilterKeys,
|
|
xxxStickyKeys,
|
|
MouseKeys,
|
|
ToggleKeys
|
|
#if 0
|
|
,UtilityManager
|
|
#endif
|
|
};
|
|
|
|
typedef struct tagMODBITINFO {
|
|
int BitPosition;
|
|
BYTE ScanCode;
|
|
USHORT Vk;
|
|
} MODBITINFO, *PMODBITINFO;
|
|
|
|
CONST MODBITINFO aModBit[] =
|
|
{
|
|
{ 0x01, SCANCODE_LSHIFT, VK_LSHIFT },
|
|
{ 0x02, SCANCODE_RSHIFT, VK_RSHIFT | KBDEXT },
|
|
{ 0x04, SCANCODE_CTRL, VK_LCONTROL },
|
|
{ 0x08, SCANCODE_CTRL, VK_RCONTROL | KBDEXT },
|
|
{ 0x10, SCANCODE_ALT, VK_LMENU },
|
|
{ 0x20, SCANCODE_ALT, VK_RMENU | KBDEXT },
|
|
{ 0x40, SCANCODE_LWIN, VK_LWIN },
|
|
{ 0x80, SCANCODE_RWIN, VK_RWIN | KBDEXT}
|
|
};
|
|
|
|
/*
|
|
* The ausMouseVKey array provides a translation from the virtual key
|
|
* value to an index. The index is used to select the appropriate
|
|
* routine to process the virtual key, as well as to select extra
|
|
* information that is used by this routine during its processing.
|
|
*/
|
|
CONST USHORT ausMouseVKey[] = {
|
|
VK_CLEAR,
|
|
VK_PRIOR,
|
|
VK_NEXT,
|
|
VK_END,
|
|
VK_HOME,
|
|
VK_LEFT,
|
|
VK_UP,
|
|
VK_RIGHT,
|
|
VK_DOWN,
|
|
VK_INSERT,
|
|
VK_DELETE,
|
|
VK_MULTIPLY,
|
|
VK_ADD,
|
|
VK_SUBTRACT,
|
|
VK_DIVIDE | KBDEXT,
|
|
VK_NUMLOCK | KBDEXT
|
|
};
|
|
|
|
CONST int cMouseVKeys = sizeof(ausMouseVKey) / sizeof(ausMouseVKey[0]);
|
|
|
|
/*
|
|
* aMouseKeyEvent is an array of function pointers. The routine to call
|
|
* is selected using the index created by scanning the ausMouseVKey array.
|
|
*/
|
|
CONST MOUSEPROC aMouseKeyEvent[] = {
|
|
xxxMKButtonClick, // Numpad 5 (Clear)
|
|
xxxMKMouseMove, // Numpad 9 (PgUp)
|
|
xxxMKMouseMove, // Numpad 3 (PgDn)
|
|
xxxMKMouseMove, // Numpad 1 (End)
|
|
xxxMKMouseMove, // Numpad 7 (Home)
|
|
xxxMKMouseMove, // Numpad 4 (Left)
|
|
xxxMKMouseMove, // Numpad 8 (Up)
|
|
xxxMKMouseMove, // Numpad 6 (Right)
|
|
xxxMKMouseMove, // Numpad 2 (Down)
|
|
xxxMKButtonSetState, // Numpad 0 (Ins)
|
|
xxxMKButtonSetState, // Numpad . (Del)
|
|
MKButtonSelect, // Numpad * (Multiply)
|
|
xxxMKButtonDoubleClick,// Numpad + (Add)
|
|
MKButtonSelect, // Numpad - (Subtract)
|
|
MKButtonSelect, // Numpad / (Divide)
|
|
xxxMKToggleMouseKeys // Num Lock
|
|
};
|
|
|
|
/*
|
|
* ausMouseKeyData contains useful data for the routines that process
|
|
* the virtual mousekeys. This array is indexed using the index created
|
|
* by scanning the ausMouseVKey array.
|
|
*/
|
|
CONST USHORT ausMouseKeyData[] = {
|
|
0, // Numpad 5: Click active button
|
|
MK_UP | MK_RIGHT, // Numpad 9: Up & Right
|
|
MK_DOWN | MK_RIGHT, // Numpad 3: Down & Right
|
|
MK_DOWN | MK_LEFT, // Numpad 1: Down & Left
|
|
MK_UP | MK_LEFT, // Numpad 7: Up & Left
|
|
MK_LEFT, // Numpad 4: Left
|
|
MK_UP, // Numpad 8: Up
|
|
MK_RIGHT, // Numpad 6: Right
|
|
MK_DOWN, // Numpad 2: Down
|
|
FALSE, // Numpad 0: Active button down
|
|
TRUE, // Numpad .: Active button up
|
|
MOUSE_BUTTON_LEFT | MOUSE_BUTTON_RIGHT, // Numpad *: Select both buttons
|
|
0, // Numpad +: Double click active button
|
|
MOUSE_BUTTON_RIGHT, // Numpad -: Select right button
|
|
MOUSE_BUTTON_LEFT, // Numpad /: Select left button
|
|
0
|
|
};
|
|
|
|
|
|
__inline void
|
|
PostAccessNotification(UINT accessKeyType)
|
|
{
|
|
if (gspwndLogonNotify != NULL)
|
|
{
|
|
glinp.ptiLastWoken = GETPTI(gspwndLogonNotify);
|
|
|
|
_PostMessage(gspwndLogonNotify, WM_LOGONNOTIFY,
|
|
LOGON_ACCESSNOTIFY, accessKeyType);
|
|
}
|
|
}
|
|
|
|
void PostRitSound(PTERMINAL pTerm, UINT message) {
|
|
PostEventMessage(
|
|
pTerm->ptiDesktop,
|
|
pTerm->ptiDesktop->pq,
|
|
QEVENT_RITSOUND,
|
|
NULL,
|
|
message, 0, 0);
|
|
return;
|
|
}
|
|
|
|
void PostAccessibility( LPARAM lParam )
|
|
{
|
|
PTERMINAL pTerm = grpdeskRitInput->rpwinstaParent->pTerm;
|
|
|
|
PostEventMessage(
|
|
pTerm->ptiDesktop,
|
|
pTerm->ptiDesktop->pq,
|
|
QEVENT_RITACCESSIBILITY,
|
|
NULL,
|
|
0, HSHELL_ACCESSIBILITYSTATE, lParam);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* AccessProceduresStream
|
|
*
|
|
* This function controls the order in which the access functions are called.
|
|
* All key events pass through this routine. If an access function returns
|
|
* FALSE then none of the other access functions in the stream are called.
|
|
* This routine is called initially from KeyboardApcProcedure(), but then
|
|
* can be called any number of times by the access functions as they process
|
|
* the current key event or add more key events.
|
|
*
|
|
* Return value:
|
|
* TRUE All access functions returned TRUE, the key event can be
|
|
* processed.
|
|
* FALSE An access function returned FALSE, the key event should be
|
|
* discarded.
|
|
*
|
|
* History:
|
|
* 11 Feb 93 GregoryW Created.
|
|
\***************************************************************************/
|
|
BOOL AccessProceduresStream(PKE pKeyEvent, ULONG ExtraInformation, int dwProcIndex)
|
|
{
|
|
int index;
|
|
|
|
CheckCritIn();
|
|
for (index = dwProcIndex; index < ARRAY_SIZE(aAccessibilityProc); index++) {
|
|
if (!aAccessibilityProc[index](pKeyEvent, ExtraInformation, index+1)) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* FKActivationTimer
|
|
*
|
|
* If the hot key (right shift key) is held down this routine is called after
|
|
* 4, 8, 12 and 16 seconds. This routine is only called at the 12 and 16
|
|
* second time points if we're in the process of enabling FilterKeys. If at
|
|
* 8 seconds FilterKeys is disabled then this routine will not be called again
|
|
* until the hot key is released and then pressed.
|
|
*
|
|
* This routine is called with the critical section already locked.
|
|
*
|
|
* Return value:
|
|
* 0
|
|
*
|
|
* History:
|
|
* 11 Feb 93 GregoryW Created.
|
|
\***************************************************************************/
|
|
VOID FKActivationTimer(
|
|
PWND pwnd,
|
|
UINT message,
|
|
UINT_PTR nID,
|
|
LPARAM lParam)
|
|
{
|
|
UINT TimerDelta;
|
|
PTERMINAL pTerm = grpdeskRitInput->rpwinstaParent->pTerm;
|
|
|
|
UNREFERENCED_PARAMETER(pwnd);
|
|
UNREFERENCED_PARAMETER(lParam);
|
|
UNREFERENCED_PARAMETER(message);
|
|
|
|
CheckCritIn();
|
|
|
|
switch (gFilterKeysState) {
|
|
|
|
case FKFIRSTWARNING:
|
|
//
|
|
// The audible feedback cannot be disabled for this warning.
|
|
//
|
|
TimerDelta = FKACTIVATIONDELTA;
|
|
break;
|
|
|
|
case FKTOGGLE:
|
|
if (TEST_ACCESSFLAG(FilterKeys, FKF_FILTERKEYSON)) {
|
|
//
|
|
// Disable Filter Keys
|
|
//
|
|
CLEAR_ACCESSFLAG(FilterKeys, FKF_FILTERKEYSON);
|
|
if (TEST_ACCESSFLAG(FilterKeys, FKF_HOTKEYSOUND)) {
|
|
PostRitSound(pTerm, RITSOUND_DOWNSIREN);
|
|
}
|
|
PostAccessibility(ACCESS_FILTERKEYS);
|
|
//
|
|
// Stop all timers that are currently running.
|
|
//
|
|
if (gtmridFKResponse != 0) {
|
|
KILLRITTIMER(NULL, gtmridFKResponse);
|
|
gtmridFKResponse = 0;
|
|
}
|
|
|
|
if (gtmridFKAcceptanceDelay != 0) {
|
|
KILLRITTIMER(NULL, gtmridFKAcceptanceDelay);
|
|
gtmridFKAcceptanceDelay = 0;
|
|
}
|
|
|
|
//
|
|
// Don't reset activation timer. Emergency levels are only
|
|
// activated after enabling Filter Keys.
|
|
//
|
|
return;
|
|
} else {
|
|
if (TEST_ACCESSFLAG(FilterKeys, FKF_HOTKEYSOUND)) {
|
|
PostRitSound(pTerm, RITSOUND_UPSIREN);
|
|
}
|
|
|
|
PostAccessNotification(ACCESS_FILTERKEYS);
|
|
|
|
}
|
|
TimerDelta = FKEMERGENCY1DELTA;
|
|
break;
|
|
|
|
case FKFIRSTLEVELEMERGENCY:
|
|
//
|
|
// First level emergency settings:
|
|
// Repeat Rate OFF
|
|
// SlowKeys OFF (Acceptance Delay of 0)
|
|
// BounceKeys Debounce Time of 1 second
|
|
//
|
|
if (TEST_ACCESSFLAG(FilterKeys, FKF_HOTKEYSOUND)) {
|
|
PostEventMessage(pTerm->ptiDesktop,
|
|
pTerm->ptiDesktop->pq,
|
|
QEVENT_RITSOUND,
|
|
NULL,
|
|
RITSOUND_DOBEEP,
|
|
RITSOUND_UPSIREN,
|
|
2);
|
|
}
|
|
gFilterKeys.iRepeatMSec = 0;
|
|
gFilterKeys.iWaitMSec = 0;
|
|
gFilterKeys.iBounceMSec = 1000;
|
|
TimerDelta = FKEMERGENCY2DELTA;
|
|
break;
|
|
|
|
case FKSECONDLEVELEMERGENCY:
|
|
//
|
|
// Second level emergency settings:
|
|
// Repeat Rate OFF
|
|
// SlowKeys Acceptance Delay of 2 seconds
|
|
// BounceKeys OFF (Debounce Time of 0)
|
|
//
|
|
gFilterKeys.iRepeatMSec = 0;
|
|
gFilterKeys.iWaitMSec = 2000;
|
|
gFilterKeys.iBounceMSec = 0;
|
|
if (TEST_ACCESSFLAG(FilterKeys, FKF_HOTKEYSOUND)) {
|
|
PostEventMessage(
|
|
pTerm->ptiDesktop,
|
|
pTerm->ptiDesktop->pq,
|
|
QEVENT_RITSOUND,
|
|
NULL,
|
|
RITSOUND_DOBEEP, RITSOUND_UPSIREN, 3);
|
|
}
|
|
return;
|
|
break;
|
|
|
|
default:
|
|
return;
|
|
}
|
|
|
|
gFilterKeysState++;
|
|
gtmridFKActivation = InternalSetTimer(NULL,
|
|
nID,
|
|
TimerDelta,
|
|
FKActivationTimer,
|
|
TMRF_RIT | TMRF_ONESHOT);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* FKBounceKeyTimer
|
|
*
|
|
* If BounceKeys is active this routine is called after the debounce time
|
|
* has expired. Until then, the last key released will not be accepted as
|
|
* input if it is pressed again.
|
|
*
|
|
* Return value:
|
|
* 0
|
|
*
|
|
* History:
|
|
* 11 Feb 93 GregoryW Created.
|
|
\***************************************************************************/
|
|
VOID FKBounceKeyTimer(
|
|
PWND pwnd,
|
|
UINT message,
|
|
UINT_PTR nID,
|
|
LPARAM lParam)
|
|
{
|
|
UNREFERENCED_PARAMETER(pwnd);
|
|
UNREFERENCED_PARAMETER(lParam);
|
|
UNREFERENCED_PARAMETER(nID);
|
|
UNREFERENCED_PARAMETER(message);
|
|
|
|
CheckCritIn();
|
|
|
|
//
|
|
// All we need to do is clear gBounceVk to allow this key as the
|
|
// next keystroke.
|
|
//
|
|
gBounceVk = 0;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* xxxFKRepeatRateTimer
|
|
*
|
|
* If FilterKeys is active and a repeat rate is set, this routine controls
|
|
* the rate at which the last key pressed repeats. The hardware keyboard
|
|
* typematic repeat is ignored in this case.
|
|
*
|
|
* This routine is called with the critical section already locked.
|
|
*
|
|
* Return value:
|
|
* 0
|
|
*
|
|
* History:
|
|
* 11 Feb 93 GregoryW Created.
|
|
\***************************************************************************/
|
|
VOID xxxFKRepeatRateTimer(
|
|
PWND pwnd,
|
|
UINT message,
|
|
UINT_PTR nID,
|
|
LPARAM lParam)
|
|
{
|
|
UNREFERENCED_PARAMETER(pwnd);
|
|
UNREFERENCED_PARAMETER(lParam);
|
|
UNREFERENCED_PARAMETER(message);
|
|
|
|
CheckCritIn();
|
|
|
|
if (TEST_ACCESSFLAG(FilterKeys, FKF_CLICKON)) {
|
|
PTERMINAL pTerm = grpdeskRitInput->rpwinstaParent->pTerm;
|
|
PostRitSound(pTerm, RITSOUND_KEYCLICK);
|
|
}
|
|
|
|
UserAssert(gtmridFKAcceptanceDelay == 0);
|
|
|
|
gtmridFKResponse = InternalSetTimer(NULL,
|
|
nID,
|
|
gFilterKeys.iRepeatMSec,
|
|
xxxFKRepeatRateTimer,
|
|
TMRF_RIT | TMRF_ONESHOT);
|
|
if (AccessProceduresStream(gpFKKeyEvent, gFKExtraInformation, gFKNextProcIndex)) {
|
|
xxxProcessKeyEvent(gpFKKeyEvent, gFKExtraInformation, FALSE);
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* xxxFKAcceptanceDelayTimer
|
|
*
|
|
* If FilterKeys is active and an acceptance delay is set, this routine
|
|
* is called after the key has been held down for the acceptance delay
|
|
* period.
|
|
*
|
|
* This routine is called with the critical section already locked.
|
|
*
|
|
* Return value:
|
|
* 0
|
|
*
|
|
* History:
|
|
* 11 Feb 93 GregoryW Created.
|
|
\***************************************************************************/
|
|
VOID xxxFKAcceptanceDelayTimer(
|
|
PWND pwnd,
|
|
UINT message,
|
|
UINT_PTR nID,
|
|
LPARAM lParam)
|
|
{
|
|
UNREFERENCED_PARAMETER(lParam);
|
|
UNREFERENCED_PARAMETER(message);
|
|
UNREFERENCED_PARAMETER(pwnd);
|
|
|
|
CheckCritIn();
|
|
|
|
//
|
|
// The key has been held down long enough. Send it on...
|
|
//
|
|
if (TEST_ACCESSFLAG(FilterKeys, FKF_CLICKON)) {
|
|
PTERMINAL pTerm = grpdeskRitInput->rpwinstaParent->pTerm;
|
|
PostRitSound(pTerm, RITSOUND_KEYCLICK);
|
|
}
|
|
|
|
if (AccessProceduresStream(gpFKKeyEvent, gFKExtraInformation, gFKNextProcIndex)) {
|
|
xxxProcessKeyEvent(gpFKKeyEvent, gFKExtraInformation, FALSE);
|
|
}
|
|
|
|
if (!gFilterKeys.iRepeatMSec) {
|
|
//
|
|
// gptmrFKAcceptanceDelay needs to be released, but we can't do it while
|
|
// in a RIT timer routine. Set a global to indicate that the subsequent
|
|
// break of this key should be passed on and the timer freed.
|
|
//
|
|
SET_ACCF(ACCF_FKMAKECODEPROCESSED);
|
|
return;
|
|
}
|
|
|
|
UserAssert(gtmridFKResponse == 0);
|
|
if (gFilterKeys.iDelayMSec) {
|
|
gtmridFKResponse = InternalSetTimer(NULL,
|
|
nID,
|
|
gFilterKeys.iDelayMSec,
|
|
xxxFKRepeatRateTimer,
|
|
TMRF_RIT | TMRF_ONESHOT);
|
|
} else {
|
|
gtmridFKResponse = InternalSetTimer(NULL,
|
|
nID,
|
|
gFilterKeys.iRepeatMSec,
|
|
xxxFKRepeatRateTimer,
|
|
TMRF_RIT | TMRF_ONESHOT);
|
|
}
|
|
|
|
//
|
|
// gptmrFKAcceptanceDelay timer structure was reused so set handle to
|
|
// NULL.
|
|
//
|
|
gtmridFKAcceptanceDelay = 0;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* FilterKeys
|
|
*
|
|
* History:
|
|
* 11 Feb 93 GregoryW Created.
|
|
\***************************************************************************/
|
|
BOOL FilterKeys(
|
|
PKE pKeyEvent,
|
|
ULONG ExtraInformation,
|
|
int NextProcIndex)
|
|
{
|
|
int fBreak;
|
|
BYTE Vk;
|
|
|
|
CheckCritIn();
|
|
Vk = (BYTE)(pKeyEvent->usFlaggedVk & 0xff);
|
|
fBreak = pKeyEvent->usFlaggedVk & KBDBREAK;
|
|
|
|
//
|
|
// Check for Filter Keys hot key (right shift key).
|
|
//
|
|
if (Vk == VK_RSHIFT) {
|
|
if (fBreak) {
|
|
if (gtmridFKActivation != 0) {
|
|
KILLRITTIMER(NULL, gtmridFKActivation);
|
|
gtmridFKActivation = 0;
|
|
}
|
|
gFilterKeysState = FKIDLE;
|
|
} else if (ONLYRIGHTSHIFTDOWN(gPhysModifierState)) {
|
|
//
|
|
// Verify that activation via hotkey is allowed.
|
|
//
|
|
if (TEST_ACCESSFLAG(FilterKeys, FKF_HOTKEYACTIVE)) {
|
|
if ((gtmridFKActivation == 0) && (gFilterKeysState != FKMOUSEMOVE)) {
|
|
gFilterKeysState = FKFIRSTWARNING;
|
|
gtmridFKActivation = InternalSetTimer(NULL,
|
|
0,
|
|
FKFIRSTWARNINGTIME,
|
|
FKActivationTimer,
|
|
TMRF_RIT | TMRF_ONESHOT);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// If another key is pressed while the hot key is down, kill
|
|
// the timer.
|
|
//
|
|
if (Vk != VK_RSHIFT && gtmridFKActivation != 0) {
|
|
gFilterKeysState = FKIDLE;
|
|
KILLRITTIMER(NULL, gtmridFKActivation);
|
|
gtmridFKActivation = 0;
|
|
}
|
|
|
|
//
|
|
// If Filter Keys not enabled send the key event on.
|
|
//
|
|
if (!TEST_ACCESSFLAG(FilterKeys, FKF_FILTERKEYSON)) {
|
|
return TRUE;
|
|
}
|
|
|
|
if (fBreak) {
|
|
//
|
|
// Kill the current timer and activate bounce key timer (if this is
|
|
// a break of the last key down).
|
|
//
|
|
if (Vk == gLastVkDown) {
|
|
KILLRITTIMER(NULL, gtmridFKResponse);
|
|
gtmridFKResponse = 0;
|
|
|
|
gLastVkDown = 0;
|
|
if (gtmridFKAcceptanceDelay != 0) {
|
|
KILLRITTIMER(NULL, gtmridFKAcceptanceDelay);
|
|
gtmridFKAcceptanceDelay = 0;
|
|
if (!TEST_ACCF(ACCF_FKMAKECODEPROCESSED)) {
|
|
//
|
|
// This key was released before accepted. Don't pass on the
|
|
// break.
|
|
//
|
|
return FALSE;
|
|
} else {
|
|
CLEAR_ACCF(ACCF_FKMAKECODEPROCESSED);
|
|
}
|
|
}
|
|
|
|
if (gFilterKeys.iBounceMSec) {
|
|
gBounceVk = Vk;
|
|
gtmridFKResponse = InternalSetTimer(NULL,
|
|
0,
|
|
gFilterKeys.iBounceMSec,
|
|
FKBounceKeyTimer,
|
|
TMRF_RIT | TMRF_ONESHOT);
|
|
if (TEST_ACCF(ACCF_IGNOREBREAKCODE)) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
//
|
|
// Make key processing
|
|
//
|
|
// First check to see if this is a typematic repeat. If so, we
|
|
// can ignore this key event. Our timer will handle any repeats.
|
|
// LastVkDown is cleared during processing of the break.
|
|
//
|
|
if (Vk == gLastVkDown) {
|
|
return FALSE;
|
|
}
|
|
//
|
|
// Remember current Virtual Key down for typematic repeat check.
|
|
//
|
|
gLastVkDown = Vk;
|
|
|
|
if (gBounceVk) {
|
|
//
|
|
// BounceKeys is active. If this is a make of the last
|
|
// key pressed we ignore it. Only when the BounceKey
|
|
// timer expires or another key is pressed will we accept
|
|
// this key.
|
|
//
|
|
if (Vk == gBounceVk) {
|
|
//
|
|
// Ignore this make event and the subsequent break
|
|
// code. BounceKey timer will be reset on break.
|
|
//
|
|
SET_ACCF(ACCF_IGNOREBREAKCODE);
|
|
return FALSE;
|
|
} else {
|
|
//
|
|
// We have a make of a new key. Kill the BounceKey
|
|
// timer and clear gBounceVk.
|
|
//
|
|
UserAssert(gtmridFKResponse);
|
|
if (gtmridFKResponse != 0) {
|
|
KILLRITTIMER(NULL, gtmridFKResponse);
|
|
gtmridFKResponse = 0;
|
|
}
|
|
gBounceVk = 0;
|
|
}
|
|
}
|
|
CLEAR_ACCF(ACCF_IGNOREBREAKCODE);
|
|
|
|
//
|
|
// Give audible feedback that key was pressed.
|
|
//
|
|
if (TEST_ACCESSFLAG(FilterKeys, FKF_CLICKON)) {
|
|
PTERMINAL pTerm = grpdeskRitInput->rpwinstaParent->pTerm;
|
|
PostRitSound(
|
|
pTerm,
|
|
RITSOUND_KEYCLICK);
|
|
}
|
|
|
|
//
|
|
// If gptmrFKAcceptanceDelay is non-NULL the previous key was
|
|
// not held down long enough to be accepted. Kill the current
|
|
// timer. A new timer will be started below for the key we're
|
|
// processing now.
|
|
//
|
|
if (gtmridFKAcceptanceDelay != 0) {
|
|
KILLRITTIMER(NULL, gtmridFKAcceptanceDelay);
|
|
gtmridFKAcceptanceDelay = 0;
|
|
}
|
|
|
|
//
|
|
// If gptmrFKResponse is non-NULL a repeat rate timer is active
|
|
// on the previous key. Kill the timer as we have a new make key.
|
|
//
|
|
if (gtmridFKResponse != 0) {
|
|
KILLRITTIMER(NULL, gtmridFKResponse);
|
|
gtmridFKResponse = 0;
|
|
}
|
|
|
|
//
|
|
// Save the current key event for later use if we process an
|
|
// acceptance delay or key repeat.
|
|
//
|
|
*gpFKKeyEvent = *pKeyEvent;
|
|
gFKExtraInformation = ExtraInformation;
|
|
gFKNextProcIndex = NextProcIndex;
|
|
|
|
//
|
|
// If there is an acceptance delay, set timer and ignore current
|
|
// key event. When timer expires, saved key event will be sent.
|
|
//
|
|
if (gFilterKeys.iWaitMSec) {
|
|
gtmridFKAcceptanceDelay = InternalSetTimer(NULL,
|
|
0,
|
|
gFilterKeys.iWaitMSec,
|
|
xxxFKAcceptanceDelayTimer,
|
|
TMRF_RIT | TMRF_ONESHOT);
|
|
CLEAR_ACCF(ACCF_FKMAKECODEPROCESSED);
|
|
return FALSE;
|
|
}
|
|
//
|
|
// No acceptance delay. Before sending this key event on the
|
|
// timer routine must be set to either the delay until repeat value
|
|
// or the repeat rate value. If repeat rate is 0 then ignore
|
|
// delay until repeat.
|
|
//
|
|
if (!gFilterKeys.iRepeatMSec) {
|
|
return TRUE;
|
|
}
|
|
|
|
UserAssert(gtmridFKResponse == 0);
|
|
if (gFilterKeys.iDelayMSec) {
|
|
gtmridFKResponse = InternalSetTimer(NULL,
|
|
0,
|
|
gFilterKeys.iDelayMSec,
|
|
xxxFKRepeatRateTimer,
|
|
TMRF_RIT | TMRF_ONESHOT);
|
|
} else {
|
|
gtmridFKResponse = InternalSetTimer(NULL,
|
|
0,
|
|
gFilterKeys.iRepeatMSec,
|
|
xxxFKRepeatRateTimer,
|
|
TMRF_RIT | TMRF_ONESHOT);
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* StopFilterKeysTimers
|
|
*
|
|
* Called from SystemParametersInfo on SPI_SETFILTERKEYS if FKF_FILTERKEYSON
|
|
* is not set. Timers must be stopped if user turns FilterKeys off.
|
|
*
|
|
* History:
|
|
* 18 Jul 94 GregoryW Created.
|
|
\***************************************************************************/
|
|
VOID StopFilterKeysTimers(VOID)
|
|
{
|
|
|
|
if (gtmridFKResponse != 0) {
|
|
KILLRITTIMER(NULL, gtmridFKResponse);
|
|
gtmridFKResponse = 0;
|
|
}
|
|
if (gtmridFKAcceptanceDelay) {
|
|
KILLRITTIMER(NULL, gtmridFKAcceptanceDelay);
|
|
gtmridFKAcceptanceDelay = 0;
|
|
}
|
|
gLastVkDown = 0;
|
|
gBounceVk = 0;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* xxxStickyKeys
|
|
*
|
|
* History:
|
|
* 11 Feb 93 GregoryW Created.
|
|
\***************************************************************************/
|
|
BOOL xxxStickyKeys(PKE pKeyEvent, ULONG ExtraInformation, int NextProcIndex)
|
|
{
|
|
int fBreak;
|
|
BYTE NewLockBits, NewLatchBits;
|
|
int BitPositions;
|
|
BOOL bChange;
|
|
PTERMINAL pTerm = grpdeskRitInput->rpwinstaParent->pTerm;
|
|
|
|
|
|
CheckCritIn();
|
|
fBreak = pKeyEvent->usFlaggedVk & KBDBREAK;
|
|
|
|
if (gCurrentModifierBit) {
|
|
//
|
|
// Process modifier key
|
|
//
|
|
|
|
//
|
|
// One method of activating StickyKeys is to press either the
|
|
// left shift key or the right shift key five times without
|
|
// pressing any other keys. We don't want the typematic shift
|
|
// (make code) to enable/disable StickyKeys so we perform a
|
|
// special test for them.
|
|
//
|
|
if (!fBreak) {
|
|
if (gCurrentModifierBit & gPrevModifierState) {
|
|
//
|
|
// This is a typematic make of a modifier key. Don't do
|
|
// any further processing. Just pass it along.
|
|
//
|
|
gPrevModifierState = gPhysModifierState;
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
gPrevModifierState = gPhysModifierState;
|
|
|
|
if (LEFTSHIFTKEY(pKeyEvent->usFlaggedVk) &&
|
|
((gPhysModifierState & ~gCurrentModifierBit) == 0)) {
|
|
gStickyKeysLeftShiftCount++;
|
|
} else {
|
|
gStickyKeysLeftShiftCount = 0;
|
|
}
|
|
if (RIGHTSHIFTKEY(pKeyEvent->usFlaggedVk) &&
|
|
((gPhysModifierState & ~gCurrentModifierBit) == 0)) {
|
|
gStickyKeysRightShiftCount++;
|
|
} else {
|
|
gStickyKeysRightShiftCount = 0;
|
|
}
|
|
|
|
//
|
|
// Check to see if StickyKeys should be toggled on/off
|
|
//
|
|
if ((gStickyKeysLeftShiftCount == (TOGGLE_STICKYKEYS_COUNT * 2)) ||
|
|
(gStickyKeysRightShiftCount == (TOGGLE_STICKYKEYS_COUNT * 2))) {
|
|
if (TEST_ACCESSFLAG(StickyKeys, SKF_HOTKEYACTIVE)) {
|
|
if (TEST_ACCESSFLAG(StickyKeys, SKF_STICKYKEYSON)) {
|
|
xxxTurnOffStickyKeys();
|
|
if (TEST_ACCESSFLAG(StickyKeys, SKF_HOTKEYSOUND)) {
|
|
PostRitSound(
|
|
pTerm,
|
|
RITSOUND_DOWNSIREN);
|
|
}
|
|
} else {
|
|
if (TEST_ACCESSFLAG(StickyKeys, SKF_HOTKEYSOUND)) {
|
|
PostRitSound(
|
|
pTerm,
|
|
RITSOUND_UPSIREN);
|
|
}
|
|
// To make the notification window get the focus
|
|
// The same is done other places where WM_LOGONNOTIFY message is
|
|
// sent : a-anilk
|
|
PostAccessNotification(ACCESS_STICKYKEYS);
|
|
|
|
}
|
|
}
|
|
gStickyKeysLeftShiftCount = 0;
|
|
gStickyKeysRightShiftCount = 0;
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// If StickyKeys is enabled process the modifier key, otherwise
|
|
// just pass on the modifier key.
|
|
//
|
|
if (TEST_ACCESSFLAG(StickyKeys, SKF_STICKYKEYSON)) {
|
|
if (fBreak) {
|
|
//
|
|
// If either locked or latched bit set for this key then
|
|
// don't pass the break on.
|
|
//
|
|
if (UNION(gLatchBits, gLockBits) & gCurrentModifierBit) {
|
|
return FALSE;
|
|
} else {
|
|
return TRUE;
|
|
}
|
|
} else{
|
|
if (gPhysModifierState != gCurrentModifierBit) {
|
|
//
|
|
// More than one modifier key down at the same time.
|
|
// This condition may signal sticky keys to turn off.
|
|
// The routine xxxTwoKeysDown will return the new value
|
|
// of fStickyKeysOn. If sticky keys is turned off
|
|
// (return value 0), the key event should be passed
|
|
// on without further processing here.
|
|
//
|
|
if (!xxxTwoKeysDown(NextProcIndex)) {
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Modifier states were set to physical state by
|
|
// xxxTwoKeysDown. The modifier keys currently in
|
|
// the down position will be latched by updating
|
|
// gLatchBits. No more processing for this key
|
|
// event is needed.
|
|
//
|
|
bChange = gLockBits ||
|
|
(gLatchBits != gPhysModifierState);
|
|
gLatchBits = gPhysModifierState;
|
|
gLockBits = 0;
|
|
if (bChange) {
|
|
PostAccessibility( ACCESS_STICKYKEYS );
|
|
}
|
|
|
|
//
|
|
// Provide sound feedback, if enabled, before returning.
|
|
//
|
|
if (TEST_ACCESSFLAG(StickyKeys, SKF_AUDIBLEFEEDBACK)) {
|
|
PostRitSound(
|
|
pTerm,
|
|
RITSOUND_LOWBEEP);
|
|
PostRitSound(
|
|
pTerm,
|
|
RITSOUND_HIGHBEEP);
|
|
}
|
|
return FALSE;
|
|
}
|
|
//
|
|
// Figure out which bits (Shift, Ctrl or Alt key bits) to
|
|
// examine. Also set up default values for NewLatchBits
|
|
// and NewLockBits in case they're not set later.
|
|
//
|
|
// See the depiction of the bit pattern in KeyboardApcProcedure.
|
|
//
|
|
// Bit 0 -- L SHIFT
|
|
// Bit 1 -- R SHIFT
|
|
// Bit 2 -- L CTL
|
|
// Bit 3 -- R CTL
|
|
// Bit 4 -- L ALT
|
|
// Bit 5 -- R RLT
|
|
// Bit 6 -- L WIN
|
|
// Bit 7 -- R WIN
|
|
switch(pKeyEvent->usFlaggedVk) {
|
|
case VK_LSHIFT:
|
|
case VK_RSHIFT:
|
|
BitPositions = 0x3;
|
|
break;
|
|
case VK_LCONTROL:
|
|
case VK_RCONTROL:
|
|
BitPositions = 0xc;
|
|
break;
|
|
case VK_LMENU:
|
|
case VK_RMENU:
|
|
BitPositions = 0x30;
|
|
break;
|
|
case VK_LWIN:
|
|
case VK_RWIN:
|
|
BitPositions = 0xc0;
|
|
break;
|
|
}
|
|
NewLatchBits = gLatchBits;
|
|
NewLockBits = gLockBits;
|
|
|
|
//
|
|
// If either left or right modifier is locked clear latched
|
|
// and locked states and send appropriate break/make messages.
|
|
//
|
|
if (gLockBits & BitPositions) {
|
|
NewLockBits = gLockBits & ~BitPositions;
|
|
NewLatchBits = gLatchBits & ~BitPositions;
|
|
xxxUpdateModifierState(
|
|
NewLockBits | NewLatchBits | gCurrentModifierBit,
|
|
NextProcIndex
|
|
);
|
|
} else {
|
|
//
|
|
// If specific lock bit (left or right) not
|
|
// previously set then toggle latch bits.
|
|
//
|
|
if (!(gLockBits & gCurrentModifierBit)) {
|
|
NewLatchBits = gLatchBits ^ gCurrentModifierBit;
|
|
}
|
|
//
|
|
// If locked mode (tri-state) enabled then if latch or lock
|
|
// bit previously set, toggle lock bit.
|
|
//
|
|
if (TEST_ACCESSFLAG(StickyKeys, SKF_TRISTATE)) {
|
|
if (UNION(gLockBits, gLatchBits) & gCurrentModifierBit) {
|
|
NewLockBits = gLockBits ^ gCurrentModifierBit;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Update globals
|
|
//
|
|
bChange = ((gLatchBits != NewLatchBits) ||
|
|
(gLockBits != NewLockBits));
|
|
|
|
gLatchBits = NewLatchBits;
|
|
gLockBits = NewLockBits;
|
|
|
|
if (bChange) {
|
|
PostAccessibility( ACCESS_STICKYKEYS );
|
|
}
|
|
//
|
|
// Now provide sound feedback if enabled. For the transition
|
|
// to LATCH mode issue a low beep then a high beep. For the
|
|
// transition to LOCKED mode issue a high beep. For the
|
|
// transition out of LOCKED mode (or LATCH mode if tri-state
|
|
// not enabled) issue a low beep.
|
|
//
|
|
if (TEST_ACCESSFLAG(StickyKeys, SKF_AUDIBLEFEEDBACK)) {
|
|
if (!(gLockBits & gCurrentModifierBit)) {
|
|
PostRitSound(
|
|
pTerm,
|
|
RITSOUND_LOWBEEP);
|
|
}
|
|
if ((gLatchBits | gLockBits) & gCurrentModifierBit) {
|
|
PostRitSound(
|
|
pTerm,
|
|
RITSOUND_HIGHBEEP);
|
|
}
|
|
}
|
|
//
|
|
// Pass key on if shift bit is set (e.g., if transitioning
|
|
// from shift to lock mode don't pass on make).
|
|
//
|
|
if (gLatchBits & gCurrentModifierBit) {
|
|
return TRUE;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
|
|
}
|
|
}
|
|
} else {
|
|
//
|
|
// Non-shift key processing here...
|
|
//
|
|
gStickyKeysLeftShiftCount = 0;
|
|
gStickyKeysRightShiftCount = 0;
|
|
if (!TEST_ACCESSFLAG(StickyKeys, SKF_STICKYKEYSON)) {
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// If no modifier keys are down, or this is a break, pass the key event
|
|
// on and clear any latch states.
|
|
//
|
|
if (!gPhysModifierState || fBreak) {
|
|
if (AccessProceduresStream(pKeyEvent, ExtraInformation, NextProcIndex)) {
|
|
xxxProcessKeyEvent(pKeyEvent, ExtraInformation, FALSE);
|
|
}
|
|
xxxUpdateModifierState(gLockBits, NextProcIndex);
|
|
|
|
bChange = gLatchBits != 0;
|
|
gLatchBits = 0;
|
|
if (bChange) {
|
|
|
|
PostAccessibility( ACCESS_STICKYKEYS );
|
|
}
|
|
return FALSE;
|
|
} else {
|
|
//
|
|
// This is a make of a non-modifier key and there is a modifier key
|
|
// down. Update the states and pass the key event on.
|
|
//
|
|
xxxTwoKeysDown(NextProcIndex);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* xxxUpdateModifierState
|
|
*
|
|
* Starting from the current modifier keys state, send the necessary key
|
|
* events (make or break) to end up with the NewModifierState passed in.
|
|
*
|
|
* Return value:
|
|
* None.
|
|
*
|
|
* History:
|
|
* 11 Feb 93 GregoryW Created.
|
|
\***************************************************************************/
|
|
VOID xxxUpdateModifierState(int NewModifierState, int NextProcIndex)
|
|
{
|
|
KE ke;
|
|
int CurrentModState;
|
|
int CurrentModBit, NewModBit;
|
|
int i;
|
|
|
|
CheckCritIn();
|
|
|
|
CurrentModState = gLockBits | gLatchBits;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(aModBit); i++) {
|
|
CurrentModBit = CurrentModState & aModBit[i].BitPosition;
|
|
NewModBit = NewModifierState & aModBit[i].BitPosition;
|
|
if (CurrentModBit != NewModBit) {
|
|
ke.bScanCode = (BYTE)aModBit[i].ScanCode;
|
|
ke.usFlaggedVk = aModBit[i].Vk;
|
|
if (CurrentModBit) { // if it's currently on, send break
|
|
ke.usFlaggedVk |= KBDBREAK;
|
|
}
|
|
if (AccessProceduresStream(&ke, 0L, NextProcIndex)) {
|
|
xxxProcessKeyEvent(&ke, 0L, FALSE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* xxxTurnOffStickyKeys
|
|
*
|
|
* The user either pressed the appropriate key sequence or used the
|
|
* access utility to turn StickyKeys off. Update modifier states and
|
|
* reset globals.
|
|
*
|
|
* Return value:
|
|
* None.
|
|
*
|
|
* History:
|
|
* 11 Feb 93 GregoryW Created.
|
|
\***************************************************************************/
|
|
VOID xxxTurnOffStickyKeys(VOID)
|
|
{
|
|
INT index;
|
|
|
|
CheckCritIn();
|
|
|
|
for (index = 0; index < ARRAY_SIZE(aAccessibilityProc); index++) {
|
|
if (aAccessibilityProc[index] == xxxStickyKeys) {
|
|
|
|
xxxUpdateModifierState(gPhysModifierState, index+1);
|
|
gLockBits = gLatchBits = 0;
|
|
CLEAR_ACCESSFLAG(StickyKeys, SKF_STICKYKEYSON);
|
|
|
|
PostAccessibility( ACCESS_STICKYKEYS );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* xxxUnlatchStickyKeys
|
|
*
|
|
* This routine releases any sticky keys that are latched. This routine
|
|
* is called during mouse up event processing.
|
|
*
|
|
* Return value:
|
|
* None.
|
|
*
|
|
* History:
|
|
* 21 Jun 93 GregoryW Created.
|
|
\***************************************************************************/
|
|
VOID xxxUnlatchStickyKeys(VOID)
|
|
{
|
|
INT index;
|
|
BOOL bChange;
|
|
|
|
if (!gLatchBits) {
|
|
return;
|
|
}
|
|
|
|
for (index = 0; index < ARRAY_SIZE(aAccessibilityProc); index++) {
|
|
if (aAccessibilityProc[index] == xxxStickyKeys) {
|
|
xxxUpdateModifierState(gLockBits, index+1);
|
|
bChange = gLatchBits != 0;
|
|
gLatchBits = 0;
|
|
|
|
if (bChange) {
|
|
|
|
PostAccessibility( ACCESS_STICKYKEYS );
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* xxxHardwareMouseKeyUp
|
|
*
|
|
* This routine is called during a mouse button up event. If MouseKeys is
|
|
* on and the button up event corresponds to a mouse key that's locked down,
|
|
* the mouse key must be released.
|
|
*
|
|
* If StickyKeys is on, all latched keys are released.
|
|
*
|
|
* Return value:
|
|
* None.
|
|
*
|
|
* History:
|
|
* 17 Jun 94 GregoryW Created.
|
|
\***************************************************************************/
|
|
|
|
VOID xxxHardwareMouseKeyUp(DWORD dwButton)
|
|
{
|
|
CheckCritIn();
|
|
|
|
if (TEST_ACCESSFLAG(MouseKeys, MKF_MOUSEKEYSON)) {
|
|
gwMKButtonState &= ~dwButton;
|
|
}
|
|
|
|
// Not required to post a setting change
|
|
//PostAccessibility( SPI_SETMOUSEKEYS );
|
|
|
|
if (TEST_ACCESSFLAG(StickyKeys, SKF_STICKYKEYSON)) {
|
|
xxxUnlatchStickyKeys();
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* xxxTwoKeysDown
|
|
*
|
|
* Two keys are down simultaneously. Check to see if StickyKeys should be
|
|
* turned off. In all cases update the modifier key state to reflect the
|
|
* physical key state and clear latched and locked modes.
|
|
*
|
|
* Return value:
|
|
* 1 if StickyKeys is enabled.
|
|
* 0 if StickyKeys is disabled.
|
|
*
|
|
* History:
|
|
* 11 Feb 93 GregoryW Created.
|
|
\***************************************************************************/
|
|
BOOL xxxTwoKeysDown(int NextProcIndex)
|
|
{
|
|
PTERMINAL pTerm = grpdeskRitInput->rpwinstaParent->pTerm;
|
|
|
|
if (TEST_ACCESSFLAG(StickyKeys, SKF_TWOKEYSOFF)) {
|
|
CLEAR_ACCESSFLAG(StickyKeys, SKF_STICKYKEYSON);
|
|
if (TEST_ACCESSFLAG(StickyKeys, SKF_HOTKEYSOUND)) {
|
|
PostRitSound(
|
|
pTerm,
|
|
RITSOUND_DOWNSIREN);
|
|
}
|
|
gStickyKeysLeftShiftCount = 0;
|
|
gStickyKeysRightShiftCount = 0;
|
|
}
|
|
xxxUpdateModifierState(gPhysModifierState, NextProcIndex);
|
|
gLockBits = gLatchBits = 0;
|
|
|
|
PostAccessibility( ACCESS_STICKYKEYS );
|
|
|
|
return TEST_BOOL_ACCESSFLAG(StickyKeys, SKF_STICKYKEYSON);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* SetGlobalCursorLevel
|
|
*
|
|
* Set the cursor level of all threads running on the visible
|
|
* windowstation.
|
|
*
|
|
* History:
|
|
* 04-17-95 JimA Created.
|
|
\***************************************************************************/
|
|
|
|
VOID SetGlobalCursorLevel(
|
|
INT iCursorLevel)
|
|
{
|
|
|
|
/*
|
|
* LATER
|
|
* We have other code which assumes that the
|
|
* iCursorLevel of a queue is the sum of the iCursorLevel values for the
|
|
* threads attached to the queue. But this code, if you set iCursorLevel to
|
|
* -1 (to indicate no mouse) will set the queue iCursorLevel to -1, no matter
|
|
* how many threads are attached to the queue. This needs to be revisited.
|
|
* See the function AttachToQueue.
|
|
* FritzS
|
|
*/
|
|
|
|
|
|
PDESKTOP pdesk;
|
|
PTHREADINFO pti;
|
|
PLIST_ENTRY pHead, pEntry;
|
|
|
|
TAGMSG1(DBGTAG_PNP, "SetGlobalCursorLevel %x", iCursorLevel);
|
|
|
|
if (grpdeskRitInput) {
|
|
for (pdesk = grpdeskRitInput->rpwinstaParent->rpdeskList;
|
|
pdesk != NULL; pdesk = pdesk->rpdeskNext) {
|
|
|
|
pHead = &pdesk->PtiList;
|
|
for (pEntry = pHead->Flink; pEntry != pHead; pEntry = pEntry->Flink) {
|
|
pti = CONTAINING_RECORD(pEntry, THREADINFO, PtiLink);
|
|
|
|
pti->iCursorLevel = iCursorLevel;
|
|
pti->pq->iCursorLevel = iCursorLevel;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* CSRSS doesn't seem to be on the list, so fix it up now.
|
|
*/
|
|
for (pti = PpiFromProcess(gpepCSRSS)->ptiList;
|
|
pti != NULL; pti = pti->ptiSibling) {
|
|
if (pti->iCursorLevel != iCursorLevel) {
|
|
TAGMSG3(DBGTAG_PNP, "pti %#p has cursorlevel %x, should be %x",
|
|
pti, pti->iCursorLevel, iCursorLevel);
|
|
}
|
|
if (pti->pq->iCursorLevel != iCursorLevel) {
|
|
TAGMSG4(DBGTAG_PNP, "pti->pq %#p->%#p has cursorlevel %x, should be %x",
|
|
pti, pti->pq, pti->pq->iCursorLevel, iCursorLevel);
|
|
}
|
|
pti->iCursorLevel = iCursorLevel;
|
|
pti->pq->iCursorLevel = iCursorLevel;
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* MKShowMouseCursor
|
|
*
|
|
* If no hardware mouse is installed and MouseKeys is enabled, we need
|
|
* to fix up the system metrics, the oem information and the queue
|
|
* information. The mouse cursor then gets displayed.
|
|
*
|
|
* Return value:
|
|
* None.
|
|
*
|
|
* History:
|
|
* 11 Feb 93 GregoryW Created.
|
|
\***************************************************************************/
|
|
VOID MKShowMouseCursor()
|
|
{
|
|
TAGMSG1(DBGTAG_PNP, "MKShowMouseCursor (gpDeviceInfoList == %#p)", gpDeviceInfoList);
|
|
|
|
//
|
|
// If TEST_GTERMF(GTERMF_MOUSE) is TRUE then we either have a hardware mouse
|
|
// or we're already pretending a mouse is installed. In either case,
|
|
// there's nothing to do so just return.
|
|
//
|
|
if (TEST_GTERMF(GTERMF_MOUSE)) {
|
|
TAGMSG0(DBGTAG_PNP, "MKShowMouseCursor just returns");
|
|
return;
|
|
}
|
|
|
|
SET_GTERMF(GTERMF_MOUSE);
|
|
SET_ACCF(ACCF_MKVIRTUALMOUSE);
|
|
SYSMET(MOUSEPRESENT) = TRUE;
|
|
SYSMET(CMOUSEBUTTONS) = 2;
|
|
/*
|
|
* HACK: CreateQueue() uses oemInfo.fMouse to determine if a mouse is
|
|
* present and thus whether to set the iCursorLevel field in the
|
|
* THREADINFO structure to 0 or -1. Unfortunately some queues have
|
|
* already been created at this point. Since oemInfo.fMouse is
|
|
* initialized to FALSE, we need to go back through any queues already
|
|
* around and set their iCursorLevel field to the correct value when
|
|
* mousekeys is enabled.
|
|
*/
|
|
SetGlobalCursorLevel(0);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* MKHideMouseCursor
|
|
*
|
|
* If no hardware mouse is installed and MouseKeys is disabled, we need
|
|
* to fix up the system metrics, the oem information and the queue
|
|
* information. The mouse cursor then disappears.
|
|
*
|
|
* Return value:
|
|
* None.
|
|
*
|
|
* History:
|
|
* 11 Feb 93 GregoryW Created.
|
|
\***************************************************************************/
|
|
VOID MKHideMouseCursor()
|
|
{
|
|
TAGMSG1(DBGTAG_PNP, "MKHideMouseCursor (gpDeviceInfoList == %#p)", gpDeviceInfoList);
|
|
|
|
//
|
|
// If a hardware mouse is present we don't need to do anything.
|
|
//
|
|
if (!TEST_ACCF(ACCF_MKVIRTUALMOUSE)) {
|
|
return;
|
|
}
|
|
|
|
CLEAR_ACCF(ACCF_MKVIRTUALMOUSE);
|
|
CLEAR_GTERMF(GTERMF_MOUSE);
|
|
SYSMET(MOUSEPRESENT) = FALSE;
|
|
SYSMET(CMOUSEBUTTONS) = 0;
|
|
|
|
SetGlobalCursorLevel(-1);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* xxxMKToggleMouseKeys
|
|
*
|
|
* This routine is called when the NumLock key is pressed and MouseKeys is
|
|
* active. If the left shift key and the left alt key are down then MouseKeys
|
|
* is turned off. If just the NumLock key is pressed then we toggle between
|
|
* MouseKeys active and the state of the number pad before MouseKeys was
|
|
* activated.
|
|
*
|
|
* Return value:
|
|
* TRUE - key should be passed on in the input stream.
|
|
* FALSE - key should not be passed on.
|
|
*
|
|
* History:
|
|
\***************************************************************************/
|
|
BOOL xxxMKToggleMouseKeys(
|
|
USHORT NotUsed)
|
|
{
|
|
BOOL bRetVal = TRUE;
|
|
BOOL bNewPassThrough;
|
|
PTERMINAL pTerm = grpdeskRitInput->rpwinstaParent->pTerm;
|
|
|
|
UNREFERENCED_PARAMETER(NotUsed);
|
|
|
|
//
|
|
// If this is a typematic repeat of NumLock we just pass it on.
|
|
//
|
|
if (TEST_ACCF(ACCF_MKREPEATVK)) {
|
|
return bRetVal;
|
|
}
|
|
//
|
|
// This is a make of NumLock. Check for disable sequence.
|
|
//
|
|
if ((gLockBits | gLatchBits | gPhysModifierState) == MOUSEKEYMODBITS) {
|
|
if (TEST_ACCESSFLAG(MouseKeys, MKF_HOTKEYACTIVE)) {
|
|
if (!gbMKMouseMode) {
|
|
//
|
|
// User wants to turn MouseKeys off. If we're currently in
|
|
// pass through mode then the NumLock key is in the same state
|
|
// (on or off) as it was when the user invoked MouseKeys. We
|
|
// want to leave it in that state, so don't pass the NumLock
|
|
// key on.
|
|
//
|
|
bRetVal = FALSE;
|
|
}
|
|
TurnOffMouseKeys();
|
|
}
|
|
return bRetVal;
|
|
}
|
|
/*
|
|
* This is a NumLock with no modifiers. Toggle current state and
|
|
* provide audible feedback.
|
|
*
|
|
* Note -- this test is the reverse of other ones because it tests the
|
|
* state of VK_NUMLOCK before the keypress flips the state of NUMLOCK.
|
|
* So the code checks for what the state will be.
|
|
*/
|
|
bNewPassThrough =
|
|
#ifdef FE_SB // MouseKeys()
|
|
(TestAsyncKeyStateToggle(gNumLockVk) != 0) ^
|
|
#else // FE_SB
|
|
(TestAsyncKeyStateToggle(VK_NUMLOCK) != 0) ^
|
|
#endif // FE_SB
|
|
(TEST_ACCESSFLAG(MouseKeys, MKF_REPLACENUMBERS) != 0);
|
|
|
|
|
|
if (!bNewPassThrough) {
|
|
gbMKMouseMode = TRUE;
|
|
PostRitSound(
|
|
pTerm,
|
|
RITSOUND_HIGHBEEP);
|
|
} else {
|
|
WORD SaveCurrentActiveButton;
|
|
//
|
|
// User wants keys to be passed on. Release all buttons currently
|
|
// down.
|
|
//
|
|
gbMKMouseMode = FALSE;
|
|
PostRitSound(
|
|
pTerm,
|
|
RITSOUND_LOWBEEP);
|
|
SaveCurrentActiveButton = gwMKCurrentButton;
|
|
gwMKCurrentButton = MOUSE_BUTTON_LEFT | MOUSE_BUTTON_RIGHT;
|
|
xxxMKButtonSetState(TRUE);
|
|
gwMKCurrentButton = SaveCurrentActiveButton;
|
|
}
|
|
|
|
PostAccessibility( ACCESS_MOUSEKEYS );
|
|
|
|
return bRetVal;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* xxxMKButtonClick
|
|
*
|
|
* Click the active mouse button.
|
|
*
|
|
* Return value:
|
|
* Always FALSE - key should not be passed on.
|
|
*
|
|
* History:
|
|
\***************************************************************************/
|
|
BOOL xxxMKButtonClick(USHORT NotUsed)
|
|
{
|
|
UNREFERENCED_PARAMETER(NotUsed);
|
|
|
|
//
|
|
// The button click only happens on initial make of key. If this is a
|
|
// typematic repeat we just ignore it.
|
|
//
|
|
if (TEST_ACCF(ACCF_MKREPEATVK)) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Ensure active button is UP before the click
|
|
//
|
|
xxxMKButtonSetState(TRUE);
|
|
|
|
//
|
|
// Now push the button DOWN
|
|
//
|
|
xxxMKButtonSetState(FALSE);
|
|
|
|
//
|
|
// Now release the button
|
|
//
|
|
xxxMKButtonSetState(TRUE);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* xxxMKMoveConstCursorTimer
|
|
*
|
|
* Timer routine that handles constant speed mouse movement. This routine
|
|
* is called 20 times per second and uses information from
|
|
* gMouseCursor.bConstantTable[] to determine how many pixels to move the
|
|
* mouse cursor on each tick.
|
|
*
|
|
* Return value:
|
|
* None.
|
|
*
|
|
* History:
|
|
\***************************************************************************/
|
|
VOID xxxMKMoveConstCursorTimer(
|
|
PWND pwnd,
|
|
UINT message,
|
|
UINT_PTR nID,
|
|
LPARAM lParam)
|
|
{
|
|
LONG MovePixels;
|
|
|
|
UNREFERENCED_PARAMETER(pwnd);
|
|
UNREFERENCED_PARAMETER(lParam);
|
|
UNREFERENCED_PARAMETER(nID);
|
|
UNREFERENCED_PARAMETER(message);
|
|
|
|
CheckCritIn();
|
|
|
|
if (TEST_ACCESSFLAG(MouseKeys, MKF_MODIFIERS)) {
|
|
if ((gLockBits | gLatchBits | gPhysModifierState) & LRSHIFT) {
|
|
MovePixels = 1;
|
|
goto MoveIt;
|
|
}
|
|
if ((gLockBits | gLatchBits | gPhysModifierState) & LRCONTROL) {
|
|
MovePixels = gMouseCursor.bConstantTable[0] * MK_CONTROL_SPEED;
|
|
goto MoveIt;
|
|
}
|
|
}
|
|
|
|
giMouseMoveTable %= gMouseCursor.bConstantTableLen;
|
|
|
|
MovePixels = gMouseCursor.bConstantTable[giMouseMoveTable++];
|
|
|
|
if (MovePixels == 0) {
|
|
return;
|
|
}
|
|
|
|
MoveIt:
|
|
//
|
|
// We're inside the critical section - leave before calling MoveEvent.
|
|
// Set gbMouseMoved to TRUE so RawInputThread wakes up the appropriate
|
|
// user thread (if any) to receive this event.
|
|
//
|
|
LeaveCrit();
|
|
|
|
xxxMoveEvent(MovePixels * gMKDeltaX, MovePixels * gMKDeltaY, 0, 0,
|
|
#ifdef GENERIC_INPUT
|
|
NULL,
|
|
NULL,
|
|
#endif
|
|
0, FALSE);
|
|
QueueMouseEvent(0, 0, 0, gptCursorAsync, NtGetTickCount(),
|
|
#ifdef GENERIC_INPUT
|
|
/*
|
|
* There's no real mouse related to this mouse message.
|
|
*/
|
|
NULL,
|
|
NULL,
|
|
#endif
|
|
FALSE, TRUE);
|
|
EnterCrit();
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* xxxMKMoveAccelCursorTimer
|
|
*
|
|
* Timer routine that handles mouse acceleration. It gets called 20 times
|
|
* per second and uses information from gMouseCursor.bAccelTable[] to determine
|
|
* how many pixels to move the mouse cursor on each tick.
|
|
*
|
|
* Return value:
|
|
* None.
|
|
*
|
|
* History:
|
|
\***************************************************************************/
|
|
VOID xxxMKMoveAccelCursorTimer(
|
|
PWND pwnd,
|
|
UINT message,
|
|
UINT_PTR nID,
|
|
LPARAM lParam)
|
|
{
|
|
LONG MovePixels;
|
|
|
|
UNREFERENCED_PARAMETER(pwnd);
|
|
UNREFERENCED_PARAMETER(message);
|
|
UNREFERENCED_PARAMETER(nID);
|
|
UNREFERENCED_PARAMETER(lParam);
|
|
|
|
CheckCritIn();
|
|
|
|
if (TEST_ACCESSFLAG(MouseKeys, MKF_MODIFIERS)) {
|
|
if ((gLockBits | gLatchBits | gPhysModifierState) & LRSHIFT) {
|
|
MovePixels = 1;
|
|
goto MoveIt;
|
|
}
|
|
if ((gLockBits | gLatchBits | gPhysModifierState) & LRCONTROL) {
|
|
MovePixels = gMouseCursor.bConstantTable[0] * MK_CONTROL_SPEED;
|
|
goto MoveIt;
|
|
}
|
|
}
|
|
|
|
if (giMouseMoveTable < gMouseCursor.bAccelTableLen) {
|
|
MovePixels = gMouseCursor.bAccelTable[giMouseMoveTable++];
|
|
} else {
|
|
//
|
|
// We've reached maximum cruising speed. Switch to constant table.
|
|
//
|
|
MovePixels = gMouseCursor.bConstantTable[0];
|
|
giMouseMoveTable = 1;
|
|
gtmridMKMoveCursor = InternalSetTimer(NULL,
|
|
gtmridMKMoveCursor,
|
|
MOUSETIMERRATE,
|
|
xxxMKMoveConstCursorTimer,
|
|
TMRF_RIT);
|
|
|
|
}
|
|
if (MovePixels == 0) {
|
|
return;
|
|
}
|
|
|
|
MoveIt:
|
|
//
|
|
// We're inside the critical section - leave before calling xxxMoveEvent.
|
|
// Set gbMouseMoved to TRUE so RawInputThread wakes up the appropriate
|
|
// user thread (if any) to receive this event.
|
|
//
|
|
LeaveCrit();
|
|
xxxMoveEvent(MovePixels * gMKDeltaX, MovePixels * gMKDeltaY, 0, 0,
|
|
#ifdef GENERIC_INPUT
|
|
NULL,
|
|
NULL,
|
|
#endif
|
|
0, FALSE);
|
|
QueueMouseEvent(0, 0, 0, gptCursorAsync, NtGetTickCount(),
|
|
#ifdef GENERIC_INPUT
|
|
NULL,
|
|
NULL,
|
|
#endif
|
|
FALSE, TRUE);
|
|
|
|
EnterCrit();
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* xxxMKMouseMove
|
|
*
|
|
* Send a mouse move event. A timer routine is set to handle the mouse
|
|
* cursor acceleration. The timer will be set on the first make of a
|
|
* mouse move key if FilterKeys repeat rate is OFF. Otherwise, the timer
|
|
* is set on the first repeat (typematic make) of the mouse move key.
|
|
* Once the timer is set the timer routine handles all mouse movement
|
|
* until the key is released or a new key is pressed.
|
|
*
|
|
* Return value:
|
|
* Always FALSE - key should not be passed on.
|
|
*
|
|
* History:
|
|
\***************************************************************************/
|
|
BOOL xxxMKMouseMove(USHORT Data)
|
|
{
|
|
|
|
|
|
/*
|
|
* Let the mouse acceleration timer routine handle repeats.
|
|
*/
|
|
if (TEST_ACCF(ACCF_MKREPEATVK) && (gtmridMKMoveCursor != 0)) {
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
gMKDeltaX = (LONG)((CHAR)LOBYTE(Data)); // Force sign extension
|
|
gMKDeltaY = (LONG)((CHAR)HIBYTE(Data)); // Force sign extension
|
|
|
|
LeaveCrit();
|
|
|
|
if ((TEST_ACCESSFLAG(MouseKeys, MKF_MODIFIERS) && ((gLockBits | gLatchBits | gPhysModifierState) & LRCONTROL))) {
|
|
xxxMoveEvent(gMKDeltaX * MK_CONTROL_SPEED * gMouseCursor.bConstantTable[0], gMKDeltaY * MK_CONTROL_SPEED * gMouseCursor.bConstantTable[0], 0, 0,
|
|
#ifdef GENERIC_INPUT
|
|
NULL,
|
|
NULL,
|
|
#endif
|
|
0, FALSE);
|
|
} else {
|
|
xxxMoveEvent(gMKDeltaX, gMKDeltaY, 0, 0,
|
|
#ifdef GENERIC_INPUT
|
|
NULL,
|
|
NULL,
|
|
#endif
|
|
0, FALSE);
|
|
}
|
|
|
|
QueueMouseEvent(0, 0, 0, gptCursorAsync, NtGetTickCount(),
|
|
#ifdef GENERIC_INPUT
|
|
NULL,
|
|
NULL,
|
|
#endif
|
|
FALSE, TRUE);
|
|
|
|
EnterCrit();
|
|
|
|
/*
|
|
* If the repeat rate is zero we'll start the mouse acceleration
|
|
* immediately. Otherwise we wait until after the first repeat
|
|
* of the mouse movement key.
|
|
*/
|
|
if (!gFilterKeys.iRepeatMSec || TEST_ACCF(ACCF_MKREPEATVK)) {
|
|
giMouseMoveTable = 0;
|
|
gtmridMKMoveCursor = InternalSetTimer(NULL,
|
|
gtmridMKMoveCursor,
|
|
MOUSETIMERRATE,
|
|
(gMouseCursor.bAccelTableLen) ?
|
|
xxxMKMoveAccelCursorTimer :
|
|
xxxMKMoveConstCursorTimer,
|
|
TMRF_RIT);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* xxxMKButtonSetState
|
|
*
|
|
* Set the active mouse button(s) to the state specified by fButtonUp
|
|
* (if fButtonUp is TRUE then the button is released, o.w. the button
|
|
* is pressed).
|
|
*
|
|
* Return value:
|
|
* Always FALSE - key should not be passed on.
|
|
*
|
|
* History:
|
|
\***************************************************************************/
|
|
BOOL xxxMKButtonSetState(USHORT fButtonUp)
|
|
{
|
|
WORD NewButtonState;
|
|
|
|
CheckCritIn();
|
|
if (fButtonUp) {
|
|
NewButtonState = gwMKButtonState & ~gwMKCurrentButton;
|
|
} else {
|
|
NewButtonState = gwMKButtonState | gwMKCurrentButton;
|
|
}
|
|
|
|
if ((NewButtonState & MOUSE_BUTTON_LEFT) != (gwMKButtonState & MOUSE_BUTTON_LEFT)) {
|
|
xxxButtonEvent(MOUSE_BUTTON_LEFT,
|
|
gptCursorAsync,
|
|
fButtonUp,
|
|
NtGetTickCount(),
|
|
0L,
|
|
#ifdef GENERIC_INPUT
|
|
NULL,
|
|
NULL,
|
|
#endif
|
|
FALSE,
|
|
FALSE);
|
|
}
|
|
if ((NewButtonState & MOUSE_BUTTON_RIGHT) != (gwMKButtonState & MOUSE_BUTTON_RIGHT)) {
|
|
xxxButtonEvent(MOUSE_BUTTON_RIGHT,
|
|
gptCursorAsync,
|
|
fButtonUp,
|
|
NtGetTickCount(),
|
|
0L,
|
|
#ifdef GENERIC_INPUT
|
|
NULL,
|
|
NULL,
|
|
#endif
|
|
FALSE,
|
|
FALSE);
|
|
}
|
|
gwMKButtonState = NewButtonState;
|
|
|
|
PostAccessibility( ACCESS_MOUSEKEYS );
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* MKButtonSelect
|
|
*
|
|
* Mark ThisButton as the active mouse button. It's possible to select both
|
|
* the left and right mouse buttons as active simultaneously.
|
|
*
|
|
* Return value:
|
|
* Always FALSE - key should not be passed on.
|
|
*
|
|
* History:
|
|
\***************************************************************************/
|
|
BOOL MKButtonSelect(WORD ThisButton)
|
|
{
|
|
gwMKCurrentButton = ThisButton;
|
|
|
|
PostAccessibility( ACCESS_MOUSEKEYS );
|
|
return FALSE;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* xxxMKButtonDoubleClick
|
|
*
|
|
* Double click the active mouse button.
|
|
*
|
|
* Return value:
|
|
* Always FALSE - key should not be passed on.
|
|
*
|
|
* History:
|
|
\***************************************************************************/
|
|
BOOL xxxMKButtonDoubleClick(
|
|
USHORT NotUsed)
|
|
{
|
|
UNREFERENCED_PARAMETER(NotUsed);
|
|
|
|
xxxMKButtonClick(0);
|
|
xxxMKButtonClick(0);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL HighContrastHotKey(
|
|
PKE pKeyEvent,
|
|
ULONG ExtraInformation,
|
|
int NotUsed)
|
|
{
|
|
int CurrentModState;
|
|
int fBreak;
|
|
BYTE Vk;
|
|
|
|
UNREFERENCED_PARAMETER(NotUsed);
|
|
UNREFERENCED_PARAMETER(ExtraInformation);
|
|
|
|
CheckCritIn();
|
|
|
|
Vk = (BYTE)(pKeyEvent->usFlaggedVk & 0xff);
|
|
fBreak = pKeyEvent->usFlaggedVk & KBDBREAK;
|
|
CurrentModState = gLockBits | gLatchBits | gPhysModifierState;
|
|
|
|
if (!TEST_ACCESSFLAG(HighContrast, HCF_HIGHCONTRASTON)) {
|
|
if (TEST_ACCESSFLAG(HighContrast, HCF_HOTKEYACTIVE) && Vk == VK_SNAPSHOT && !fBreak && CurrentModState == MOUSEKEYMODBITS) {
|
|
|
|
if (TEST_ACCESSFLAG(HighContrast, MKF_HOTKEYSOUND)) {
|
|
PTERMINAL pTerm = grpdeskRitInput->rpwinstaParent->pTerm;
|
|
PostRitSound(
|
|
pTerm,
|
|
RITSOUND_UPSIREN);
|
|
}
|
|
|
|
PostAccessNotification(ACCESS_HIGHCONTRAST);
|
|
|
|
return FALSE;
|
|
}
|
|
} else {
|
|
if (TEST_ACCESSFLAG(HighContrast, HCF_HOTKEYACTIVE) && Vk == VK_SNAPSHOT && !fBreak && CurrentModState == MOUSEKEYMODBITS) {
|
|
|
|
CLEAR_ACCESSFLAG(HighContrast, HCF_HIGHCONTRASTON);
|
|
|
|
if (TEST_ACCESSFLAG(HighContrast, MKF_HOTKEYSOUND)) {
|
|
PTERMINAL pTerm = grpdeskRitInput->rpwinstaParent->pTerm;
|
|
PostRitSound(
|
|
pTerm,
|
|
RITSOUND_DOWNSIREN);
|
|
}
|
|
|
|
if (gspwndLogonNotify != NULL) {
|
|
|
|
_PostMessage(gspwndLogonNotify, WM_LOGONNOTIFY,
|
|
LOGON_ACCESSNOTIFY, ACCESS_HIGHCONTRASTOFF);
|
|
}
|
|
}
|
|
}
|
|
|
|
return TRUE; // send key event to next accessibility routine.
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* MouseKeys
|
|
*
|
|
* This is the strategy routine that gets called as part of the input stream
|
|
* processing. MouseKeys enabling/disabling is handled here. All MouseKeys
|
|
* helper routines are called from this routine.
|
|
*
|
|
* Return value:
|
|
* TRUE - key event should be passed on to the next access routine.
|
|
* FALSE - key event was processed and should not be passed on.
|
|
*
|
|
* History:
|
|
\***************************************************************************/
|
|
BOOL MouseKeys(
|
|
PKE pKeyEvent,
|
|
ULONG ExtraInformation,
|
|
int NotUsed)
|
|
{
|
|
int CurrentModState;
|
|
int fBreak;
|
|
BYTE Vk;
|
|
USHORT FlaggedVk;
|
|
int i;
|
|
|
|
UNREFERENCED_PARAMETER(ExtraInformation);
|
|
UNREFERENCED_PARAMETER(NotUsed);
|
|
|
|
CheckCritIn();
|
|
Vk = (BYTE)(pKeyEvent->usFlaggedVk & 0xff);
|
|
fBreak = pKeyEvent->usFlaggedVk & KBDBREAK;
|
|
CurrentModState = gLockBits | gLatchBits | gPhysModifierState;
|
|
|
|
if (!TEST_ACCESSFLAG(MouseKeys, MKF_MOUSEKEYSON)) {
|
|
//
|
|
// MouseKeys currently disabled. Check for enabling sequence:
|
|
// left Shift + left Alt + Num Lock.
|
|
//
|
|
#ifdef FE_SB // MouseKeys()
|
|
if (TEST_ACCESSFLAG(MouseKeys, MKF_HOTKEYACTIVE) && Vk == gNumLockVk && !fBreak && CurrentModState == MOUSEKEYMODBITS) {
|
|
#else // FE_SB
|
|
if (TEST_ACCESSFLAG(MouseKeys, MKF_HOTKEYACTIVE) && Vk == VK_NUMLOCK && !fBreak && CurrentModState == MOUSEKEYMODBITS) {
|
|
#endif // FE_SB
|
|
gMKPreviousVk = Vk;
|
|
if (TEST_ACCESSFLAG(MouseKeys, MKF_HOTKEYSOUND)) {
|
|
PTERMINAL pTerm = grpdeskRitInput->rpwinstaParent->pTerm;
|
|
PostRitSound(
|
|
pTerm,
|
|
RITSOUND_UPSIREN);
|
|
}
|
|
PostAccessNotification(ACCESS_MOUSEKEYS);
|
|
|
|
return FALSE;
|
|
}
|
|
} else {
|
|
//
|
|
// Is this a MouseKey key?
|
|
//
|
|
//
|
|
FlaggedVk = Vk | (pKeyEvent->usFlaggedVk & KBDEXT);
|
|
for (i = 0; i < cMouseVKeys; i++) {
|
|
#ifdef FE_SB // MouseKeys()
|
|
if (FlaggedVk == gpusMouseVKey[i]) {
|
|
#else // FE_SB
|
|
if (FlaggedVk == ausMouseVKey[i]) {
|
|
#endif // FE_SB
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i == cMouseVKeys) {
|
|
return TRUE; // not a mousekey
|
|
}
|
|
//
|
|
// Check to see if we should pass on key events until Num Lock is
|
|
// entered.
|
|
//
|
|
|
|
if (!gbMKMouseMode) {
|
|
#ifdef FE_SB // MouseKeys()
|
|
if (Vk != gNumLockVk) {
|
|
#else // FE_SB
|
|
if (Vk != VK_NUMLOCK) {
|
|
#endif // FE_SB
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check for Ctrl-Alt-Numpad Del. Pass key event on if sequence
|
|
// detected.
|
|
//
|
|
if (Vk == VK_DELETE && CurrentModState & LRALT && CurrentModState & LRCONTROL) {
|
|
return TRUE;
|
|
}
|
|
if (fBreak) {
|
|
//
|
|
// If this is a break of the key that we're accelerating then
|
|
// kill the timer.
|
|
//
|
|
if (gMKPreviousVk == Vk) {
|
|
if (gtmridMKMoveCursor != 0) {
|
|
KILLRITTIMER(NULL, gtmridMKMoveCursor);
|
|
gtmridMKMoveCursor = 0;
|
|
}
|
|
CLEAR_ACCF(ACCF_MKREPEATVK);
|
|
gMKPreviousVk = 0;
|
|
}
|
|
//
|
|
// Pass break of Numlock along. Other mousekeys stop here.
|
|
//
|
|
#ifdef FE_SB // MouseKeys()
|
|
if (Vk == gNumLockVk) {
|
|
#else // FE_SB
|
|
if (Vk == VK_NUMLOCK) {
|
|
#endif // FE_SB
|
|
return TRUE;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
} else {
|
|
SET_OR_CLEAR_ACCF(ACCF_MKREPEATVK,
|
|
(gMKPreviousVk == Vk));
|
|
//
|
|
// If this is not a typematic repeat, kill the mouse acceleration
|
|
// timer.
|
|
//
|
|
if ((!TEST_ACCF(ACCF_MKREPEATVK)) && (gtmridMKMoveCursor)) {
|
|
KILLRITTIMER(NULL, gtmridMKMoveCursor);
|
|
gtmridMKMoveCursor = 0;
|
|
}
|
|
gMKPreviousVk = Vk;
|
|
}
|
|
return aMouseKeyEvent[i](ausMouseKeyData[i]);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* TurnOffMouseKeys
|
|
*
|
|
* Return value:
|
|
* None.
|
|
*
|
|
* History:
|
|
* 11 Feb 93 GregoryW Created.
|
|
\***************************************************************************/
|
|
VOID TurnOffMouseKeys(VOID)
|
|
{
|
|
CLEAR_ACCESSFLAG(MouseKeys, MKF_MOUSEKEYSON);
|
|
// gMKPassThrough = 0;
|
|
CLEAR_ACCF(ACCF_MKREPEATVK);
|
|
MKHideMouseCursor();
|
|
if (TEST_ACCESSFLAG(MouseKeys, MKF_HOTKEYSOUND)) {
|
|
PostRitSound(
|
|
grpdeskRitInput->rpwinstaParent->pTerm,
|
|
RITSOUND_DOWNSIREN);
|
|
}
|
|
PostAccessibility( ACCESS_MOUSEKEYS );
|
|
}
|
|
|
|
|
|
/*
|
|
* Let's assert at the compile time if those values are
|
|
* defined unexpectedly.
|
|
*/
|
|
#if (MAXSPEED_MIN >= MAXSPEED_MAX) || (MAXSPEED_MIN <= 0) || (TIMETOMAXSPEED_MIN >= TIMETOMAXSPEED_MAX) || (TIMETOMAXSPEED_MIN <= 0)
|
|
#error The mousekey min/max values are not as expected.
|
|
#endif
|
|
|
|
/***************************************************************************\
|
|
* CalculateMouseTable
|
|
*
|
|
* Set mouse table based on time to max speed and max speed. This routine
|
|
* is called during user logon (after the registry entries for the access
|
|
* features are read).
|
|
*
|
|
* Return value:
|
|
* None.
|
|
*
|
|
* History:
|
|
* Taken from access utility.
|
|
*
|
|
****************************************************************************/
|
|
|
|
VOID CalculateMouseTable(VOID)
|
|
{
|
|
long Total_Distance; /* in 1000th of pixel */
|
|
|
|
long Accel_Per_Tick; /* in 1000th of pixel/tick */
|
|
long Current_Speed; /* in 1000th of pixel/tick */
|
|
long Max_Speed; /* in 1000th of pixel/tick */
|
|
long Real_Total_Distance; /* in pixels */
|
|
long Real_Delta_Distance; /* in pixels */
|
|
int i;
|
|
int Num_Constant_Table,Num_Accel_Table;
|
|
|
|
UserAssert(gMouseKeys.iMaxSpeed >= MAXSPEED_MIN && gMouseKeys.iMaxSpeed <= MAXSPEED_MAX);
|
|
UserAssert(gMouseKeys.iTimeToMaxSpeed >= TIMETOMAXSPEED_MIN && gMouseKeys.iTimeToMaxSpeed <= TIMETOMAXSPEED_MAX);
|
|
UserAssert(gMouseKeys.iTimeToMaxSpeed != 0);
|
|
|
|
Max_Speed = gMouseKeys.iMaxSpeed;
|
|
Max_Speed *= 1000 / MOUSETICKS;
|
|
|
|
Accel_Per_Tick = Max_Speed * 1000 / (gMouseKeys.iTimeToMaxSpeed * MOUSETICKS);
|
|
Current_Speed = 0;
|
|
Total_Distance = 0;
|
|
Real_Total_Distance = 0;
|
|
Num_Constant_Table = 0;
|
|
Num_Accel_Table = 0;
|
|
|
|
for(i=0; i<= 255; i++) {
|
|
Current_Speed = Current_Speed + Accel_Per_Tick;
|
|
if (Current_Speed > Max_Speed) {
|
|
Current_Speed = Max_Speed;
|
|
}
|
|
Total_Distance += Current_Speed;
|
|
|
|
//
|
|
// Calculate how many pixels to move on this tick
|
|
//
|
|
Real_Delta_Distance = ((Total_Distance - (Real_Total_Distance * 1000)) + 500) / 1000 ;
|
|
//
|
|
// Calculate total distance moved up to this point
|
|
//
|
|
Real_Total_Distance = Real_Total_Distance + Real_Delta_Distance;
|
|
|
|
if ((Current_Speed < Max_Speed) && (Num_Accel_Table < 128)) {
|
|
gMouseCursor.bAccelTable[Num_Accel_Table++] = (BYTE)Real_Delta_Distance;
|
|
}
|
|
|
|
if ((Current_Speed == Max_Speed) && (Num_Constant_Table < 128)) {
|
|
gMouseCursor.bConstantTable[Num_Constant_Table++] = (BYTE)Real_Delta_Distance;
|
|
}
|
|
|
|
}
|
|
gMouseCursor.bAccelTableLen = (BYTE)Num_Accel_Table;
|
|
gMouseCursor.bConstantTableLen = (BYTE)Num_Constant_Table;
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* xxxToggleKeysTimer
|
|
*
|
|
* Enable ToggleKeys if it is currently disabled. Disable ToggleKeys if it
|
|
* is currently enabled.
|
|
*
|
|
* This routine is called only when the NumLock key is held down for 5 seconds.
|
|
*
|
|
* Return value:
|
|
* 0
|
|
*
|
|
* History:
|
|
* 11 Feb 93 GregoryW Created.
|
|
\***************************************************************************/
|
|
VOID xxxToggleKeysTimer(
|
|
PWND pwnd,
|
|
UINT message,
|
|
UINT_PTR nID,
|
|
LPARAM lParam)
|
|
{
|
|
KE ToggleKeyEvent;
|
|
PTERMINAL pTerm = grpdeskRitInput->rpwinstaParent->pTerm;
|
|
|
|
UNREFERENCED_PARAMETER(pwnd);
|
|
UNREFERENCED_PARAMETER(message);
|
|
UNREFERENCED_PARAMETER(nID);
|
|
UNREFERENCED_PARAMETER(lParam);
|
|
|
|
CheckCritIn();
|
|
//
|
|
// Toggle ToggleKeys and provide audible feedback if appropriate.
|
|
//
|
|
if (TEST_ACCESSFLAG(ToggleKeys, TKF_TOGGLEKEYSON)) {
|
|
CLEAR_ACCESSFLAG(ToggleKeys, TKF_TOGGLEKEYSON);
|
|
if (TEST_ACCESSFLAG(ToggleKeys, TKF_HOTKEYSOUND)) {
|
|
PostRitSound(
|
|
pTerm,
|
|
RITSOUND_DOWNSIREN);
|
|
}
|
|
} else {
|
|
if (TEST_ACCESSFLAG(ToggleKeys, TKF_HOTKEYSOUND)) {
|
|
PostRitSound(
|
|
pTerm,
|
|
RITSOUND_UPSIREN);
|
|
}
|
|
|
|
PostAccessNotification(ACCESS_TOGGLEKEYS);
|
|
}
|
|
//
|
|
// Send a fake break/make combination so state of numlock key remains
|
|
// the same as it was before user pressed it to activate/deactivate
|
|
// ToggleKeys.
|
|
//
|
|
ToggleKeyEvent.bScanCode = gTKScanCode;
|
|
#ifdef FE_SB // ToggleKeysTimer()
|
|
ToggleKeyEvent.usFlaggedVk = gNumLockVk | KBDBREAK;
|
|
#else
|
|
ToggleKeyEvent.usFlaggedVk = VK_NUMLOCK | KBDBREAK;
|
|
#endif // FE_SB
|
|
if (AccessProceduresStream(&ToggleKeyEvent, gTKExtraInformation, gTKNextProcIndex)) {
|
|
xxxProcessKeyEvent(&ToggleKeyEvent, gTKExtraInformation, FALSE);
|
|
}
|
|
#ifdef FE_SB // ToggleKeysTimer()
|
|
ToggleKeyEvent.usFlaggedVk = gNumLockVk;
|
|
#else
|
|
ToggleKeyEvent.usFlaggedVk = VK_NUMLOCK;
|
|
#endif // FE_SB
|
|
if (AccessProceduresStream(&ToggleKeyEvent, gTKExtraInformation, gTKNextProcIndex)) {
|
|
xxxProcessKeyEvent(&ToggleKeyEvent, gTKExtraInformation, FALSE);
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* ToggleKeys
|
|
*
|
|
* This is the strategy routine that gets called as part of the input stream
|
|
* processing. Keys of interest are Num Lock, Scroll Lock and Caps Lock.
|
|
*
|
|
* Return value:
|
|
* TRUE - key event should be passed on to the next access routine.
|
|
* FALSE - key event was processed and should not be passed on.
|
|
*
|
|
* History:
|
|
\***************************************************************************/
|
|
BOOL ToggleKeys(PKE pKeyEvent, ULONG ExtraInformation, int NextProcIndex)
|
|
{
|
|
int fBreak;
|
|
BYTE Vk;
|
|
|
|
CheckCritIn();
|
|
Vk = (BYTE)pKeyEvent->usFlaggedVk;
|
|
fBreak = pKeyEvent->usFlaggedVk & KBDBREAK;
|
|
|
|
//
|
|
// Check for Numlock key. On the first make set the ToggleKeys timer.
|
|
// The timer is killed on the break of the Numlock key.
|
|
//
|
|
switch (Vk) {
|
|
case VK_NUMLOCK:
|
|
#ifdef FE_SB // ToggleKeys()
|
|
NumLockProc:
|
|
#endif // FE_SB
|
|
/*
|
|
* Don't handle NUMLOCK toggles if the user is doing MouseKey
|
|
* toggling.
|
|
*/
|
|
if ((gLockBits | gLatchBits | gPhysModifierState) == MOUSEKEYMODBITS &&
|
|
TEST_ACCESSFLAG(MouseKeys, MKF_HOTKEYACTIVE)) {
|
|
break;
|
|
}
|
|
if (fBreak)
|
|
{
|
|
//
|
|
// Only reset gptmrToggleKeys on the break of NumLock. This
|
|
// prevents cycling the toggle keys state by continually
|
|
// holding down the NumLock key.
|
|
//
|
|
KILLRITTIMER(NULL, gtmridToggleKeys);
|
|
gtmridToggleKeys = 0;
|
|
gTKExtraInformation = 0;
|
|
gTKScanCode = 0;
|
|
}
|
|
else
|
|
{
|
|
if (gtmridToggleKeys == 0 &&
|
|
TEST_ACCESSFLAG(ToggleKeys, TKF_HOTKEYACTIVE))
|
|
{
|
|
|
|
//
|
|
// Remember key information to be used by timer routine.
|
|
//
|
|
gTKExtraInformation = ExtraInformation;
|
|
gTKScanCode = pKeyEvent->bScanCode;
|
|
gTKNextProcIndex = NextProcIndex;
|
|
gtmridToggleKeys = InternalSetTimer(NULL,
|
|
0,
|
|
TOGGLEKEYTOGGLETIME,
|
|
xxxToggleKeysTimer,
|
|
TMRF_RIT | TMRF_ONESHOT);
|
|
}
|
|
}
|
|
|
|
//
|
|
// If MouseKeys is on, audible feedback has already occurred for this
|
|
// keystroke. Skip the rest of the processing.
|
|
//
|
|
if (TEST_ACCESSFLAG(MouseKeys, MKF_MOUSEKEYSON)) {
|
|
break;
|
|
}
|
|
// fall through
|
|
|
|
case VK_SCROLL:
|
|
case VK_CAPITAL:
|
|
#ifdef FE_SB // ToggleKeys()
|
|
CapitalProc:
|
|
#endif // FE_SB
|
|
if (TEST_ACCESSFLAG(ToggleKeys, TKF_TOGGLEKEYSON) && !fBreak) {
|
|
if (!TestAsyncKeyStateDown(Vk)) {
|
|
PTERMINAL pTerm = grpdeskRitInput->rpwinstaParent->pTerm;
|
|
if (!TestAsyncKeyStateToggle(Vk)) {
|
|
PostRitSound(
|
|
pTerm,
|
|
RITSOUND_HIGHBEEP);
|
|
} else {
|
|
PostRitSound(
|
|
pTerm,
|
|
RITSOUND_LOWBEEP);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
#ifdef FE_SB // ToggleKeys()
|
|
if (Vk == gNumLockVk) goto NumLockProc;
|
|
if (Vk == gOemScrollVk) goto CapitalProc;
|
|
#endif // FE_SB
|
|
if (gtmridToggleKeys != 0) {
|
|
KILLRITTIMER(NULL, gtmridToggleKeys);
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* AccessTimeOutTimer
|
|
*
|
|
* This routine is called if no keyboard activity takes place for the
|
|
* user configured amount of time. All access related functions are
|
|
* disabled.
|
|
*
|
|
* This routine is called with the critical section already locked.
|
|
*
|
|
* Return value:
|
|
* 0
|
|
*
|
|
* History:
|
|
\***************************************************************************/
|
|
VOID xxxAccessTimeOutTimer(
|
|
PWND pwnd,
|
|
UINT message,
|
|
UINT_PTR nID,
|
|
LPARAM lParam)
|
|
{
|
|
UNREFERENCED_PARAMETER(pwnd);
|
|
UNREFERENCED_PARAMETER(message);
|
|
UNREFERENCED_PARAMETER(nID);
|
|
UNREFERENCED_PARAMETER(lParam);
|
|
|
|
CheckCritIn();
|
|
|
|
/*
|
|
* The timeout timer will remain on (if so configured) as long as
|
|
* TEST_ACCF(ACCF_ACCESSENABLED) is TRUE. This means we might get timeouts when
|
|
* only hot keys are enabled, but no features are actually on. Don't
|
|
* provide any audible feedback in this case.
|
|
*/
|
|
if ( TEST_ACCESSFLAG(FilterKeys, FKF_FILTERKEYSON) ||
|
|
TEST_ACCESSFLAG(StickyKeys, SKF_STICKYKEYSON) ||
|
|
TEST_ACCESSFLAG(MouseKeys, MKF_MOUSEKEYSON) ||
|
|
TEST_ACCESSFLAG(ToggleKeys, TKF_TOGGLEKEYSON) ||
|
|
TEST_ACCESSFLAG(SoundSentry, SSF_SOUNDSENTRYON) ||
|
|
TEST_ACCESSFLAG(HighContrast, HCF_HIGHCONTRASTON) ||
|
|
TEST_ACCF(ACCF_SHOWSOUNDSON)) {
|
|
|
|
PTERMINAL pTerm = grpdeskRitInput->rpwinstaParent->pTerm;
|
|
CLEAR_ACCESSFLAG(FilterKeys, FKF_FILTERKEYSON);
|
|
xxxTurnOffStickyKeys();
|
|
CLEAR_ACCESSFLAG(MouseKeys, MKF_MOUSEKEYSON);
|
|
CLEAR_ACCESSFLAG(ToggleKeys, TKF_TOGGLEKEYSON);
|
|
CLEAR_ACCESSFLAG(SoundSentry, SSF_SOUNDSENTRYON);
|
|
CLEAR_ACCF(ACCF_SHOWSOUNDSON);
|
|
CLEAR_ACCESSFLAG(HighContrast, HCF_HIGHCONTRASTON);
|
|
|
|
if (gspwndLogonNotify != NULL) {
|
|
|
|
_PostMessage(gspwndLogonNotify, WM_LOGONNOTIFY,
|
|
LOGON_ACCESSNOTIFY, ACCESS_HIGHCONTRASTOFF);
|
|
}
|
|
|
|
if (TEST_ACCESSFLAG(AccessTimeOut, ATF_ONOFFFEEDBACK)) {
|
|
PostRitSound(
|
|
pTerm,
|
|
RITSOUND_DOWNSIREN);
|
|
}
|
|
PostAccessibility( ACCESS_MOUSEKEYS );
|
|
|
|
PostAccessibility( ACCESS_FILTERKEYS );
|
|
|
|
PostAccessibility( ACCESS_STICKYKEYS );
|
|
|
|
}
|
|
SetAccessEnabledFlag();
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* AccessTimeOutReset
|
|
*
|
|
* This routine resets the timeout timer.
|
|
*
|
|
* Return value:
|
|
* 0
|
|
*
|
|
* History:
|
|
\***************************************************************************/
|
|
VOID AccessTimeOutReset(
|
|
VOID)
|
|
{
|
|
|
|
if (gtmridAccessTimeOut != 0) {
|
|
KILLRITTIMER(NULL, gtmridAccessTimeOut);
|
|
}
|
|
|
|
if (TEST_ACCESSFLAG(AccessTimeOut, ATF_TIMEOUTON)) {
|
|
gtmridAccessTimeOut = InternalSetTimer(NULL,
|
|
0,
|
|
(UINT)gAccessTimeOut.iTimeOutMSec,
|
|
xxxAccessTimeOutTimer,
|
|
TMRF_RIT | TMRF_ONESHOT);
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* xxxUpdatePerUserAccessPackSettings
|
|
*
|
|
* Sets the initial access pack features according to the user's profile.
|
|
*
|
|
* 02-14-93 GregoryW Created.
|
|
\***************************************************************************/
|
|
VOID xxxUpdatePerUserAccessPackSettings(
|
|
PUNICODE_STRING pProfileUserName)
|
|
{
|
|
LUID luidCaller;
|
|
NTSTATUS Status;
|
|
BOOL fSystem;
|
|
BOOL fRegFilterKeysOn;
|
|
BOOL fRegStickyKeysOn;
|
|
BOOL fRegMouseKeysOn;
|
|
BOOL fRegToggleKeysOn;
|
|
BOOL fRegTimeOutOn;
|
|
BOOL fRegKeyboardPref;
|
|
BOOL fRegScreenReader;
|
|
BOOL fRegHighContrastOn;
|
|
DWORD dwDefFlags;
|
|
WCHAR wcHighContrastScheme[MAX_SCHEME_NAME_SIZE];
|
|
|
|
Status = GetProcessLuid(NULL, &luidCaller);
|
|
//
|
|
// If we're called in the system context no one is logged on.
|
|
// We want to read the current .DEFAULT settings for the access
|
|
// features. Later when we're called in the user context (e.g.,
|
|
// someone has successfully logged on) we check to see if the
|
|
// current access state is the same as the default setting. If
|
|
// not, the user has enabled/disabled one or more access features
|
|
// from the keyboard. These changes will be propagated across
|
|
// the logon into the user's intial state (overriding the settings
|
|
// in the user's profile).
|
|
//
|
|
if (NT_SUCCESS(Status) && RtlEqualLuid(&luidCaller, &luidSystem)) {
|
|
fSystem = TRUE;
|
|
} else {
|
|
fSystem = FALSE;
|
|
}
|
|
|
|
FastGetProfileIntW(pProfileUserName,
|
|
PMAP_KEYBOARDRESPONSE,
|
|
TEXT("Flags"),
|
|
0,
|
|
&dwDefFlags,
|
|
0);
|
|
|
|
fRegFilterKeysOn = (dwDefFlags & FKF_FILTERKEYSON) != 0;
|
|
|
|
FastGetProfileIntW(pProfileUserName,
|
|
PMAP_STICKYKEYS,
|
|
TEXT("Flags"),
|
|
0,
|
|
&dwDefFlags,
|
|
0);
|
|
fRegStickyKeysOn = (dwDefFlags & SKF_STICKYKEYSON) != 0;
|
|
|
|
FastGetProfileIntW(pProfileUserName,
|
|
PMAP_MOUSEKEYS,
|
|
TEXT("Flags"),
|
|
0,
|
|
&dwDefFlags,
|
|
0);
|
|
fRegMouseKeysOn = (dwDefFlags & MKF_MOUSEKEYSON) != 0;
|
|
|
|
FastGetProfileIntW(pProfileUserName,
|
|
PMAP_TOGGLEKEYS,
|
|
TEXT("Flags"),
|
|
0,
|
|
&dwDefFlags,
|
|
0);
|
|
fRegToggleKeysOn = (dwDefFlags & TKF_TOGGLEKEYSON) != 0;
|
|
|
|
FastGetProfileIntW(pProfileUserName,
|
|
PMAP_KEYBOARDPREF,
|
|
TEXT("On"),
|
|
0,
|
|
&dwDefFlags,
|
|
0);
|
|
fRegKeyboardPref = !!dwDefFlags;
|
|
|
|
FastGetProfileIntW(pProfileUserName,
|
|
PMAP_SCREENREADER,
|
|
TEXT("On"),
|
|
0,
|
|
&dwDefFlags,
|
|
0);
|
|
fRegScreenReader = !!dwDefFlags;
|
|
|
|
FastGetProfileIntW(pProfileUserName,
|
|
PMAP_TIMEOUT,
|
|
TEXT("Flags"),
|
|
0,
|
|
&dwDefFlags,
|
|
0);
|
|
fRegTimeOutOn = (dwDefFlags & ATF_TIMEOUTON) != 0;
|
|
|
|
FastGetProfileIntW(pProfileUserName,
|
|
PMAP_HIGHCONTRAST,
|
|
TEXT("Flags"),
|
|
0,
|
|
&dwDefFlags,
|
|
0);
|
|
fRegHighContrastOn = (dwDefFlags & HCF_HIGHCONTRASTON) != 0;
|
|
|
|
if (fSystem) {
|
|
//
|
|
// We're in system mode (e.g., no one is logged in). Remember
|
|
// the .DEFAULT state for comparison during the next user logon
|
|
// and set the current state to the .DEFAULT state.
|
|
//
|
|
if (fRegFilterKeysOn) {
|
|
SET_ACCF(ACCF_DEFAULTFILTERKEYSON);
|
|
SET_ACCESSFLAG(FilterKeys, FKF_FILTERKEYSON);
|
|
} else {
|
|
CLEAR_ACCF(ACCF_DEFAULTFILTERKEYSON);
|
|
CLEAR_ACCESSFLAG(FilterKeys, FKF_FILTERKEYSON);
|
|
}
|
|
|
|
//
|
|
// If StickyKeys is currently on and we're about to turn it
|
|
// off we need to make sure the latch keys and lock keys are
|
|
// released.
|
|
//
|
|
if (TEST_ACCESSFLAG(StickyKeys, SKF_STICKYKEYSON) && (fRegFilterKeysOn == 0)) {
|
|
xxxTurnOffStickyKeys();
|
|
}
|
|
|
|
if (fRegStickyKeysOn) {
|
|
SET_ACCF(ACCF_DEFAULTSTICKYKEYSON);
|
|
SET_ACCESSFLAG(StickyKeys, SKF_STICKYKEYSON);
|
|
} else {
|
|
CLEAR_ACCF(ACCF_DEFAULTSTICKYKEYSON);
|
|
CLEAR_ACCESSFLAG(StickyKeys, SKF_STICKYKEYSON);
|
|
}
|
|
|
|
if (fRegMouseKeysOn) {
|
|
SET_ACCF(ACCF_DEFAULTMOUSEKEYSON);
|
|
SET_ACCESSFLAG(MouseKeys, MKF_MOUSEKEYSON);
|
|
} else {
|
|
CLEAR_ACCF(ACCF_DEFAULTMOUSEKEYSON);
|
|
CLEAR_ACCESSFLAG(MouseKeys, MKF_MOUSEKEYSON);
|
|
}
|
|
|
|
if (fRegToggleKeysOn) {
|
|
SET_ACCF(ACCF_DEFAULTTOGGLEKEYSON);
|
|
SET_ACCESSFLAG(ToggleKeys, TKF_TOGGLEKEYSON);
|
|
} else {
|
|
CLEAR_ACCF(ACCF_DEFAULTTOGGLEKEYSON);
|
|
CLEAR_ACCESSFLAG(ToggleKeys, TKF_TOGGLEKEYSON);
|
|
}
|
|
|
|
if (fRegTimeOutOn) {
|
|
SET_ACCF(ACCF_DEFAULTTIMEOUTON);
|
|
SET_ACCESSFLAG(AccessTimeOut, ATF_TIMEOUTON);
|
|
} else {
|
|
CLEAR_ACCF(ACCF_DEFAULTTIMEOUTON);
|
|
CLEAR_ACCESSFLAG(AccessTimeOut, ATF_TIMEOUTON);
|
|
}
|
|
|
|
if (fRegKeyboardPref) {
|
|
SET_ACCF(ACCF_DEFAULTKEYBOARDPREF);
|
|
SET_ACCF(ACCF_KEYBOARDPREF);
|
|
SET_SRVIF(SRVIF_KEYBOARDPREF);
|
|
} else {
|
|
CLEAR_ACCF(ACCF_DEFAULTKEYBOARDPREF);
|
|
CLEAR_ACCF(ACCF_KEYBOARDPREF);
|
|
CLEAR_SRVIF(SRVIF_KEYBOARDPREF);
|
|
}
|
|
|
|
if (fRegScreenReader) {
|
|
SET_ACCF(ACCF_DEFAULTSCREENREADER);
|
|
SET_ACCF(ACCF_SCREENREADER);
|
|
} else {
|
|
CLEAR_ACCF(ACCF_DEFAULTSCREENREADER);
|
|
CLEAR_ACCF(ACCF_SCREENREADER);
|
|
}
|
|
|
|
if (fRegHighContrastOn) {
|
|
SET_ACCF(ACCF_DEFAULTHIGHCONTRASTON);
|
|
SET_ACCESSFLAG(HighContrast, HCF_HIGHCONTRASTON);
|
|
} else {
|
|
CLEAR_ACCF(ACCF_DEFAULTHIGHCONTRASTON);
|
|
CLEAR_ACCESSFLAG(HighContrast, HCF_HIGHCONTRASTON);
|
|
}
|
|
} else {
|
|
//
|
|
// A user has successfully logged on. If the current state is
|
|
// different from the default state stored earlier then we know
|
|
// the user has modified the state via the keyboard (at the logon
|
|
// dialog). This state will override whatever on/off state the
|
|
// user has set in their profile. If the current state is the
|
|
// same as the default state then the on/off setting from the
|
|
// user profile is used.
|
|
//
|
|
|
|
if ( TEST_BOOL_ACCESSFLAG(FilterKeys, FKF_FILTERKEYSON) ==
|
|
TEST_BOOL_ACCF(ACCF_DEFAULTFILTERKEYSON)) {
|
|
//
|
|
// Current state and default state are the same. Use the
|
|
// user's profile setting.
|
|
//
|
|
|
|
SET_OR_CLEAR_ACCESSFLAG(FilterKeys, FKF_FILTERKEYSON, fRegFilterKeysOn);
|
|
}
|
|
|
|
if ( TEST_BOOL_ACCESSFLAG(StickyKeys, SKF_STICKYKEYSON) ==
|
|
TEST_BOOL_ACCF(ACCF_DEFAULTSTICKYKEYSON)) {
|
|
//
|
|
// If StickyKeys is currently on and we're about to turn it
|
|
// off we need to make sure the latch keys and lock keys are
|
|
// released.
|
|
//
|
|
if ( TEST_ACCESSFLAG(StickyKeys, SKF_STICKYKEYSON) &&
|
|
(fRegStickyKeysOn == 0)) {
|
|
|
|
xxxTurnOffStickyKeys();
|
|
}
|
|
|
|
SET_OR_CLEAR_ACCESSFLAG(StickyKeys, SKF_STICKYKEYSON, fRegStickyKeysOn);
|
|
}
|
|
|
|
if ( TEST_BOOL_ACCESSFLAG(MouseKeys, MKF_MOUSEKEYSON) ==
|
|
TEST_BOOL_ACCF(ACCF_DEFAULTMOUSEKEYSON)) {
|
|
//
|
|
// Current state and default state are the same. Use the user's
|
|
// profile setting.
|
|
//
|
|
SET_OR_CLEAR_ACCESSFLAG(MouseKeys, MKF_MOUSEKEYSON, fRegMouseKeysOn);
|
|
}
|
|
|
|
if ( TEST_BOOL_ACCESSFLAG(ToggleKeys, TKF_TOGGLEKEYSON) ==
|
|
TEST_BOOL_ACCF(ACCF_DEFAULTTOGGLEKEYSON)) {
|
|
//
|
|
// Current state and default state are the same. Use the user's
|
|
// profile setting.
|
|
//
|
|
SET_OR_CLEAR_ACCESSFLAG(ToggleKeys, TKF_TOGGLEKEYSON, fRegToggleKeysOn);
|
|
}
|
|
|
|
if ( TEST_BOOL_ACCESSFLAG(AccessTimeOut, ATF_TIMEOUTON) ==
|
|
TEST_BOOL_ACCF(ACCF_DEFAULTTIMEOUTON)) {
|
|
//
|
|
// Current state and default state are the same. Use the user's
|
|
// profile setting.
|
|
//
|
|
SET_OR_CLEAR_ACCESSFLAG(AccessTimeOut, ATF_TIMEOUTON, fRegTimeOutOn);
|
|
}
|
|
|
|
if ( TEST_BOOL_ACCF(ACCF_KEYBOARDPREF) ==
|
|
TEST_BOOL_ACCF(ACCF_DEFAULTKEYBOARDPREF)) {
|
|
//
|
|
// Current state and default state are the same. Use the user's
|
|
// profile setting.
|
|
//
|
|
SET_OR_CLEAR_ACCF(ACCF_KEYBOARDPREF, fRegKeyboardPref);
|
|
}
|
|
|
|
if ( TEST_BOOL_ACCF(ACCF_SCREENREADER) ==
|
|
TEST_BOOL_ACCF(ACCF_DEFAULTSCREENREADER)) {
|
|
//
|
|
// Current state and default state are the same. Use the user's
|
|
// profile setting.
|
|
//
|
|
SET_OR_CLEAR_ACCF(ACCF_SCREENREADER, fRegScreenReader);
|
|
}
|
|
|
|
if ( TEST_BOOL_ACCESSFLAG(HighContrast, HCF_HIGHCONTRASTON) ==
|
|
TEST_BOOL_ACCF(ACCF_DEFAULTHIGHCONTRASTON)) {
|
|
//
|
|
// Current state and default state are the same. Use the user's
|
|
// profile setting.
|
|
//
|
|
SET_OR_CLEAR_ACCESSFLAG(HighContrast, HCF_HIGHCONTRASTON, fRegHighContrastOn);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get the default FilterKeys state.
|
|
//
|
|
// -------- flag --------------- value --------- default ------
|
|
// #define FKF_FILTERKEYSON 0x00000001 0
|
|
// #define FKF_AVAILABLE 0x00000002 2
|
|
// #define FKF_HOTKEYACTIVE 0x00000004 0
|
|
// #define FKF_CONFIRMHOTKEY 0x00000008 0
|
|
// #define FKF_HOTKEYSOUND 0x00000010 10
|
|
// #define FKF_INDICATOR 0x00000020 0
|
|
// #define FKF_CLICKON 0x00000040 40
|
|
// ----------------------------------------- total = 0x52 = 82
|
|
//
|
|
|
|
FastGetProfileIntW(pProfileUserName,
|
|
PMAP_KEYBOARDRESPONSE,
|
|
TEXT("Flags"),
|
|
82,
|
|
&dwDefFlags,
|
|
0);
|
|
|
|
SET_OR_CLEAR_FLAG(
|
|
dwDefFlags,
|
|
FKF_FILTERKEYSON,
|
|
TEST_ACCESSFLAG(FilterKeys, FKF_FILTERKEYSON));
|
|
|
|
gFilterKeys.dwFlags = dwDefFlags;
|
|
FastGetProfileIntW(pProfileUserName,
|
|
PMAP_KEYBOARDRESPONSE,
|
|
TEXT("DelayBeforeAcceptance"),
|
|
1000,
|
|
&gFilterKeys.iWaitMSec,
|
|
0);
|
|
|
|
FastGetProfileIntW(pProfileUserName,
|
|
PMAP_KEYBOARDRESPONSE,
|
|
TEXT("AutoRepeatRate"),
|
|
500,
|
|
&gFilterKeys.iRepeatMSec,
|
|
0);
|
|
|
|
FastGetProfileIntW(pProfileUserName,
|
|
PMAP_KEYBOARDRESPONSE,
|
|
TEXT("AutoRepeatDelay"),
|
|
1000,
|
|
&gFilterKeys.iDelayMSec,
|
|
0);
|
|
|
|
FastGetProfileIntW(pProfileUserName,
|
|
PMAP_KEYBOARDRESPONSE,
|
|
TEXT("BounceTime"),
|
|
0,
|
|
&gFilterKeys.iBounceMSec,
|
|
0);
|
|
|
|
//
|
|
// Fill in the SoundSentry state. This release of the
|
|
// accessibility features only supports iWindowsEffect.
|
|
//
|
|
// -------- flag --------------- value --------- default ------
|
|
// #define SSF_SOUNDSENTRYON 0x00000001 0
|
|
// #define SSF_AVAILABLE 0x00000002 1
|
|
// #define SSF_INDICATOR 0x00000004 0
|
|
// ----------------------------------------- total = 0x2 = 2
|
|
//
|
|
FastGetProfileIntW(pProfileUserName,
|
|
PMAP_SOUNDSENTRY,
|
|
TEXT("Flags"),
|
|
2,
|
|
&gSoundSentry.dwFlags,
|
|
0);
|
|
|
|
FastGetProfileIntW(pProfileUserName,
|
|
PMAP_SOUNDSENTRY,
|
|
TEXT("FSTextEffect"),
|
|
0,
|
|
&gSoundSentry.iFSTextEffect,
|
|
0);
|
|
|
|
FastGetProfileIntW(pProfileUserName,
|
|
PMAP_SOUNDSENTRY,
|
|
TEXT("WindowsEffect"),
|
|
0,
|
|
&gSoundSentry.iWindowsEffect,
|
|
0);
|
|
|
|
/*
|
|
* Set ShowSounds flag.
|
|
*/
|
|
FastGetProfileIntW(pProfileUserName,
|
|
PMAP_SHOWSOUNDS,
|
|
TEXT("On"),
|
|
0,
|
|
&dwDefFlags,
|
|
0);
|
|
SET_OR_CLEAR_ACCF(ACCF_SHOWSOUNDSON, dwDefFlags);
|
|
/*
|
|
* Bug 17210. Update the System Metrics Info.
|
|
*/
|
|
SYSMET(SHOWSOUNDS) = TEST_BOOL_ACCF(ACCF_SHOWSOUNDSON);
|
|
|
|
//
|
|
// Get the default StickyKeys state.
|
|
//
|
|
// -------- flag --------------- value --------- default ------
|
|
// #define SKF_STICKYKEYSON 0x00000001 0
|
|
// #define SKF_AVAILABLE 0x00000002 2
|
|
// #define SKF_HOTKEYACTIVE 0x00000004 0
|
|
// #define SKF_CONFIRMHOTKEY 0x00000008 0
|
|
// #define SKF_HOTKEYSOUND 0x00000010 10
|
|
// #define SKF_INDICATOR 0x00000020 0
|
|
// #define SKF_AUDIBLEFEEDBACK 0x00000040 40
|
|
// #define SKF_TRISTATE 0x00000080 80
|
|
// #define SKF_TWOKEYSOFF 0x00000100 100
|
|
// ----------------------------------------- total = 0x1d2 = 466
|
|
//
|
|
FastGetProfileIntW(pProfileUserName,
|
|
PMAP_STICKYKEYS,
|
|
TEXT("Flags"),
|
|
466,
|
|
&dwDefFlags,
|
|
0);
|
|
|
|
SET_OR_CLEAR_FLAG(
|
|
dwDefFlags,
|
|
SKF_STICKYKEYSON,
|
|
TEST_ACCESSFLAG(StickyKeys, SKF_STICKYKEYSON));
|
|
|
|
gStickyKeys.dwFlags = dwDefFlags;
|
|
|
|
//
|
|
// Get the default MouseKeys state.
|
|
//
|
|
// -------- flag --------------- value --------- default ------
|
|
// #define MKF_MOUSEKEYSON 0x00000001 0
|
|
// #define MKF_AVAILABLE 0x00000002 2
|
|
// #define MKF_HOTKEYACTIVE 0x00000004 0
|
|
// #define MKF_CONFIRMHOTKEY 0x00000008 0
|
|
// #define MKF_HOTKEYSOUND 0x00000010 10
|
|
// #define MKF_INDICATOR 0x00000020 0
|
|
// #define MKF_MODIFIERS 0x00000040 0
|
|
// #define MKF_REPLACENUMBERS 0x00000080 0
|
|
// ----------------------------------------- total = 0x12 = 18
|
|
//
|
|
FastGetProfileIntW(pProfileUserName,
|
|
PMAP_MOUSEKEYS,
|
|
TEXT("Flags"),
|
|
18,
|
|
&dwDefFlags,
|
|
0);
|
|
|
|
SET_OR_CLEAR_FLAG(
|
|
dwDefFlags,
|
|
MKF_MOUSEKEYSON,
|
|
TEST_ACCESSFLAG(MouseKeys, MKF_MOUSEKEYSON));
|
|
|
|
gMouseKeys.dwFlags = dwDefFlags;
|
|
FastGetProfileIntW(pProfileUserName,
|
|
PMAP_MOUSEKEYS,
|
|
TEXT("MaximumSpeed"),
|
|
MAXSPEED_DEF,
|
|
&gMouseKeys.iMaxSpeed,
|
|
0);
|
|
|
|
FastGetProfileIntW(pProfileUserName,
|
|
PMAP_MOUSEKEYS,
|
|
TEXT("TimeToMaximumSpeed"),
|
|
TIMETOMAXSPEED_DEF,
|
|
&gMouseKeys.iTimeToMaxSpeed,
|
|
0);
|
|
|
|
/*
|
|
* Avoid unexpected values, like when the migration from previous OS has set bogus values
|
|
* or when the registry is simply broken...
|
|
*/
|
|
if (gMouseKeys.iMaxSpeed < MAXSPEED_MIN || gMouseKeys.iMaxSpeed > MAXSPEED_MAX) {
|
|
gMouseKeys.iMaxSpeed = MAXSPEED_DEF;
|
|
}
|
|
if (gMouseKeys.iTimeToMaxSpeed < TIMETOMAXSPEED_MIN || gMouseKeys.iTimeToMaxSpeed > TIMETOMAXSPEED_MAX) {
|
|
gMouseKeys.iTimeToMaxSpeed = TIMETOMAXSPEED_DEF;
|
|
}
|
|
|
|
CalculateMouseTable();
|
|
|
|
gbMKMouseMode =
|
|
#ifdef FE_SB
|
|
(TestAsyncKeyStateToggle(gNumLockVk) != 0) ^
|
|
#else // FE_SB
|
|
(TestAsyncKeyStateToggle(VK_NUMLOCK) != 0) ^
|
|
#endif // FE_SB
|
|
(TEST_ACCESSFLAG(MouseKeys, MKF_REPLACENUMBERS) != 0);
|
|
|
|
//
|
|
// If the system does not have a hardware mouse:
|
|
// If MouseKeys is enabled show the mouse cursor,
|
|
// o.w. hide the mouse cursor.
|
|
//
|
|
if (TEST_ACCESSFLAG(MouseKeys, MKF_MOUSEKEYSON)) {
|
|
MKShowMouseCursor();
|
|
} else {
|
|
MKHideMouseCursor();
|
|
}
|
|
|
|
//
|
|
// Get the default ToggleKeys state.
|
|
//
|
|
// -------- flag --------------- value --------- default ------
|
|
// #define TKF_TOGGLEKEYSON 0x00000001 0
|
|
// #define TKF_AVAILABLE 0x00000002 2
|
|
// #define TKF_HOTKEYACTIVE 0x00000004 0
|
|
// #define TKF_CONFIRMHOTKEY 0x00000008 0
|
|
// #define TKF_HOTKEYSOUND 0x00000010 10
|
|
// #define TKF_INDICATOR 0x00000020 0
|
|
// ----------------------------------------- total = 0x12 = 18
|
|
//
|
|
FastGetProfileIntW(pProfileUserName,
|
|
PMAP_TOGGLEKEYS,
|
|
TEXT("Flags"),
|
|
18,
|
|
&dwDefFlags,
|
|
0);
|
|
|
|
SET_OR_CLEAR_FLAG(
|
|
dwDefFlags,
|
|
TKF_TOGGLEKEYSON,
|
|
TEST_ACCESSFLAG(ToggleKeys, TKF_TOGGLEKEYSON));
|
|
|
|
gToggleKeys.dwFlags = dwDefFlags;
|
|
|
|
//
|
|
// Get the default Timeout state.
|
|
//
|
|
// -------- flag --------------- value --------- default ------
|
|
// #define ATF_TIMEOUTON 0x00000001 0
|
|
// #define ATF_ONOFFFEEDBACK 0x00000002 2
|
|
// ----------------------------------------- total = 0x2 = 2
|
|
//
|
|
FastGetProfileIntW(pProfileUserName,
|
|
PMAP_TIMEOUT,
|
|
TEXT("Flags"),
|
|
2,
|
|
&dwDefFlags,
|
|
0);
|
|
|
|
SET_OR_CLEAR_FLAG(
|
|
dwDefFlags,
|
|
ATF_TIMEOUTON,
|
|
TEST_ACCESSFLAG(AccessTimeOut, ATF_TIMEOUTON));
|
|
|
|
gAccessTimeOut.dwFlags = dwDefFlags;
|
|
|
|
#ifdef FE_SB //
|
|
if (gpKbdNlsTbl) {
|
|
//
|
|
// Is there any alternative MouseVKey table in KBDNLSTABLE ?
|
|
//
|
|
if ((gpKbdNlsTbl->NumOfMouseVKey == cMouseVKeys) &&
|
|
(gpKbdNlsTbl->pusMouseVKey != NULL)) {
|
|
//
|
|
// Overwite the pointer.
|
|
//
|
|
gpusMouseVKey = gpKbdNlsTbl->pusMouseVKey;
|
|
}
|
|
|
|
//
|
|
// Is there any remapping flag for VK_NUMLOCK/VK_SCROLL ?
|
|
//
|
|
if (gpKbdNlsTbl->LayoutInformation & NLSKBD_INFO_ACCESSIBILITY_KEYMAP) {
|
|
//
|
|
// Overwrite default.
|
|
//
|
|
gNumLockVk = VK_HOME;
|
|
gOemScrollVk = VK_KANA;
|
|
}
|
|
}
|
|
#endif // FE_SB
|
|
|
|
FastGetProfileIntW(pProfileUserName,
|
|
PMAP_TIMEOUT,
|
|
TEXT("TimeToWait"),
|
|
300000,
|
|
&gAccessTimeOut.iTimeOutMSec,
|
|
0); // default is 5 minutes
|
|
|
|
/*
|
|
* Get High Contrast state
|
|
*/
|
|
|
|
FastGetProfileIntW(pProfileUserName,
|
|
PMAP_HIGHCONTRAST,
|
|
TEXT("Flags"),
|
|
HCF_AVAILABLE | HCF_HOTKEYSOUND | HCF_HOTKEYAVAILABLE,
|
|
&dwDefFlags,
|
|
0);
|
|
|
|
SET_OR_CLEAR_FLAG(
|
|
dwDefFlags,
|
|
HCF_HIGHCONTRASTON,
|
|
TEST_ACCESSFLAG(HighContrast, HCF_HIGHCONTRASTON));
|
|
|
|
gHighContrast.dwFlags = dwDefFlags;
|
|
|
|
/*
|
|
* Get scheme -- set up buffer
|
|
*/
|
|
|
|
if (FastGetProfileStringW(pProfileUserName,
|
|
PMAP_HIGHCONTRAST,
|
|
TEXT("High Contrast Scheme"),
|
|
NULL,
|
|
wcHighContrastScheme,
|
|
MAX_SCHEME_NAME_SIZE,
|
|
0)) {
|
|
|
|
/*
|
|
* copy data
|
|
*/
|
|
|
|
wcscpy(gHighContrastDefaultScheme, wcHighContrastScheme);
|
|
}
|
|
|
|
|
|
AccessTimeOutReset();
|
|
SetAccessEnabledFlag();
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* SetAccessEnabledFlag
|
|
*
|
|
* Sets the global flag ACCF_ACCESSENABLED to non-zero if any accessibility
|
|
* function is on or hot key activation is enabled. When TEST_ACCF(ACCF_ACCESSENABLED)
|
|
* is zero keyboard input is processed directly. When TEST_ACCF(ACCF_ACCESSENABLED) is
|
|
* non-zero keyboard input is filtered through AccessProceduresStream().
|
|
* See KeyboardApcProcedure in ntinput.c.
|
|
*
|
|
* History:
|
|
* 01-19-94 GregoryW Created.
|
|
\***************************************************************************/
|
|
VOID SetAccessEnabledFlag(VOID)
|
|
{
|
|
|
|
SET_OR_CLEAR_ACCF(ACCF_ACCESSENABLED,
|
|
TEST_ACCESSFLAG(FilterKeys, FKF_FILTERKEYSON) ||
|
|
TEST_ACCESSFLAG(FilterKeys, FKF_HOTKEYACTIVE) ||
|
|
TEST_ACCESSFLAG(StickyKeys, SKF_STICKYKEYSON) ||
|
|
TEST_ACCESSFLAG(StickyKeys, SKF_HOTKEYACTIVE) ||
|
|
TEST_ACCESSFLAG(HighContrast, HCF_HOTKEYACTIVE) ||
|
|
TEST_ACCESSFLAG(MouseKeys, MKF_MOUSEKEYSON) ||
|
|
TEST_ACCESSFLAG(MouseKeys, MKF_HOTKEYACTIVE) ||
|
|
TEST_ACCESSFLAG(ToggleKeys, TKF_TOGGLEKEYSON) ||
|
|
TEST_ACCESSFLAG(ToggleKeys, TKF_HOTKEYACTIVE) ||
|
|
TEST_ACCESSFLAG(SoundSentry, SSF_SOUNDSENTRYON)||
|
|
TEST_ACCF(ACCF_SHOWSOUNDSON));
|
|
}
|
|
|
|
VOID SoundSentryTimer(
|
|
PWND pwnd,
|
|
UINT message,
|
|
UINT_PTR idTimer,
|
|
LPARAM lParam)
|
|
{
|
|
TL tlpwndT;
|
|
PWND pwndSoundSentry;
|
|
|
|
UNREFERENCED_PARAMETER(pwnd);
|
|
UNREFERENCED_PARAMETER(message);
|
|
UNREFERENCED_PARAMETER(lParam);
|
|
|
|
if (pwndSoundSentry = RevalidateHwnd(ghwndSoundSentry)) {
|
|
ThreadLock(pwndSoundSentry, &tlpwndT);
|
|
xxxFlashWindow(pwndSoundSentry,
|
|
(TEST_BOOL_ACCF(ACCF_FIRSTTICK) ? FLASHW_ALL : FLASHW_STOP),
|
|
0);
|
|
ThreadUnlock(&tlpwndT);
|
|
}
|
|
|
|
if (TEST_ACCF(ACCF_FIRSTTICK)) {
|
|
gtmridSoundSentry = InternalSetTimer(NULL,
|
|
idTimer,
|
|
5,
|
|
SoundSentryTimer,
|
|
TMRF_RIT | TMRF_ONESHOT);
|
|
CLEAR_ACCF(ACCF_FIRSTTICK);
|
|
} else {
|
|
ghwndSoundSentry = NULL;
|
|
gtmridSoundSentry = 0;
|
|
SET_ACCF(ACCF_FIRSTTICK);
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* _UserSoundSentryWorker
|
|
*
|
|
* This is the worker routine that provides the visual feedback requested
|
|
* by the user.
|
|
*
|
|
* History:
|
|
* 08-02-93 GregoryW Created.
|
|
\***************************************************************************/
|
|
BOOL
|
|
_UserSoundSentryWorker(VOID)
|
|
{
|
|
PWND pwndActive;
|
|
TL tlpwndT;
|
|
|
|
CheckCritIn();
|
|
//
|
|
// Check to see if SoundSentry is on.
|
|
//
|
|
if (!TEST_ACCESSFLAG(SoundSentry, SSF_SOUNDSENTRYON)) {
|
|
return TRUE;
|
|
}
|
|
|
|
if ((gpqForeground != NULL) && (gpqForeground->spwndActive != NULL)) {
|
|
pwndActive = gpqForeground->spwndActive;
|
|
} else {
|
|
return TRUE;
|
|
}
|
|
|
|
switch (gSoundSentry.iWindowsEffect) {
|
|
|
|
case SSWF_NONE:
|
|
break;
|
|
|
|
case SSWF_TITLE:
|
|
//
|
|
// Flash the active caption bar.
|
|
//
|
|
if (gtmridSoundSentry) {
|
|
break;
|
|
}
|
|
ThreadLock(pwndActive, &tlpwndT);
|
|
xxxFlashWindow(pwndActive, FLASHW_ALL, 0);
|
|
ThreadUnlock(&tlpwndT);
|
|
|
|
ghwndSoundSentry = HWq(pwndActive);
|
|
gtmridSoundSentry = InternalSetTimer(NULL,
|
|
0,
|
|
gpsi->dtCaretBlink,
|
|
SoundSentryTimer,
|
|
TMRF_RIT | TMRF_ONESHOT);
|
|
break;
|
|
|
|
case SSWF_WINDOW:
|
|
{
|
|
//
|
|
// Flash the active window.
|
|
//
|
|
HDC hdc;
|
|
RECT rc;
|
|
|
|
hdc = _GetWindowDC(pwndActive);
|
|
_GetWindowRect(pwndActive, &rc);
|
|
//
|
|
// _GetWindowRect returns screen coordinates. First adjust them
|
|
// to window (display) coordinates and then map them to logical
|
|
// coordinates before calling InvertRect.
|
|
//
|
|
OffsetRect(&rc, -rc.left, -rc.top);
|
|
GreDPtoLP(hdc, (LPPOINT)&rc, 2);
|
|
InvertRect(hdc, &rc);
|
|
InvertRect(hdc, &rc);
|
|
_ReleaseDC(hdc);
|
|
break;
|
|
}
|
|
|
|
case SSWF_DISPLAY:
|
|
{
|
|
//
|
|
// Flash the entire display.
|
|
//
|
|
HDC hdc;
|
|
RECT rc;
|
|
|
|
hdc = _GetDCEx(PWNDDESKTOP(pwndActive), NULL, DCX_WINDOW | DCX_CACHE);
|
|
rc.left = rc.top = 0;
|
|
rc.right = SYSMET(CXVIRTUALSCREEN);
|
|
rc.bottom = SYSMET(CYVIRTUALSCREEN);
|
|
InvertRect(hdc, &rc);
|
|
InvertRect(hdc, &rc);
|
|
_ReleaseDC(hdc);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* UtilityManager
|
|
*
|
|
* This is the strategy routine that gets called as part of the input stream
|
|
* processing. Utility Manager launching happens here.
|
|
*
|
|
* Return value:
|
|
* TRUE - key event should be passed on to the next access routine.
|
|
* FALSE - key event was processed and should not be passed on.
|
|
*
|
|
* History: 10-28-98 a-anilk created
|
|
\***************************************************************************/
|
|
BOOL UtilityManager(
|
|
PKE pKeyEvent,
|
|
ULONG ExtraInformation,
|
|
int NotUsed)
|
|
{
|
|
int CurrentModState;
|
|
int fBreak;
|
|
BYTE Vk;
|
|
|
|
UNREFERENCED_PARAMETER(NotUsed);
|
|
UNREFERENCED_PARAMETER(ExtraInformation);
|
|
|
|
CheckCritIn();
|
|
|
|
Vk = (BYTE)(pKeyEvent->usFlaggedVk & 0xff);
|
|
fBreak = pKeyEvent->usFlaggedVk & KBDBREAK;
|
|
CurrentModState = gLockBits | gLatchBits | gPhysModifierState;
|
|
|
|
// the hot key to launch the utility manager is WinKey + U
|
|
if (Vk == VK_U && !fBreak && (CurrentModState & LRWIN)) {
|
|
PostAccessNotification(ACCESS_UTILITYMANAGER);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE; // send key event to next accessibility routine.
|
|
}
|
|
|