/*******************************************************

功能: 键盘驱动
接口: 
原理:
说明: 本机键盘驱动MDD层
编制: 刘启明
日期: 2010-08-01

*******************************************************/

#include <windows.h>
#include <memory.h>
#include <nkintr.h>
#include <pkfuncs.h>
#include <keybddr.h>
#include <keybdpdd.h>
#include <winuserm.h>
#include <nkintr.h>
#include <bsp.h>

#ifndef OAL_INTR_FORCE_STATIC
#define OAL_INTR_FORCE_STATIC       (1 << 2)
#endif

#define C_VIRTUAL_KEYS     			256
#define MAX_TO_UNICODE_CHARACTERS	1

#define AUTO_REPEAT_INITIAL_DELAY_MIN		250
#define AUTO_REPEAT_INITIAL_DELAY_MAX		1000
#define AUTO_REPEAT_INITIAL_DELAY_DEFAULT	500

#define AUTO_REPEAT_KEYS_PER_SEC_MIN		2
#define AUTO_REPEAT_KEYS_PER_SEC_MAX		30
#define AUTO_REPEAT_KEYS_PER_SEC_DEFAULT	20

static DWORD v_AutoRepeatInitialDelay = AUTO_REPEAT_INITIAL_DELAY_DEFAULT;
static DWORD v_AutoRepeatKeysPerSec = AUTO_REPEAT_KEYS_PER_SEC_DEFAULT;

//	Routine to call back in to user when there is a keyboard event.
static PFN_KEYBD_EVENT_CALLBACK	v_pfnKeybdEventCallback;


//	The real state of the certain keys.
static BOOL		v_fLShiftDown;
static BOOL		v_fRShiftDown;
static BOOL		v_fLAltDown;
static BOOL		v_fRAltDown;
static BOOL		v_fLCtrlDown;
static BOOL		v_fRCtrlDown;


//	Use a pseudo Alt key since we simulate that the Alt key goes up and down.
static BOOL		v_fLPseudoAltDown;
static BOOL		v_fRPseudoAltDown;
static BOOL		v_fLastKeyDownWasPseudoAlt;


//	Keep track of the last vkey actually sent for keys which have multiple 
//	virtual keys.  
static UINT32	v_LShiftVKeySent;
static UINT32	v_RShiftVKeySent;
static UINT32	v_PeriodVKeySent;
static UINT32	v_CommaVKeySent;
static UINT32	v_BackVKeySent;
static UINT32	v_RightVKeySent;
static UINT32	v_LeftVKeySent;
static UINT32	v_UpVKeySent;
static UINT32	v_DownVKeySent;


#define ANY_ALT_DOWN()		(v_fLAltDown || v_fRAltDown)
#define ANY_CTRL_DOWN()		(v_fLCtrlDown || v_fRCtrlDown)
#define ANY_SHIFT_DOWN()	(v_fLShiftDown || v_fRShiftDown)

UINT32 g_EINT28SysIntr = SYSINTR_NOP;
UINT32 g_EINT29SysIntr = SYSINTR_NOP;
UINT32 g_EINT4SysIntr = SYSINTR_NOP;
UINT32 g_EINT7SysIntr = SYSINTR_NOP;
UINT32 g_EINT9SysIntr = SYSINTR_NOP;
UINT32 g_EINT16SysIntr = SYSINTR_NOP;


//extern MRX_FLAG *MRXFLAG;

//      @doc    EXTERNAL DRIVERS
/*      @struct TO_UNICODE_STATE |

State info needed by the driver to convert virtual keys to Unicode.

@xref
	<tab><f KeybdDriverGetDeviceInfo><nl>
	<tab><f KeybdDriverInitStates><nl>
	<tab><f KeybdDriverVKeyToUnicode><nl>


@comm This struct would be used by the driver to maintain any state 
information needed between key events in order to generate Unicode 
characters from virtual keys.  

@comm This structure is not visible outside of the driver.  The driver 
provides information on its size via the <f KeybdDriverGetDeviceInfo> 
function.  The input system allocates a buffer for the structure and 
passes it in when it calls <f KeybdDriverVKeyToUnicode>.  

@comm In the sample driver code, it appears for demo purposes only.  

*/
struct TO_UNICODE_STATE
	{
	int     KeybdSpecificVKeyToUnicodeStateHere;    //      @field  Whatever is needed by a specific keyboard driver
	};


extern "C"
//      @doc EXTERNAL DRIVERS
/*      @func

Gives information about the keyboard and driver.


@rdesc If the function succeeds the return value is TRUE, otherwise, it is 
FALSE.  Extended error information is available via the GetLastError 
function.  

@xref
	<tab><c KBDI_VKEY_TO_UNICODE_INFO_ID><nl>
	<tab><c KBDI_AUTOREPEAT_INFO_ID><nl>
	<tab><c KBDI_AUTOREPEAT_SELECTIONS_INFO_ID>

@comm This function must be re-entrant since it is exposed by the input 
system via the <f KeybdGetDeviceInfo> function and may be called by 
multiple threads.  

*/
BOOL KeybdDriverGetInfo(
	INT		iKeybdId,	// @parm Id of the keyboard to get the information from.
	INT		iIndex,		// @parm Id of info to retrieve.
	LPVOID  lpOutput	// @parm Output buffer.
	)
{
	RETAILMSG(0, (TEXT("<KBD> enter KeybdDriverGetInfo(),keybdId=0x%x\n"), iKeybdId)); 
	if ( lpOutput == NULL )
	{
		SetLastError(ERROR_INVALID_PARAMETER);
		return FALSE;
	}

	switch ( iIndex )
	{
		case KBDI_VKEY_TO_UNICODE_INFO_ID:
			{
			struct KBDI_VKEY_TO_UNICODE_INFO	*pInfo =
					(struct KBDI_VKEY_TO_UNICODE_INFO*)lpOutput;
			pInfo ->cbToUnicodeState = sizeof(TO_UNICODE_STATE); //for demo purposes only
			pInfo ->cMaxToUnicodeCharacters = MAX_TO_UNICODE_CHARACTERS;
			}
			break;

		case KBDI_AUTOREPEAT_INFO_ID:
			{
			struct KBDI_AUTOREPEAT_INFO	*pInfo =
					(struct KBDI_AUTOREPEAT_INFO*)lpOutput;
			pInfo ->CurrentInitialDelay = v_AutoRepeatInitialDelay;
			pInfo ->CurrentRepeatRate = v_AutoRepeatKeysPerSec;
			pInfo ->cInitialDelaysSelectable = -1;
			pInfo ->cRepeatRatesSelectable = -1;
			}
			break;

		case KBDI_AUTOREPEAT_SELECTIONS_INFO_ID:
			{
			INT32	*pInfo = (INT32*)lpOutput;
			*(pInfo)	= AUTO_REPEAT_INITIAL_DELAY_MIN;		//	Min initial delay
			*(pInfo+1)	= AUTO_REPEAT_INITIAL_DELAY_MAX;		//	Max initial delay
			*(pInfo+2)	= AUTO_REPEAT_KEYS_PER_SEC_MIN;		//	Min repeat rate
			*(pInfo+3)	= AUTO_REPEAT_KEYS_PER_SEC_MAX;		//	Max repeat rate
			}
			break;

		default:
			SetLastError(ERROR_INVALID_PARAMETER);
			return FALSE;
		}
  return TRUE;
}
#ifdef DEBUG
PFN_KEYBD_DRIVER_GET_INFO v_pfnGetInfoTest = KeybdDriverGetInfo;
#endif


extern "C"
//      @doc EXTERNAL DRIVERS
/*      @func

Initializes the virtual key state and driver specific state.

@xref

	<tab><t KEY_STATE><nl>
	<tab><t TO_UNICODE_STATE><nl>
	<tab><f KeybdDriverGetInfo><nl>
	<tab><c KBDI_VKEY_TO_UNICODE_INFO_ID><nl>
	<tab><t KBDI_VKEY_TO_UNICODE_INFO><nl>

@comm After calling <f KeybdDriverGetInfo> and allocating its required 
memory, the input system calls this function to allow the driver to 
initialize the memory.

@comm The pKeybdDriverToUnicodeState is a pointer to a piece of memory of 
at least cbToUnicodeState bytes as reported by the <f KeybdDriverGetInfo> 
function in the <t KBDI_VKEY_TO_UNICODE_INFO> structure.  If 
cbToUnicodeState was reported as 0, this parameter may be NULL.  

@comm This function must be re-entrant since it is exposed by the input 
system via the <f KeybdInitStates> function and may be called by multiple 
threads.  

*/
BOOL KeybdDriverInitStates(
	INT			iKeybdId,						// @parm Id of the keyboard to initialize keystate data for.
	KEY_STATE   KeyState,                       // @parm Key state to initialize.
	void        *pKeybdDeviceToUnicodeState     // @parm Keyboard specific state to initialize.
	)
{
	TO_UNICODE_STATE	*pDemoState = (TO_UNICODE_STATE*)pKeybdDeviceToUnicodeState;
	int	i;

	RETAILMSG(0, (TEXT("<KBD> enter KeybdDriverInitStates()\n"))); 

	// We have some demo state, so check for it.  If we had no state,
	// this parameter could be NULL with no error.
	if ( pDemoState == NULL )
	{
		SetLastError(ERROR_INVALID_PARAMETER);
		return FALSE;
	}
 
	for ( i = 0; i < COUNT_VKEYS; i++ )
	{
		KeyState[i] = 0;
	}

	return TRUE;
}
#ifdef DEBUG
PFN_KEYBD_DRIVER_INIT_STATES v_pfnInitStatesTest = KeybdDriverInitStates;
#endif

/*++

MapLRVKeys:

Map left and right virtual keys to their common vkey.  


Return Value:

The common vkey.  

--*/
UINT32
MapLRVKeys(
	UINT32	vkey
	)
{
	if ( ( vkey == VK_LCONTROL ) ||
		 ( vkey == VK_RCONTROL ) )
		return VK_CONTROL;

	if ( ( vkey == VK_LMENU ) ||
		 ( vkey == VK_RMENU ) )
		return VK_MENU;

	if ( ( vkey == VK_LSHIFT ) ||
		 ( vkey == VK_RSHIFT ) )
		return VK_SHIFT;

//	VK_LWIN and VK_RWIN do not have a merged key.

	return vkey;
}





/*++

NewKeyStateFlags:

Figure out the new key state flags based on the current state and the 
event.  


Return Value:

The new flag settings.  

--*/
UINT32
NewKeyStateFlags(
	KEY_STATE_FLAGS CurrentState,
	KEY_STATE_FLAGS KeyEventFlags
	)
{
//	Just interested in down/up flag.
	KeyEventFlags &= KeyStateDownFlag;

//	First update the key state for the specific key.
//	Remember the previous state.
	if ( KeyStateIsDown(CurrentState) )
		CurrentState |= KeyStatePrevDownFlag;
	else
		CurrentState &= ~KeyStatePrevDownFlag;

//	Set the new state.
	if ( KeyEventFlags )
		CurrentState |= KeyStateDownFlag | KeyStateGetAsyncDownFlag;
	else
		CurrentState &= ~KeyStateDownFlag;

//	Toggle flag only changes on down transition, not auto repeat.
	if ( KeyStateIsDownTransition(CurrentState) )
		CurrentState ^= KeyStateToggledFlag;

	return CurrentState;
}


/*++

KeybdDriverKeyStateToShiftFlags:

Collapse the shift state from a given key state array into a single 
element.  


Notes:

For the given virtual key, the current flags from the key state array are 
put into the low bits of the output flags.  


--*/
void KeybdDriverKeyStateToShiftFlags(
	KEY_STATE       KeyState,           // Key state array.
	UINT32          VKey,               // Virtual key.
	KEY_STATE_FLAGS *pShiftStateFlags   // Location to put collapsed shift state.
	)
{
	KEY_STATE_FLAGS ShiftFlags = 0;

//  Add the standard keys.
	if ( KeyStateIsDown(KeyState[VK_CONTROL]) )
	{
		ShiftFlags |= KeyShiftAnyCtrlFlag;
	}

	if ( KeyStateIsDown(KeyState[VK_LCONTROL]) )
	{
		ShiftFlags |= KeyShiftLeftCtrlFlag;
	}

	if ( KeyStateIsDown(KeyState[VK_RCONTROL]) )
	{
		ShiftFlags |= KeyShiftRightCtrlFlag;
	}


	if ( KeyStateIsDown(KeyState[VK_SHIFT]) )
	{
		ShiftFlags |= KeyShiftAnyShiftFlag;
	}

	if ( KeyStateIsDown(KeyState[VK_LSHIFT]) )
	{
		ShiftFlags |= KeyShiftLeftShiftFlag;
	}

	if ( KeyStateIsDown(KeyState[VK_RSHIFT]) )
	{
		ShiftFlags |= KeyShiftRightShiftFlag;
	}


	if ( KeyStateIsDown(KeyState[VK_MENU]) )
	{
		ShiftFlags |= KeyShiftAnyAltFlag;
	}

	if ( KeyStateIsDown(KeyState[VK_LMENU]) )
	{
		ShiftFlags |= KeyShiftLeftAltFlag;
	}

	if ( KeyStateIsDown(KeyState[VK_RMENU]) )
	{
		ShiftFlags |= KeyShiftRightAltFlag;
	}

	if ( KeyStateIsDown(KeyState[VK_LWIN]) )
	{
		ShiftFlags |= KeyShiftLeftWinFlag;
	}

	if ( KeyStateIsDown(KeyState[VK_RWIN]) )
	{
		ShiftFlags |= KeyShiftRightWinFlag;
	}

	if ( KeyStateIsToggled(KeyState[VK_CAPITAL]) )
		ShiftFlags |= KeyShiftCapitalFlag;

	//  Set the low order bits to reflect the current key.
	*pShiftStateFlags = ShiftFlags | KeyState[VKey];
	return;
}




/*
   The following defines will be used for converting the key pressed
   into a Unicode character using the context information (the state
   of other keys). We are assuming here that the count of bytes required
   for state info for Unicode character generation (cbToUnicodeState
   field in KEYBD_DRIVER_INFO structure) is zero, ie the driver does
   not maintain any state information (besides CAPS, SHIFT, ALT and
   CONTROL state) for Unicode character generation.
   The Unicode character generated depends on the key pressed and
   the state of the following keys: CAPS, SHIFT, ALT and CONTROL. Hence
   for every Virtual key generated by  the PDD, there can be a total of
   16 different Unicode Characters  in the worst case. Since there are
   a total of 256 different virtual keys  possible, storing all possible
   states would require a lot of memory. Hence rows which are together
   have been grouped and columns which have the same corresponding values
   in every row of the group have been merged together to minimise storage.
   */

#define NO_CHAR         255
#define FIRST_COLUMN    0
#define SECOND_COLUMN   1
#define THIRD_COLUMN    2
#define FOURTH_COLUMN   3
#define FIFTH_COLUMN    4
#define SIXTH_COLUMN    5
#define SAME_VALUE_COLUMN  16

const UINT8 VKeyToUnicodeSpecialMappingTable[8] =
// mapping table for VK_BACK , VK_TAB and VK_RETURN
{
  SAME_VALUE_COLUMN,  // only key
  SAME_VALUE_COLUMN,  // key + shift
  SAME_VALUE_COLUMN,  // key         + caps
  SAME_VALUE_COLUMN,  // key + shift + caps
  FIRST_COLUMN     ,  // key                + control
  NO_CHAR          ,  // key + shift        + control
  SECOND_COLUMN    ,  // key         + caps + control
  NO_CHAR             // key + shift + caps + control
  };

const UINT8 VKeyToUnicodeSpecialTable[6][2] =
{
  {
    127,      // back + Control
    8         // back + Control + Caps
    },
  {
    NO_CHAR,  // tab + Control
    NO_CHAR   // tab + Control + Caps
    },
  {
    NO_CHAR,  // unused VKey
    NO_CHAR   // unused VKey
    },
  {
    NO_CHAR,  // unused VKey
    NO_CHAR   // unused VKey
    },
  {
    NO_CHAR,  // unused VKey
    NO_CHAR   // unused VKey
    },
  {
    10,        // return + control
    10        // return + control + Caps
    }
};

const UINT8 VKeyToUnicodeEscapeMappingTable[8] =
// mapping table for VK_ESCAPE
{
  SAME_VALUE_COLUMN,  // only key
  SAME_VALUE_COLUMN,  // key + shift
  SAME_VALUE_COLUMN,  // key         + caps
  SAME_VALUE_COLUMN,  // key + shift + caps
  SAME_VALUE_COLUMN,  // key                + control
  SAME_VALUE_COLUMN,  // key + shift        + control
  NO_CHAR          ,  // key         + caps + control
  NO_CHAR             // key + shift + caps + control
  };

const UINT8 VKeyToUnicodeSpaceMappingTable[8] =
// mapping table for VK_SPACE
{
  SAME_VALUE_COLUMN,  // only key
  SAME_VALUE_COLUMN,  // key + shift
  SAME_VALUE_COLUMN,  // key         + caps
  SAME_VALUE_COLUMN,  // key + shift + caps
  SAME_VALUE_COLUMN,  // key                + control
  NO_CHAR          ,  // key + shift        + control
  SAME_VALUE_COLUMN,  // key         + caps + control
  NO_CHAR             // key + shift + caps + control
  };

const UINT8 VKeyToUnicodeNumbersMappingTable[8] =
{
  SAME_VALUE_COLUMN,  // only key
  FIRST_COLUMN     ,  // key + shift
  SAME_VALUE_COLUMN,  // key         + caps
  FIRST_COLUMN     ,  // key + shift + caps
  NO_CHAR          ,  // key                + control
  SECOND_COLUMN    ,  // key + shift        + control
  NO_CHAR          ,  // key         + caps + control
  SECOND_COLUMN       // key + shift + caps + control
  };

const UINT8 VKeyToUnicodeNumbersTable[10][2] =
{
  {
    ')'    ,  // SHIFT + 0
    NO_CHAR   // SHIFT + 0 +Control
    },
  {
    '!'    ,  // SHIFT + 1
    NO_CHAR   // SHIFT + 1 +Control
    },
  {
    '@'    ,  // SHIFT + 2
    0        // SHIFT + 2 +Control
    },
  {
    '#'    ,  // SHIFT + 3
    NO_CHAR   // SHIFT + 3 +Control
    },
  {
    '$'    ,  // SHIFT + 4
    NO_CHAR   // SHIFT + 4 +Control
    },
  {
    '%'    ,  // SHIFT + 5
    NO_CHAR   // SHIFT + 5 +Control
    },
  {
    '^'    ,  // SHIFT + 6
    30        // SHIFT + 6 +Control
    },
  {
    '&'    ,  // SHIFT + 7
    NO_CHAR   // SHIFT + 7 +Control
    },
  {
    '*'    ,  // SHIFT + 8
    NO_CHAR   // SHIFT + 8 +Control
    },
  {
    '('    ,  // SHIFT + 9
    NO_CHAR   // SHIFT + 9 +Control
    }

};


const UINT8 VKeyToUnicodeAlphabetsMappingTable[8] =
{
  FIRST_COLUMN,       // only key
  SAME_VALUE_COLUMN,  // key + shift
  SAME_VALUE_COLUMN,  // key         + caps
  FIRST_COLUMN,       // key + shift + caps
  SECOND_COLUMN    ,  // key                + control
  SECOND_COLUMN    ,  // key + shift        + control
  SECOND_COLUMN    ,  // key         + caps + control
  SECOND_COLUMN       // key + shift + caps + control
  };

const UINT8 VKeyToUnicodePunctuation1MappingTable[8] =
{
  FIRST_COLUMN  ,  // only key
  SECOND_COLUMN ,  // key + shift
  FIRST_COLUMN  ,  // key         + caps
  SECOND_COLUMN ,  // key + shift + caps
  NO_CHAR       ,  // key                + control
  THIRD_COLUMN  ,  // key + shift        + control
  NO_CHAR       ,  // key         + caps + control
  THIRD_COLUMN     // key + shift + caps + control
  };

const UINT8 VKeyToUnicodePunctuation1Table[7][3] =
{
  {
    ';'    ,  // semi colon
    ':'    ,  // semi colon +shift
    NO_CHAR   // semi colon +shift +control
    },
  {
    '='     ,  // equal
    '+'     ,  // equal +shift
    NO_CHAR   // equal +shift +control
    },
  {
    ',' ,     // comma
    '<' ,     // comma +shift
    NO_CHAR  // comma +shift +control
    },
  {
    '-' ,  // hyphen
    '_' ,  // hyphen +shift
    31    // hyphen +shift +control
    },
  {
    '.'     ,  // period
    '>'     ,  // period +shift
    NO_CHAR   // period +shift +control
    },
  {
    '/' ,    // slash
    '?' ,    // slash +shift
    NO_CHAR  // slash +shift +control
    },
  {
    '`' ,     // backquote
    '~' ,     // backquote +shift
    NO_CHAR   // backquote +shift +control
    }
};


const UINT8 VKeyToUnicodePunctuation2MappingTable[8] =
{
  FIRST_COLUMN  ,  // only key
  SECOND_COLUMN ,  // key + shift
  FIRST_COLUMN  ,  // key         + caps
  SECOND_COLUMN ,  // key + shift + caps
  THIRD_COLUMN  ,  // key                + control
  NO_CHAR       ,  // key + shift        + control
  THIRD_COLUMN  ,  // key         + caps + control
  NO_CHAR          // key + shift + caps + control
  };


const UINT8 VKeyToUnicodePunctuation2Table[4][3] =
{
  {
    '['    ,  // lsquarebracket
    '{'    ,  // lsquarebracket +shift
    27        // lsquarebracket +control
    },
  {
    '\\',     // backslash
    '|' ,     // backslash +shift
    28       // backslash +control
    },
  {
    ']'  ,    // rsquarebracket
    '}'  ,    // rsquarebracket +shift
    29       //  +control
    },
  {
    '\''   ,  // apostrophe
    '"'   ,  // apostrophe +shift
    NO_CHAR  // apostrophe +control no char
    }
};

#define ALT_INDEX      8
#define CONTROL_INDEX  4
#define CAPS_INDEX     2
#define SHIFT_INDEX    1




/*++

VKeyToUnicode:

Utility function that does the actual Unicode character lookup.  


Return Value:

The Unicode character.

--*/
UINT16
VKeyToUnicode(
	UINT32			VirtualKey,    //	Virtual Key causing the event.
	KEY_STATE_FLAGS	*ShiftFlags   //	State of Shift, control, caps and alt keys.
		    )
{
	UINT32   index=0;
	UINT8    column,return_value = NO_CHAR;

	if ( *ShiftFlags & KeyShiftAnyCtrlFlag)
		index += CONTROL_INDEX;
	
	if ( *ShiftFlags & KeyShiftCapitalFlag)
	{
		index += CAPS_INDEX;
	}
	
	if ( *ShiftFlags & KeyShiftAnyShiftFlag)
		index += SHIFT_INDEX;
	
	if (( *ShiftFlags & KeyShiftAnyAltFlag) &&
	( *ShiftFlags & KeyShiftAnyCtrlFlag) )
	{
		*ShiftFlags |= KeyShiftNoCharacterFlag;
		return  NO_CHAR;
	}


	if ((VirtualKey >= VK_0) && (VirtualKey <= VK_9) )
	{
		column =  VKeyToUnicodeNumbersMappingTable[index];
		switch (column)
		{
			case  SAME_VALUE_COLUMN:
				return  VirtualKey;
			case  NO_CHAR:
				*ShiftFlags |= KeyShiftNoCharacterFlag;
				return  NO_CHAR;
			default:
				return_value = VKeyToUnicodeNumbersTable[VirtualKey - VK_0][column];
		}
	}


	if ((VirtualKey >= 'A') && (VirtualKey <= 'Z') )
	{
		//    NKDbgPrintfW(1, (L"index %d\r\n",index ));
		column =  VKeyToUnicodeAlphabetsMappingTable[index];
		switch (column)
		{
		case  SAME_VALUE_COLUMN:
			return  VirtualKey;
		case  NO_CHAR:
			*ShiftFlags |= KeyShiftNoCharacterFlag;
			return  NO_CHAR;
		case FIRST_COLUMN:
			return  VirtualKey + 'a' - 'A' ;
		case SECOND_COLUMN:
			return  VirtualKey - 'A' + 1 ;
		}
	}
	
	if ((VirtualKey >= VK_SEMICOLON) && (VirtualKey <= VK_BACKQUOTE) )
	{
		column =  VKeyToUnicodePunctuation1MappingTable[index];
		switch (column)
		{
		case  SAME_VALUE_COLUMN:
			return  VirtualKey;
		case  NO_CHAR:
			*ShiftFlags |= KeyShiftNoCharacterFlag;
			return  NO_CHAR;
		default:
			return_value = VKeyToUnicodePunctuation1Table
					[VirtualKey - VK_SEMICOLON][column];
		}
	}

	if ((VirtualKey >= VK_LBRACKET) && (VirtualKey <= VK_APOSTROPHE) )
	{
		column =  VKeyToUnicodePunctuation2MappingTable[index];
		switch (column)
		{
		case  SAME_VALUE_COLUMN:
			return  VirtualKey;
		case  NO_CHAR:
			*ShiftFlags |= KeyShiftNoCharacterFlag;
			return  NO_CHAR;
		default:
			return_value = VKeyToUnicodePunctuation2Table
				[VirtualKey - VK_LBRACKET][column];
		}
	}

	if ((VirtualKey >= VK_BACK) && (VirtualKey <= VK_RETURN) )
	{
		column =  VKeyToUnicodeSpecialMappingTable[index];
		switch (column)
		{
		case  SAME_VALUE_COLUMN:
			return  VirtualKey;
		case  NO_CHAR:
			*ShiftFlags |= KeyShiftNoCharacterFlag;
			return  NO_CHAR;
		default:
			return_value = VKeyToUnicodeSpecialTable
				[VirtualKey - VK_BACK][column];
		}
	}

	if (VirtualKey == VK_ESCAPE )
	{
		column =  VKeyToUnicodeEscapeMappingTable[index];
		switch (column)
		{
		case  SAME_VALUE_COLUMN:
			return  VirtualKey;
		case  NO_CHAR:
			*ShiftFlags |= KeyShiftNoCharacterFlag;
			return  NO_CHAR;
		}
	}

	if (VirtualKey == VK_SPACE )
	{
		column =  VKeyToUnicodeSpaceMappingTable[index];
		switch (column)
		{
		case  SAME_VALUE_COLUMN:
			return  VirtualKey;
		case  NO_CHAR:
			*ShiftFlags |= KeyShiftNoCharacterFlag;
			return  NO_CHAR;
		}
	}
	
	  if (return_value == NO_CHAR) {
		*ShiftFlags |= KeyShiftNoCharacterFlag;
		return  NO_CHAR;
	  } else {
		return return_value;
	  }
}



extern "C"
//      @doc EXTERNAL DRIVERS
/*      @func
	
Generates the appropriate Unicode characters and shift state flags given a 
virtual key, key state array and driver state.  
	
@xref
	
	<tab><t KEY_STATE_FLAGS><nl>
	<tab><t KEY_STATE><nl>
	<tab><t TO_UNICODE_STATE><nl>
	<tab><f KeybdDriverGetInfo><nl>
	<tab><f KeybdDriverInitStates>
	
@comm If KeyState is NULL, the driver must fill in the character which 
corresponds to the unshifted state for the virtual key and return.  
	
@comm In all other cases, this function must at least update the key state 
array and fill in the shift state determined from the key state array and, 
if necessary, update the <t TO_UNICODE_STATE>.  This means that the count 
of characters generated will always be at least 1.  See <t 
KEY_STATE_FLAGS> in order to generate the shift state without a 
corresponding character.  
	
@comm Each character should have a corresponding shift state entered into 
the shift state buffer, even if it is the same as the preceding shift 
state.  
	
@comm Each entry in the character buffer is 32-bits whereas a Unicode 
character is 16 bits.  The driver should clear the high order 16-bits.  
	
	<tab>*((UINT16*)pCharacterBuffer) = Unicode character; // NO doesn't clear high bits.<nl>
	<nl>		
	<tab>*pCharacterBuffer = Unicode character; // YES<nl>

*/
UINT32 KeybdDriverVKeyToUnicode(
	UINT32			VirtualKey,						//	@parm   Virtual Key causing the event.
	KEY_STATE_FLAGS	KeyEvent,						//	@parm   KeyStateDownFlag set or cleared.
	KEY_STATE		KeyState,						//	@parm   Key State.
	void			*pKeybdDriverToUnicodeState,	//	@parm   Driver  specific <t TO_UNICODE_STATE>.
	UINT32			cBufferSize,		//	@parm	Count of 32-bit  entries in each buffer.
	UINT32			*pcCharacters,		//	@parm	Count of characters generated.
	KEY_STATE_FLAGS	*pShiftStateBuffer,	//	@parm	Location to put shift state info.
	UINT32			*pCharacterBuffer	//  @parm	Location to put characters.
	)
{
	KEY_STATE_FLAGS	ShiftFlags;
	UINT32			VKeyCommon;

	RETAILMSG(0, (TEXT("<KBD> enter KeybdDriverVKeyToUnicode()\n"))); 

	VirtualKey &= 0xff;

//	Do special case for MapVirtualKey here.
	if ( KeyState == NULL )
	{
		if ( pCharacterBuffer )
			*pCharacterBuffer = towlower(VirtualKey);
		return ERROR_SUCCESS;
	}

//	We have ToUnicodeState for demo, so check for it.
	if ( pKeybdDriverToUnicodeState == NULL )
	{
		SetLastError(ERROR_INVALID_PARAMETER);
		return ERROR_INVALID_PARAMETER;
	}

	if ( ( cBufferSize != MAX_TO_UNICODE_CHARACTERS ) ||
		 ( pShiftStateBuffer == NULL ) ||
		 ( pCharacterBuffer == NULL ) )
	{
		SetLastError(ERROR_INVALID_PARAMETER);
		return ERROR_INVALID_PARAMETER;
	}

// Update the virtual key state.
	KeyState[VirtualKey] = NewKeyStateFlags(KeyState[VirtualKey], KeyEvent);

// Now check the duplicate keys.
	VKeyCommon = MapLRVKeys(VirtualKey);
	if ( VKeyCommon != VirtualKey )
	{
		KeyState[VKeyCommon] = NewKeyStateFlags(KeyState[VKeyCommon], KeyEvent);
		VirtualKey = VKeyCommon;
	}

// Figure out the new shift state flags.
	KeybdDriverKeyStateToShiftFlags(KeyState, VirtualKey, &ShiftFlags);

// Always return the shift state which counts for one entry.
	*pcCharacters = 1;

// Do multiple characters here if necessary
	if ( KeyEvent & KeyStateDownFlag )
	{
		*pCharacterBuffer = VKeyToUnicode(VirtualKey, &ShiftFlags);
	}
	else
	{
		ShiftFlags |= KeyShiftNoCharacterFlag;
	}

	*pShiftStateBuffer = ShiftFlags;

	return ERROR_SUCCESS;
}

#ifdef DEBUG
PFN_KEYBD_DRIVER_VKEY_TO_UNICODE v_pfnVKeyToUnicodeTest = KeybdDriverVKeyToUnicode;
#endif




extern "C"
//      @doc EXTERNAL DRIVERS
/*		@func

Sets information about the keyboard device.

@rdesc If the function succeeds the return value is TRUE, otherwise, it is 
FALSE.  Extended error information is available via the GetLastError 
function.

@xref
	<tab><f KeybdDriverGetIno><nl>
	<tab><c KBDI_AUTOREPEAT_INFO_ID><nl>

--*/
BOOL KeybdDriverSetMode(
	INT		iKeybdId,	// @parm Id of the keyboard to set the information.
	INT		iIndex,		// @parm Id of info to set.
	LPVOID	lpInput 	// @parm Input buffer.
    )
{
	RETAILMSG(0, (TEXT("<KBD> enter KeybdDriverSetMode()\n"))); 

	if ( lpInput == NULL )
		{
		SetLastError(ERROR_INVALID_PARAMETER);
		return FALSE;
		}

	switch ( iIndex )
	{
		case KBDI_AUTOREPEAT_INFO_ID:
			{
			struct KBDI_AUTOREPEAT_INFO	*pInfo =
					(struct KBDI_AUTOREPEAT_INFO*)lpInput;
			if ( pInfo ->CurrentInitialDelay > AUTO_REPEAT_INITIAL_DELAY_MAX )
			{
//				RETAILMSG(ZONE_DEBUG,
//					(TEXT("Initial delay too large, using %d\r\n"),
//						AUTO_REPEAT_INITIAL_DELAY_MAX));
				v_AutoRepeatInitialDelay = AUTO_REPEAT_INITIAL_DELAY_MAX;
			}
			else if ( pInfo ->CurrentInitialDelay < AUTO_REPEAT_INITIAL_DELAY_MIN )
			{
//				RETAILMSG(ZONE_DEBUG,
//					(TEXT("Initial delay too small, using %d\r\n"),
//						AUTO_REPEAT_INITIAL_DELAY_MIN));
				v_AutoRepeatInitialDelay = AUTO_REPEAT_INITIAL_DELAY_MIN;
			}
			else
			{
				v_AutoRepeatInitialDelay = pInfo ->CurrentInitialDelay;
			}


			if ( pInfo ->CurrentRepeatRate > AUTO_REPEAT_KEYS_PER_SEC_MAX )
			{
//				RETAILMSG(ZONE_DEBUG,
//					(TEXT("Initial repeat rate too large, using %d\r\n"),
//						AUTO_REPEAT_KEYS_PER_SEC_MAX));
				v_AutoRepeatKeysPerSec = AUTO_REPEAT_KEYS_PER_SEC_MAX;
			}
			else if ( ( pInfo ->CurrentRepeatRate < AUTO_REPEAT_KEYS_PER_SEC_MIN ) &&
					  ( pInfo ->CurrentRepeatRate != 0 ) )
			{
//				RETAILMSG(ZONE_DEBUG,
//					(TEXT("Initial repeat rate too small, using %d\r\n"),
//						AUTO_REPEAT_KEYS_PER_SEC_MIN));
				v_AutoRepeatKeysPerSec = AUTO_REPEAT_KEYS_PER_SEC_MIN;
			}
			else
			{
				v_AutoRepeatKeysPerSec = pInfo ->CurrentRepeatRate;
			}

		}
		break;

		default:
			SetLastError(ERROR_INVALID_PARAMETER);
			return FALSE;
	}

	return TRUE;
}
#ifdef DEBUG
PFN_KEYBD_DRIVER_SET_MODE  v_pfnSetModeTest = KeybdDriverSetMode;
#endif




/*++

KeybdSendKeyDownToUser:

On a key down, do all of the mapping on the keyboard and send the 
keys to the input system.  


Notes:

The basic pattern for each special key is to see if we are already sending 
a virtual key and auto-repeat it or check if its particular modifier is 
down and send the modified key or just send the unmodified key.  

For modified keys, we need to make it look like the modifier key went up 
and then the desired virtual key went down.  Additionally, when the 
modifier is the Alt key, we need to send a null character before the Alt 
up so that menus do not activate.  

--*/
void
KeybdSendKeyDownToUser(
	UINT32			VirtualKey,
	KEY_STATE_FLAGS	KeyStateFlags
	)
{
	UINT32	vkUp = 0;	//复合按键功能
	UINT32	vkDown;		//当前按下的键
	BOOL	fSendNull = FALSE;
	UINT32	vkOnly = VirtualKey & 0xff;		//	Just the vkey
	UINT32	vkOther = VirtualKey & ~0xff;	//	Just the other stuff

//	RETAILMSG(ZONE_DEBUG,(TEXT("<KBD> VirtualKey ==0x%x  KeyStateFlags==0x%x !\r\n"),VirtualKey,KeyStateFlags));

	if( vkOnly == VK_OFF )
	{
		vkDown = VK_OFF | KEYBD_DEVICE_SILENT_REPEAT;
	}
	else if( vkOnly == VK_LSHIFT ) /*if VK_LSHIFT and VK_RSHIFT both pressed, virtual key is VK_CAPITAL*/
	{
		if(v_LShiftVKeySent) // Key is auto-repeating.按住Shift不放，则重复发送RShift的键值
		{
			RETAILMSG(1,(_T("LShift is already pressed!\n")));
			vkDown = v_LShiftVKeySent; // Send out whatever we sent last time.
		}
		else if(v_fRShiftDown)	//按下LShift时发现RShift也被按下，则发送VK_CAPITAL
		{
			RETAILMSG(1,(_T("Both LShift and RShift are pressed!\n")));
			vkUp = VK_RSHIFT; // Make the right shift go up...先按下右键不放，再按下左键，则释放右键???		
			//vkDown = v_LShiftVKeySent = v_RShiftVKeySent = VK_CAPITAL | KEYBD_DEVICE_SILENT_REPEAT;
			//lqm:左右shift键一起按下，则上传回车键。测试用。。。
			vkDown = v_LShiftVKeySent = v_RShiftVKeySent = VK_RETURN;	
		}
		else//只有LShift按下，上传LShift的值并同时赋到v_LShiftVKeySent
		{
			RETAILMSG(1,(_T("LShift is pressed!\n")));
			vkDown = v_LShiftVKeySent = VK_LSHIFT | KEYBD_DEVICE_SILENT_REPEAT;		
		}
		v_fLShiftDown = TRUE;
	}
	else if(vkOnly == VK_RSHIFT)// etc.如果RShift被按下
	{
		if(v_RShiftVKeySent)//如果右键已经按下，则继续发送右键键值
		{
			RETAILMSG(1,(_T("RShift is already pressed!\n")));
			vkDown = v_RShiftVKeySent;
		}
		else if(v_fLShiftDown)//如果左键已经按下，则发送组合键值VK_CAPITAL
		{
			RETAILMSG(1,(_T("Both RShift and LShift are pressed!\n")));
			vkUp = VK_LSHIFT;
			vkDown = v_RShiftVKeySent = v_LShiftVKeySent = VK_CAPITAL | KEYBD_DEVICE_SILENT_REPEAT;
		}
		else//如果右键第一次按下，则发送右键键值，同时将键值保存到v_RShiftVKeySent，待抬起后释放
		{
			RETAILMSG(1,(_T("RShift is pressed!\n")));
			vkDown = v_RShiftVKeySent = VK_RSHIFT | KEYBD_DEVICE_SILENT_REPEAT;
		}
		v_fRShiftDown = TRUE;//RShift被按下时将标识位v_fRShiftDown置1
	}
	else if ( vkOnly == VK_BACK ) /*if VK_SHIFT and VK_BACK both pressed, virtual key is VK_DELETE*/
	{
		if ( v_BackVKeySent )
		{
			vkDown = v_BackVKeySent;
		}
		else if ( v_fLShiftDown )
		{
			vkUp = VK_LSHIFT;
			vkDown = v_BackVKeySent = VK_DELETE;
		}
		else if ( v_fRShiftDown )
		{
			vkUp = VK_RSHIFT;
			vkDown = v_BackVKeySent = VK_DELETE;
		}
		else
		{
			vkDown = v_BackVKeySent = VK_BACK;
		}
	}
	else if ( vkOnly == VK_COMMA )//用于逗号键 "," 的常量。
	{
		if ( v_CommaVKeySent )
		{
			vkDown = v_CommaVKeySent;
		}
		else if ( ANY_ALT_DOWN() && !ANY_CTRL_DOWN() && !ANY_SHIFT_DOWN() )//'Alt'+','
		{
			fSendNull = TRUE;
			vkUp = VK_MENU;
			vkDown = v_CommaVKeySent = KEYBD_DEVICE_CONTRAST_LESS;
		}
		else
		{
			vkDown = v_CommaVKeySent = VK_COMMA;
		}
	}
	else if ( vkOnly == VK_PERIOD )//用于句点键 "." 的常量。
	{
		if ( v_PeriodVKeySent )
		{
			vkDown = v_PeriodVKeySent;
		}
		else if ( ANY_ALT_DOWN() && !ANY_CTRL_DOWN() && !ANY_SHIFT_DOWN() )//'Alt'+'.'
		{
			fSendNull = TRUE;
			vkUp = VK_MENU;
			vkDown = v_PeriodVKeySent = KEYBD_DEVICE_CONTRAST_MORE;
		}
		else
		{
			vkDown = v_PeriodVKeySent = VK_PERIOD;
		}
	}
	else if ( vkOnly == VK_RIGHT ) /*if VK_ALT and VK_RIGHT both pressed, virtual key is VK_END*/
	{
		if ( v_RightVKeySent )
		{
			vkDown = v_RightVKeySent;
		}
		else if ( ANY_ALT_DOWN() )
		{
			fSendNull = TRUE;
			vkUp = VK_MENU;
			vkDown = v_RightVKeySent = VK_END;
		}
		else
		{
			vkDown = v_RightVKeySent = VK_RIGHT;
		}
	}
	else if ( vkOnly == VK_LEFT ) /*if VK_ALT and VK_LEFT both pressed, virtual key is VK_HOME*/
	{
		if ( v_LeftVKeySent )
		{
			vkDown = v_LeftVKeySent;
		}
		else if ( ANY_ALT_DOWN() )
		{
			fSendNull = TRUE;
			vkUp = VK_MENU;
			vkDown = v_LeftVKeySent = VK_HOME;
		}
		else
		{
			vkDown = v_LeftVKeySent = VK_LEFT;
		}
	}
	else if ( vkOnly == VK_UP ) /*if VK_ALT and VK_UP both pressed, virtual key is VK_PRIOR*/
	{
		if ( v_UpVKeySent )
		{
			vkDown = v_UpVKeySent;
		}
		else if ( ANY_ALT_DOWN() )
		{
			fSendNull = TRUE;
			vkUp = VK_MENU;
			vkDown = v_UpVKeySent = VK_PRIOR;
		}
		else
		{
			vkDown = v_UpVKeySent = VK_UP;
		}
	}
	else if ( vkOnly == VK_DOWN )  /*if VK_ALT and VK_DOWN both pressed, virtual key is VK_NEXT*/
	{
		if ( v_DownVKeySent )
		{
			vkDown = v_DownVKeySent;
		}
		else if ( ANY_ALT_DOWN() )
		{
			fSendNull = TRUE;
			vkUp = VK_MENU;
			vkDown = v_DownVKeySent = VK_NEXT;
		}
		else
		{
			vkDown = v_DownVKeySent = VK_DOWN;
		}
	}
	else if ( vkOnly == VK_LMENU )  //[左Alt键]/*VK_LMENU /VK_RMENU pressed, alt down */
	{
		vkDown = VK_LMENU | KEYBD_DEVICE_SILENT_REPEAT;
		v_fLAltDown = TRUE;
		v_fLPseudoAltDown = TRUE;
	}
	else if ( vkOnly == VK_RMENU )	//[右Alt键]
	{
		vkDown = VK_RMENU | KEYBD_DEVICE_SILENT_REPEAT;
		v_fRAltDown = TRUE;
		v_fRPseudoAltDown = TRUE;
	}
	else if ( vkOnly == VK_LCONTROL ) /*VK_CONTROL pressed*/
	{
		vkDown = vkOnly | KEYBD_DEVICE_SILENT_REPEAT;
		v_fLCtrlDown = TRUE;
	}
	else if ( vkOnly == VK_RCONTROL )
	{
		vkDown = vkOnly | KEYBD_DEVICE_SILENT_REPEAT;
		v_fRCtrlDown = TRUE;
	}
	else if ( ( vkOnly == VK_LWIN ) ||  ( vkOnly == VK_RWIN ) ) //Windows键
	{
		vkDown = vkOnly | KEYBD_DEVICE_SILENT_REPEAT;
	}
	else
	{
		vkDown = vkOnly;
	}

//	May need to send null character to keep menus from activating.
	if (fSendNull)
	{
		(*v_pfnKeybdEventCallback)(KEYBD_DEVICE_SILENT | vkOther, KeyStateDownFlag);//Virtual key==0 down.	
		(*v_pfnKeybdEventCallback)(vkOther, 0);	//	Virtual key == 0 up.
	}

//	Make it look like the modifier(s) went up.
	if (vkUp)//按住键A后再按住键B，则提交松开A键的消息???
	{
		if(vkUp == VK_MENU)
		{
			if ( v_fLAltDown )
			{
				(*v_pfnKeybdEventCallback)(VK_LMENU | vkOther, 0);
				v_fLPseudoAltDown = FALSE;
			}
			if ( v_fRAltDown )
			{
				(*v_pfnKeybdEventCallback)(VK_RMENU | vkOther, 0);
				v_fRPseudoAltDown = FALSE;
			}
		}
		else
		{
			(*v_pfnKeybdEventCallback)(vkUp | vkOther, 0);
		}
	}

	v_fLastKeyDownWasPseudoAlt = FALSE;

//	Finally!  Send the key down.
	(*v_pfnKeybdEventCallback)(vkDown|vkOther, KeyStateFlags);//将键值和键状态上传给系统
	return;
}


/*++

KeybdSendKeyUpToUser:

On a key up, undo all of the mapping on the keyboard and send the 
keys to the input system.  


Notes:

When a special key is released, we send an up event for whatever virtual 
key was original sent as going down.  

If the special key's modifier key is still down, we send a down event for 
it to keep the state consistent.  (We earlier sent an up event for the modifier
when the special key went down.).

We remember if the modifier key we are resending the down for is the Alt 
key.  If it is, when it is really released, we send a null through the 
system to keep menus from activating.  Otherwise, menus would see this 
down followed directly by an Alt up and so would activate.  

--*/
void
KeybdSendKeyUpToUser(
	UINT32			VirtualKey,
	KEY_STATE_FLAGS	KeyStateFlags
	)
{
	UINT32	vkUp;
	UINT32	vkDown = 0;
	BOOL	fSendUp = TRUE;
	BOOL	fSendNull = FALSE;
	UINT32	vkOnly = VirtualKey & 0xff;		// Just the vkey
	UINT32	vkOther = VirtualKey & ~0xff;	// Just the other stuff

	//	The key up we send is usually this.
	vkUp = vkOnly;
//	RETAILMSG(ZONE_DEBUG,(TEXT("<KBD> VirtualKey ==0x%x  KeyStateFlags==0x%x !\r\n"),VirtualKey,KeyStateFlags));
	if( vkOnly == VK_LSHIFT )
	{
		RETAILMSG(1,(_T("Release VK_LSHIFT.\n")));
		vkUp = v_LShiftVKeySent;			// Whatever we sent out is what goes up.
		v_LShiftVKeySent = 0;				// LShift抬起时，则清空v_LShiftVKeySent
		v_fLShiftDown = FALSE;				// LShift抬起时，则清空标识位v_fLShiftDown
		if(v_fRShiftDown)					// Rshift仍然处于按下状态，则将右键键值存入v_RShiftVKeySent
		{
			vkDown = v_RShiftVKeySent = VK_RSHIFT;
		}
	}
	else if(vkOnly == VK_RSHIFT)			// etc.
	{
		RETAILMSG(1,(_T("Release VK_RSHIFT.\n")));
		vkUp = v_RShiftVKeySent;			// Whatever we sent out is what goes up.
		v_RShiftVKeySent = 0;
		v_fRShiftDown = FALSE;
		if ( v_fLShiftDown )
		{
			vkDown = v_LShiftVKeySent = VK_LSHIFT;
		}
	}
	else if ( vkOnly == VK_BACK )
	{
		vkUp = v_BackVKeySent;
		v_BackVKeySent = 0;
		if ( v_fLShiftDown )
		{
			vkDown = VK_LSHIFT;
		}
		else if ( v_fRShiftDown )
		{
			vkDown = VK_RSHIFT;
		}
	}
	else if ( vkOnly == VK_COMMA )
	{
		vkUp = v_CommaVKeySent;
		v_CommaVKeySent = 0;
		if ( ANY_ALT_DOWN() )
		{
			vkDown = VK_MENU;
		}
	}
	else if ( vkOnly == VK_PERIOD )
	{
		vkUp = v_PeriodVKeySent;
		v_PeriodVKeySent = 0;
		if ( ANY_ALT_DOWN() )
		{
			vkDown = VK_MENU;
		}
	}
	else if ( vkOnly == VK_RIGHT )
	{
		vkUp = v_RightVKeySent;
		v_RightVKeySent = 0;
		if ( ANY_ALT_DOWN() )
		{
			vkDown = VK_MENU;
		}
	}
	else if ( vkOnly == VK_LEFT )
	{
		vkUp = v_LeftVKeySent;
		v_LeftVKeySent = 0;
		if ( ANY_ALT_DOWN() )
		{
			vkDown = VK_MENU;
		}
	}
	else if ( vkOnly == VK_UP )
	{
		vkUp = v_UpVKeySent;
		v_UpVKeySent = 0;
		if ( ANY_ALT_DOWN() )
		{
			vkDown = VK_MENU;
		}
	}
	else if ( vkOnly == VK_DOWN )
	{
		vkUp = v_DownVKeySent;
		v_DownVKeySent = 0;
		if ( ANY_ALT_DOWN() )
		{
			vkDown = VK_MENU;
		}
	}
	else if ( vkOnly == VK_LMENU )
	{
		v_fLAltDown = FALSE;
		//	If we already sent out that the Alt key went up, don't send it again.
		if ( v_fLPseudoAltDown )
		{
			v_fLPseudoAltDown = FALSE;
		}
		else
		{
			fSendUp = FALSE;
		}
	}
	else if ( vkOnly == VK_RMENU )
	{
		v_fRAltDown = FALSE;
		//	If we already sent out that the Alt key went up, don't send it again.
		if ( v_fRPseudoAltDown )
		{
			v_fRPseudoAltDown = FALSE;
		}
		else
		{
			fSendUp = FALSE;
		}
	}
	else if ( vkOnly == VK_LCONTROL )
	{
		v_fLCtrlDown = FALSE;
	}
	else if ( vkOnly == VK_RCONTROL )
	{
		v_fRCtrlDown = FALSE;
	}

	if ( fSendUp )
	{
		/*	If we are about to release the alt key and the last key down we sent 
		was pseudo-alt, send a null character through to keep menus from 
		activating.  */ 
		if ( v_fLastKeyDownWasPseudoAlt &&
			 ( ( vkUp == VK_LMENU ) || ( vkUp == VK_RMENU ) ) )
		{
				(*v_pfnKeybdEventCallback)(
					KEYBD_DEVICE_SILENT | vkOther,		//	Virtual key == 0 down.
					KeyStateDownFlag);
				(*v_pfnKeybdEventCallback)(vkOther, 0);	//	Virtual key == 0 up.
		}
		
		//	Send the virtual key up.
		(*v_pfnKeybdEventCallback)(vkUp|vkOther, KeyStateFlags);		
	}

	//	Make it look like the modifier(s) went down.
	if (vkDown)//如果发现有键释放的同时，仍然还有键按下，则提交处于按下状态的键值给系统
	{
		if ( vkDown == VK_MENU )
		{
			if ( v_fLAltDown )
			{
				(*v_pfnKeybdEventCallback)(VK_LMENU | vkOther | KEYBD_DEVICE_SILENT, KeyStateDownFlag);
				v_fLPseudoAltDown = TRUE;
			}
			if ( v_fRAltDown )
			{
				(*v_pfnKeybdEventCallback)(VK_RMENU | vkOther | KEYBD_DEVICE_SILENT, KeyStateDownFlag);
				v_fRPseudoAltDown = TRUE;
			}
			v_fLastKeyDownWasPseudoAlt = TRUE;
		}
		else
		{
			(*v_pfnKeybdEventCallback)(vkDown | vkOther | KEYBD_DEVICE_SILENT, KeyStateDownFlag);
			v_fLastKeyDownWasPseudoAlt = FALSE;
		}		
	}

	return;
}


DWORD KEYBD_Thread(PVOID pArg)
{
	HANDLE			hKeyIntr;
	UINT32			VirtualKey[16];		//	hardcoded w/ PDD
	KEY_STATE_FLAGS	KeyStateFlags[16];	//  hardcoded w/ PDD
	INT				nKeys;
	DWORD dwRet=0;
	DWORD dwReturnBytes = 0;

	hKeyIntr = CreateEvent(NULL,FALSE,FALSE,NULL);//创建按键中断事件
	if(!hKeyIntr)
	{
		ERRORMSG(1,(TEXT("<KBD> Could not create event handle for keyboard interrupt!!!\r\n")));
		goto exit;
	}
	if(!InterruptInitialize(g_EINT28SysIntr, hKeyIntr, 0, 0))//绑定中断事件与按键中断号
	{
		ERRORMSG(1,	(TEXT("<KBD> InterruptInitialize(g_EINT0SysIntr - %d) failed!!!\r\n"),g_EINT28SysIntr));
		goto exit;
	}
	if(!InterruptInitialize(g_EINT29SysIntr, hKeyIntr, 0, 0))//绑定中断事件与按键中断号
	{
		ERRORMSG(1,	(TEXT("<KBD> InterruptInitialize(g_EINT1SysIntr - %d) failed!!!\r\n"),g_EINT29SysIntr));
		goto exit;
	}
	if(!InterruptInitialize(g_EINT4SysIntr, hKeyIntr, 0, 0))//绑定中断事件与按键中断号
	{
		ERRORMSG(1,	(TEXT("<KBD> InterruptInitialize(g_EINT2SysIntr - %d) failed!!!\r\n"),g_EINT4SysIntr));
		goto exit;
	}
	if(!InterruptInitialize(g_EINT7SysIntr, hKeyIntr, 0, 0))//绑定中断事件与按键中断号
	{
		ERRORMSG(1,	(TEXT("<KBD> InterruptInitialize(g_EINT3SysIntr - %d) failed!!!\r\n"),g_EINT7SysIntr));
		goto exit;
	}
	if(!InterruptInitialize(g_EINT9SysIntr, hKeyIntr, 0, 0))//绑定中断事件与按键中断号
	{
		ERRORMSG(1,	(TEXT("<KBD> InterruptInitialize(g_EINT4SysIntr - %d) failed!!!\r\n"),g_EINT9SysIntr));
		goto exit;
	}
	if(!InterruptInitialize(g_EINT16SysIntr, hKeyIntr, 0, 0))//绑定中断事件与按键中断号
	{
		ERRORMSG(1,	(TEXT("<KBD> InterruptInitialize(g_EINT5SysIntr - %d) failed!!!\r\n"),g_EINT16SysIntr));
		goto exit;
	}

	while(1)
	{
		dwRet=WaitForSingleObject(hKeyIntr,INFINITE);
		if(dwRet == WAIT_OBJECT_0)
		{
			RETAILMSG(0,(TEXT("<KBD> EINT Intr occured!\r\n"))); 
			if(nKeys = KeybdPdd_GetEvent(VirtualKey, KeyStateFlags))
			{
				int	i;
				for( i = 0; i < nKeys; i++ )
				{
					RETAILMSG(0,(TEXT("<KBD> VirtualKey ==0x%x  KeyStateFlags==0x%x !\r\n"),VirtualKey,KeyStateFlags));

					if(KeyStateIsDown(KeyStateFlags[i]))//发送按下键信息
					{
						KeybdSendKeyDownToUser(VirtualKey[i], KeyStateFlags[i]);
					}
					else//发送抬起键信息
					{
						KeybdSendKeyUpToUser(VirtualKey[i], KeyStateFlags[i]);
					}
				}
			}
		}
		else
		{
			CloseHandle(hKeyIntr);
			RETAILMSG(0, (TEXT("[KBD] KEYBD_Thread Exit. \r\n")));
			return 0;
		}
		RETAILMSG(0,(TEXT("<KBD> interrupt done!\r\n"))); 
		
		InterruptDone(g_EINT28SysIntr);
		InterruptDone(g_EINT29SysIntr);
		InterruptDone(g_EINT4SysIntr);
		InterruptDone(g_EINT7SysIntr);
		InterruptDone(g_EINT9SysIntr);
		InterruptDone(g_EINT16SysIntr);
		
	}
exit:
	ERRORMSG(1, (TEXT("<KBD> Keyboard driver thread terminating.\r\n")));
	return 0;
}


#define DEFAULT_THREAD_PRIORITY  	145
#define KEYNAME_KEYBD_DRIVER   		TEXT("\\Drivers\\BuiltIn\\KeyBd")
#define VALNAME_THREAD_PRIO     	TEXT("Priority256")

/*++

Autodoc Information:

    @func DWORD | KeybdDriverpGetPriority |
    This routine reads the KEYBD_Thread thread priority from the registry.

    @rdesc
    Returns thread priority read from registry or the default priority if not
    in the registry.

--*/
static DWORD KeybdDriverpGetPriority(void)
{
    HKEY hKey;
    DWORD dwType;
    DWORD dwVal;
    DWORD dwSize;
    DWORD dwStatus;

    dwStatus = RegOpenKeyEx(
                    HKEY_LOCAL_MACHINE,
                    KEYNAME_KEYBD_DRIVER,
                    0,
                    0,
                    &hKey
                    );
                    
    if (dwStatus)
    {
        DEBUGMSG(1,
        	(TEXT("KEYBDDR:KeybdDriverpGetPriority - RegOpenKeyEx(%s) failed %d, using default thread priority\n"),
            KEYNAME_KEYBD_DRIVER, dwStatus));
        return DEFAULT_THREAD_PRIORITY;
    }

    dwSize = sizeof(DWORD);
    dwStatus = RegQueryValueEx(
                    hKey,
                    VALNAME_THREAD_PRIO,
                    0,
                    &dwType,
                    (PUCHAR)&dwVal,
                    &dwSize
                    );
                    
    if (dwStatus)
    {
        DEBUGMSG(1,
        	(TEXT("KEYBDDR:KeybdDriverpGetPriority - Failed to get %s value, defaulting to %d\r\n"),
            VALNAME_THREAD_PRIO, DEFAULT_THREAD_PRIORITY));
        dwVal = DEFAULT_THREAD_PRIORITY;
    }

    RegCloseKey(hKey);
    
    return dwVal;
}   // KeybdDriverpGetPriority



extern "C"
/*++

@doc EXTERNAL DRIVERS

@func

System power state change notification.

@comm This routine is called in a kernel context and may not make any 
system calls whatsoever.  It may read and write its own memory and that's 
about it.  

@comm Resets the auto-repeat state and calls the <f KeybdPdd_PowerHandler> routine.  

--*/
void KeybdDriverPowerHandler(
	BOOL	bOff	// @parm TRUE, the system is powering off; FALSE, the system is powering up.
	)
{
	RETAILMSG(0, (TEXT("<KBD> enter KeybdDriverPowerHandler(), off=%d\n"), bOff)); 
	KeybdPdd_PowerHandler(bOff);
	return;
}

#ifdef DEBUG
PFN_KEYBD_DRIVER_POWER_HANDLER v_pfnPowerHandler = KeybdDriverPowerHandler;
#endif



extern "C"
//	@doc EXTERNAL DRIVERS
/*	@func Do one time only keyboard driver initialization.

@rdesc If the function succeeds the return value is TRUE, otherwise, it is 
FALSE.  Extended error information is available via the GetLastError 
function.  

@comm Calls <f KeybdPdd_InitializeDriver> then starts driver interrupt 
service thread.
--*/
void KeybdDriverInitialize(
	PFN_KEYBD_EVENT_CALLBACK	pfnKeybdEventCallback	// @parm The callback into the input system.
	)
{
	HANDLE g_KEYBD_Thread;
	DWORD  IDThread;
	UINT Irq;

	RETAILMSG(1, (TEXT("<KBD> enter KeybdDriverInitialize()\n"))); 

	v_pfnKeybdEventCallback = pfnKeybdEventCallback;
	KeybdPdd_InitializeDriver(pfnKeybdEventCallback);

	// Request a SYSINTR value from the OAL.
	Irq=IRQ_EINT28;
	if(!KernelIoControl(IOCTL_HAL_REQUEST_SYSINTR, &Irq, sizeof(Irq), &g_EINT28SysIntr, sizeof(UINT32), NULL))
	{
	    RETAILMSG(1, (TEXT("ERROR: Failed to request the touch sysintr.\r\n")));
	    g_EINT28SysIntr = SYSINTR_UNDEFINED;
	}
	g_KEYBD_Thread = CreateThread(0,0,(LPTHREAD_START_ROUTINE)KEYBD_Thread,0,0,&IDThread);
	if(g_KEYBD_Thread == NULL)
	{
		ERRORMSG(1, (TEXT("<KBD> Could not start keybd driver thread.\r\n")));
		return;
	}
	// Get thread priority from registry and set
	// lqm masked for test.
	//CeSetThreadPriority(g_KEYBD_Thread, KeybdDriverpGetPriority());

	Irq = IRQ_EINT29;
	if(!KernelIoControl(IOCTL_HAL_REQUEST_SYSINTR, &Irq, sizeof(UINT32), &g_EINT29SysIntr, sizeof(UINT32), NULL))
	{
		DEBUGMSG(1, (TEXT("ERROR: Failed to request the KEY sysintr.\r\n")));
		g_EINT29SysIntr = SYSINTR_UNDEFINED;
		return;
	}

	Irq = IRQ_EINT4;
	if (!KernelIoControl(IOCTL_HAL_REQUEST_SYSINTR, &Irq, sizeof(UINT32), &g_EINT4SysIntr, sizeof(UINT32), NULL))
	{
		DEBUGMSG(1, (TEXT("ERROR: Failed to request the KEY sysintr.\r\n")));
		g_EINT4SysIntr = SYSINTR_UNDEFINED;
		return;
	}

	Irq = IRQ_EINT7;
	if (!KernelIoControl(IOCTL_HAL_REQUEST_SYSINTR, &Irq, sizeof(UINT32), &g_EINT7SysIntr, sizeof(UINT32), NULL))
	{
		DEBUGMSG(1, (TEXT("ERROR: Failed to request the KEY sysintr.\r\n")));
		g_EINT7SysIntr = SYSINTR_UNDEFINED;
		return;
	}
	
	Irq = IRQ_EINT9;
	if (!KernelIoControl(IOCTL_HAL_REQUEST_SYSINTR, &Irq, sizeof(UINT32), &g_EINT9SysIntr, sizeof(UINT32), NULL))
	{
		DEBUGMSG(1, (TEXT("ERROR: Failed to request the KEY sysintr.\r\n")));
		g_EINT9SysIntr = SYSINTR_UNDEFINED;
		return;
	}

	
	Irq = IRQ_EINT16;
	if (!KernelIoControl(IOCTL_HAL_REQUEST_SYSINTR, &Irq, sizeof(UINT32), &g_EINT16SysIntr, sizeof(UINT32), NULL))
	{
		DEBUGMSG(1, (TEXT("ERROR: Failed to request the KEY sysintr.\r\n")));
		g_EINT16SysIntr = SYSINTR_UNDEFINED;
		return;
	}

	return;
}

#ifdef DEBUG
PFN_KEYBD_DRIVER_INITIALIZE v_pfnDriverInitializeTest = KeybdDriverInitialize;
#endif

BOOL
WINAPI
DllEntry(
	HANDLE  hinstDLL,
	DWORD   Op,
	LPVOID  lpvReserved
	)
{
	if (!KeybdPdd_DllEntry(hinstDLL, Op, lpvReserved) )
		return FALSE;

	return TRUE;
}
