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.
1169 lines
24 KiB
1169 lines
24 KiB
/*++
|
|
|
|
Copyright (c) 1993 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
spkbd.c
|
|
|
|
Abstract:
|
|
|
|
Text setup keyboard support routines.
|
|
|
|
Author:
|
|
|
|
Ted Miller (tedm) 30-July-1993
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
|
|
#include "spprecmp.h"
|
|
#pragma hdrstop
|
|
#include <kbd.h>
|
|
#include <ntddkbd.h>
|
|
|
|
PKBDTABLES KeyboardTable;
|
|
|
|
HANDLE hKeyboard;
|
|
|
|
BOOLEAN KeyboardInitialized = FALSE;
|
|
BOOLEAN KbdLayoutInitialized = FALSE;
|
|
|
|
USHORT CurrentLEDs;
|
|
|
|
//
|
|
// Globals for async I/O calls
|
|
//
|
|
KEYBOARD_INDICATOR_PARAMETERS asyncKbdParams;
|
|
IO_STATUS_BLOCK asyncIoStatusBlock;
|
|
|
|
|
|
#define MAX_KEYBOARD_ITEMS 10
|
|
|
|
|
|
|
|
VOID
|
|
spkbdApcProcedure(
|
|
IN PVOID ApcContext,
|
|
IN PIO_STATUS_BLOCK IoStatusBlock,
|
|
IN ULONG Reserved
|
|
);
|
|
|
|
VOID
|
|
spkbdSetLEDs(
|
|
VOID
|
|
);
|
|
|
|
VOID
|
|
SpkbdInitialize(
|
|
VOID
|
|
);
|
|
|
|
VOID
|
|
SpkbdTerminate(
|
|
VOID
|
|
);
|
|
|
|
VOID
|
|
SpkbdLoadLayoutDll(
|
|
IN PVOID SifHandle,
|
|
IN PWSTR Directory
|
|
);
|
|
|
|
ULONG
|
|
SpkbdGetKeypress(
|
|
VOID
|
|
);
|
|
|
|
BOOLEAN
|
|
SpkbdIsKeyWaiting(
|
|
VOID
|
|
);
|
|
|
|
VOID
|
|
SpkbdDrain(
|
|
VOID
|
|
);
|
|
|
|
|
|
//
|
|
// Buffer for one character.
|
|
//
|
|
volatile ULONG KbdNextChar;
|
|
|
|
|
|
//
|
|
// The following are used in async calls to NtReadFile and so
|
|
// cannot be on the stack.
|
|
//
|
|
IO_STATUS_BLOCK IoStatusKeyboard;
|
|
KEYBOARD_INPUT_DATA KeyboardInputData[MAX_KEYBOARD_ITEMS];
|
|
LARGE_INTEGER DontCareLargeInteger;
|
|
|
|
|
|
//
|
|
// Current state of shift, control, alt keys.
|
|
//
|
|
USHORT ModifierBits = 0;
|
|
|
|
#define START_KEYBOARD_READ() \
|
|
\
|
|
ZwReadFile( \
|
|
hKeyboard, \
|
|
NULL, \
|
|
spkbdApcProcedure, \
|
|
NULL, \
|
|
&IoStatusKeyboard, \
|
|
KeyboardInputData, \
|
|
sizeof(KeyboardInputData), \
|
|
&DontCareLargeInteger, \
|
|
NULL \
|
|
)
|
|
|
|
|
|
|
|
VOID
|
|
SpInputInitialize(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize all input support. This includes
|
|
|
|
- opening the serial port and checking for a terminal.
|
|
- opening the keyboard device.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None. Does not return if not successful.
|
|
|
|
--*/
|
|
|
|
{
|
|
SpkbdInitialize();
|
|
SpTermInitialize();
|
|
}
|
|
|
|
VOID
|
|
SpInputTerminate(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Terminate all input support. This includes
|
|
|
|
- closing the serial port.
|
|
- closing the keyboard device.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
SpkbdTerminate();
|
|
SpTermTerminate();
|
|
}
|
|
|
|
VOID
|
|
SpInputLoadLayoutDll(
|
|
IN PVOID SifHandle,
|
|
IN PWSTR Directory
|
|
)
|
|
{
|
|
SpkbdLoadLayoutDll(SifHandle, Directory);
|
|
}
|
|
|
|
ULONG
|
|
SpInputGetKeypress(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Wait for a keypress and return it to the caller.
|
|
The return value will be an ASCII value (ie, not a scan code).
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
ASCII value.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG Tmp;
|
|
|
|
//
|
|
// If we are in upgrade graphics mode then
|
|
// switch to textmode
|
|
//
|
|
SpvidSwitchToTextmode();
|
|
|
|
while (TRUE) {
|
|
|
|
if (SpTermIsKeyWaiting()) {
|
|
Tmp = SpTermGetKeypress();
|
|
if (Tmp != 0) {
|
|
return Tmp;
|
|
}
|
|
}
|
|
|
|
if (SpkbdIsKeyWaiting()) {
|
|
return SpkbdGetKeypress();
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
BOOLEAN
|
|
SpInputIsKeyWaiting(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Tell the caller if a keypress is waiting to be fetched by
|
|
a call to SpInputGetKeypress().
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
TRUE is key waiting; FALSE otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
return (SpTermIsKeyWaiting() || SpkbdIsKeyWaiting());
|
|
}
|
|
|
|
VOID
|
|
SpInputDrain(
|
|
VOID
|
|
)
|
|
{
|
|
SpTermDrain();
|
|
SpkbdDrain();
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Below here are all the functions for keyboard operations...
|
|
//
|
|
//
|
|
|
|
VOID
|
|
SpkbdInitialize(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize keyboard support. This includes
|
|
|
|
- opening the keyboard device.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None. Does not return if not successful.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
OBJECT_ATTRIBUTES Attributes;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
UNICODE_STRING UnicodeString;
|
|
|
|
ASSERT(!KeyboardInitialized);
|
|
if(KeyboardInitialized) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Open the keyboard.
|
|
//
|
|
RtlInitUnicodeString(&UnicodeString,DD_KEYBOARD_DEVICE_NAME_U L"0");
|
|
|
|
InitializeObjectAttributes(
|
|
&Attributes,
|
|
&UnicodeString,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
Status = ZwCreateFile(
|
|
&hKeyboard,
|
|
GENERIC_READ | SYNCHRONIZE | FILE_READ_ATTRIBUTES,
|
|
&Attributes,
|
|
&IoStatusBlock,
|
|
NULL, // allocation size
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
0, // no sharing
|
|
FILE_OPEN,
|
|
0,
|
|
NULL, // no EAs
|
|
0
|
|
);
|
|
|
|
if(!NT_SUCCESS(Status)) {
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: NtOpenFile of " DD_KEYBOARD_DEVICE_NAME "0 returns %lx\n",Status));
|
|
SpFatalKbdError(SP_SCRN_KBD_OPEN_FAILED);
|
|
}
|
|
|
|
//
|
|
// Initialize LEDs.
|
|
//
|
|
|
|
//
|
|
// No NEC98 has NumLock and NumLock LED.
|
|
// Num keys must be act as Numlock alternated keys.
|
|
//
|
|
CurrentLEDs = (!IsNEC_98 ? 0 : KEYBOARD_NUM_LOCK_ON);
|
|
spkbdSetLEDs();
|
|
|
|
KeyboardInitialized = TRUE;
|
|
|
|
//
|
|
// Do not initialize keyboard input yet because we don't have a layout.
|
|
//
|
|
}
|
|
|
|
|
|
VOID
|
|
SpkbdTerminate(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Terminate keyboard support. This includes
|
|
|
|
- closing the keyboard device.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
ASSERT(KeyboardInitialized);
|
|
|
|
if(KeyboardInitialized) {
|
|
|
|
Status = ZwClose(hKeyboard);
|
|
|
|
if(!NT_SUCCESS(Status)) {
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Unable to close " DD_KEYBOARD_DEVICE_NAME "0 (status = %lx)\n",Status));
|
|
}
|
|
|
|
KeyboardInitialized = FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
SpkbdLoadLayoutDll(
|
|
IN PVOID SifHandle,
|
|
IN PWSTR Directory
|
|
)
|
|
{
|
|
PWSTR p,LayoutDll;
|
|
NTSTATUS Status;
|
|
|
|
//
|
|
// Determine layout name.
|
|
//
|
|
if(p = SpGetSectionKeyIndex(SifHandle,SIF_NLS,SIF_DEFAULTLAYOUT,1)) {
|
|
LayoutDll = p;
|
|
} else {
|
|
p = SpGetSectionKeyIndex(SifHandle,SIF_NLS,SIF_DEFAULTLAYOUT,0);
|
|
if(!p) {
|
|
SpFatalSifError(SifHandle,SIF_NLS,SIF_DEFAULTLAYOUT,0,0);
|
|
}
|
|
|
|
LayoutDll = SpGetSectionKeyIndex(SifHandle,SIF_KEYBOARDLAYOUTFILES,p,0);
|
|
if(!LayoutDll) {
|
|
SpFatalSifError(SifHandle,SIF_KEYBOARDLAYOUTFILES,p,0,0);
|
|
}
|
|
}
|
|
|
|
SpDisplayStatusText(SP_STAT_LOADING_KBD_LAYOUT,DEFAULT_STATUS_ATTRIBUTE,LayoutDll);
|
|
|
|
//
|
|
// Bugcheck if we can't load the layout dll, because we won't be able
|
|
// to put up a screen and ask the user to hit f3, etc.
|
|
//
|
|
Status = SpLoadKbdLayoutDll(Directory,LayoutDll,&KeyboardTable);
|
|
if(!NT_SUCCESS(Status)) {
|
|
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Unable to load layout dll %ws (%lx)\n",LayoutDll,Status));
|
|
SpFatalKbdError(SP_SCRN_KBD_LAYOUT_FAILED, LayoutDll);
|
|
}
|
|
|
|
//
|
|
// Erase status text line.
|
|
//
|
|
SpDisplayStatusOptions(DEFAULT_STATUS_ATTRIBUTE,0);
|
|
|
|
//
|
|
// Now that we've loaded the layout, we can start accepting keyboard input.
|
|
//
|
|
START_KEYBOARD_READ();
|
|
|
|
KbdLayoutInitialized = TRUE;
|
|
}
|
|
|
|
|
|
ULONG
|
|
SpkbdGetKeypress(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Wait for a keypress and return it to the caller.
|
|
The return value will be an ASCII value (ie, not a scan code).
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
ASCII value.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG k;
|
|
|
|
//
|
|
// Shouldn't be calling this until we have loaded a keyboard layout.
|
|
//
|
|
ASSERT(KeyboardTable);
|
|
|
|
//
|
|
// Wait for the user to press a key.
|
|
//
|
|
while(!KbdNextChar) {
|
|
;
|
|
}
|
|
|
|
k = KbdNextChar;
|
|
KbdNextChar = 0;
|
|
|
|
return(k);
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
SpkbdIsKeyWaiting(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Tell the caller if a keypress is waiting to be fetched by
|
|
a call to SpkbdGetKeypress().
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
TRUE is key waiting; FALSE otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
//
|
|
// Shouldn't be calling this until we have loaded a keyboard layout.
|
|
//
|
|
ASSERT(KeyboardTable);
|
|
|
|
return((BOOLEAN)(KbdNextChar != 0));
|
|
}
|
|
|
|
|
|
VOID
|
|
SpkbdDrain(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Drain the keyboard buffer, throwing away any keystrokes
|
|
in the buffer waiting to be fetched.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
TRUE is key waiting; FALSE otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
ASSERT(KeyboardTable);
|
|
|
|
KbdNextChar = 0;
|
|
}
|
|
|
|
|
|
|
|
ULONG
|
|
spkbdScanCodeToChar(
|
|
IN UCHAR Prefix,
|
|
IN USHORT ScanCode,
|
|
IN BOOLEAN Break
|
|
);
|
|
|
|
|
|
VOID
|
|
spkbdApcProcedure(
|
|
IN PVOID ApcContext,
|
|
IN PIO_STATUS_BLOCK IoStatusBlock,
|
|
IN ULONG Reserved
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Async Procedure Call routine for keyboard reads. The I/O
|
|
system will call this routine when the keyboard class driver
|
|
wants to return some data to us.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
UCHAR bPrefix;
|
|
PKEYBOARD_INPUT_DATA pkei;
|
|
ULONG k;
|
|
|
|
UNREFERENCED_PARAMETER(ApcContext);
|
|
UNREFERENCED_PARAMETER(Reserved);
|
|
|
|
for(pkei = KeyboardInputData;
|
|
(PUCHAR)pkei < (PUCHAR)KeyboardInputData + IoStatusBlock->Information;
|
|
pkei++)
|
|
{
|
|
if(pkei->Flags & KEY_E0) {
|
|
bPrefix = 0xE0;
|
|
} else if (pkei->Flags & KEY_E1) {
|
|
bPrefix = 0xE1;
|
|
} else {
|
|
bPrefix = 0;
|
|
}
|
|
|
|
k = spkbdScanCodeToChar(
|
|
bPrefix,
|
|
pkei->MakeCode,
|
|
(BOOLEAN)((pkei->Flags & KEY_BREAK) != 0)
|
|
);
|
|
|
|
if(k) {
|
|
KbdNextChar = k;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Keyboard might have been terminated.
|
|
//
|
|
if(KeyboardInitialized) {
|
|
START_KEYBOARD_READ();
|
|
}
|
|
}
|
|
|
|
|
|
WCHAR SavedDeadChar = 0;
|
|
UCHAR AltNumpadAccum = 0;
|
|
|
|
struct {
|
|
BYTE CursorKey;
|
|
BYTE NumberKey;
|
|
BYTE Value;
|
|
} NumpadCursorToNumber[] = { { VK_INSERT, VK_NUMPAD0, 0 },
|
|
{ VK_END , VK_NUMPAD1, 1 },
|
|
{ VK_DOWN , VK_NUMPAD2, 2 },
|
|
{ VK_NEXT , VK_NUMPAD3, 3 },
|
|
{ VK_LEFT , VK_NUMPAD4, 4 },
|
|
{ VK_CLEAR , VK_NUMPAD5, 5 },
|
|
{ VK_RIGHT , VK_NUMPAD6, 6 },
|
|
{ VK_HOME , VK_NUMPAD7, 7 },
|
|
{ VK_UP , VK_NUMPAD8, 8 },
|
|
{ VK_PRIOR , VK_NUMPAD9, 9 },
|
|
{ VK_DELETE, VK_DECIMAL, 10 }, // no value.
|
|
{ 0 , 0 , 0 }
|
|
};
|
|
|
|
ULONG
|
|
spkbdScanCodeToChar(
|
|
IN UCHAR Prefix,
|
|
IN USHORT ScanCode,
|
|
IN BOOLEAN Break
|
|
)
|
|
{
|
|
USHORT VKey = 0;
|
|
PVSC_VK VscVk;
|
|
PVK_TO_WCHAR_TABLE pVKT;
|
|
PVK_TO_WCHARS1 pVK;
|
|
USHORT Modifier;
|
|
USHORT ModBits,ModNum;
|
|
WCHAR deadChar;
|
|
|
|
ScanCode &= 0x7f;
|
|
|
|
if(Prefix == 0) {
|
|
|
|
if(ScanCode < KeyboardTable->bMaxVSCtoVK) {
|
|
|
|
//
|
|
// Index directly into non-prefix scan code table.
|
|
//
|
|
VKey = KeyboardTable->pusVSCtoVK[ScanCode];
|
|
if(VKey == 0) {
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Unknown scan code 0x%x\n",ScanCode));
|
|
return (0);
|
|
}
|
|
} else {
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Unknown scan code 0x%x\n",ScanCode));
|
|
return(0);
|
|
}
|
|
} else {
|
|
if(Prefix == 0xe0) {
|
|
//
|
|
// Ignore the SHIFT keystrokes generated by the hardware
|
|
//
|
|
if((ScanCode == SCANCODE_LSHIFT) || (ScanCode == SCANCODE_RSHIFT)) {
|
|
return(0);
|
|
}
|
|
VscVk = KeyboardTable->pVSCtoVK_E0;
|
|
} else if(Prefix == 0xe1) {
|
|
VscVk = KeyboardTable->pVSCtoVK_E1;
|
|
} else {
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Unknown keyboard scan prefix 0x%x\n",Prefix));
|
|
return(0);
|
|
}
|
|
|
|
while(VscVk->Vk) {
|
|
if(VscVk->Vsc == ScanCode) {
|
|
VKey = VscVk->Vk;
|
|
break;
|
|
}
|
|
VscVk++;
|
|
}
|
|
if(VKey == 0) {
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Unknown keyboard scan prefix/code 0x%x/0x%x\n",Prefix,ScanCode));
|
|
return(0);
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// VirtualKey --> modifier bits. This translation is also
|
|
// mapped out in the pCharModifiers field in the keyboard layout
|
|
// table but that seems redundant.
|
|
//
|
|
Modifier = 0;
|
|
switch(VKey & 0xff) {
|
|
case VK_LSHIFT:
|
|
case VK_RSHIFT:
|
|
Modifier = KBDSHIFT;
|
|
break;
|
|
case VK_LCONTROL:
|
|
case VK_RCONTROL:
|
|
Modifier = KBDCTRL;
|
|
break;
|
|
case VK_RMENU:
|
|
//
|
|
// AltGr ==> control+alt modifier.
|
|
//
|
|
if(KeyboardTable->fLocaleFlags & KLLF_ALTGR) {
|
|
Modifier = KBDCTRL;
|
|
}
|
|
// fall through
|
|
case VK_LMENU:
|
|
Modifier |= KBDALT;
|
|
break;
|
|
}
|
|
|
|
if(Break) {
|
|
//
|
|
// Key is being released.
|
|
// If it's not a modifer, ignore it.
|
|
//
|
|
if(!Modifier) {
|
|
return(0);
|
|
}
|
|
//
|
|
// Key being released is a modifier.
|
|
//
|
|
ModifierBits &= ~Modifier;
|
|
|
|
//
|
|
// If it's ALT going up and we have a numpad key being entered,
|
|
// return it.
|
|
//
|
|
if((Modifier & KBDALT) && AltNumpadAccum) {
|
|
|
|
WCHAR UnicodeChar;
|
|
|
|
RtlOemToUnicodeN(
|
|
&UnicodeChar,
|
|
sizeof(UnicodeChar),
|
|
NULL,
|
|
&AltNumpadAccum,
|
|
1
|
|
);
|
|
|
|
AltNumpadAccum = 0;
|
|
|
|
return(UnicodeChar);
|
|
}
|
|
return(0);
|
|
} else {
|
|
if(Modifier) {
|
|
//
|
|
// Key is being pressed and is a modifier.
|
|
//
|
|
ModifierBits |= Modifier;
|
|
|
|
//
|
|
// If ALT is going down, reset alt+numpad value.
|
|
//
|
|
if(Modifier & KBDALT) {
|
|
AltNumpadAccum = 0;
|
|
}
|
|
return(0);
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we get here, we've got a non-modifier key being made (pressed).
|
|
// If the previous key was a dead key, the user gets only
|
|
// one try to get a valid second half.
|
|
//
|
|
deadChar = SavedDeadChar;
|
|
SavedDeadChar = 0;
|
|
|
|
|
|
//
|
|
// Special processing if the key is a numeric keypad key.
|
|
//
|
|
if(VKey & KBDNUMPAD) {
|
|
|
|
int i;
|
|
|
|
for(i=0; NumpadCursorToNumber[i].CursorKey; i++) {
|
|
if(NumpadCursorToNumber[i].CursorKey == (BYTE)VKey) {
|
|
|
|
//
|
|
// Key is a numeric keypad key. If ALT (and only alt) is down,
|
|
// then we have an alt+numpad code being entered.
|
|
//
|
|
if(((ModifierBits & ~KBDALT) == 0) && (NumpadCursorToNumber[i].Value < 10)) {
|
|
|
|
AltNumpadAccum = (AltNumpadAccum * 10) + NumpadCursorToNumber[i].Value;
|
|
}
|
|
|
|
//
|
|
// If numlock is on, translate the key from cursor movement
|
|
// to a number key.
|
|
//
|
|
if(CurrentLEDs & KEYBOARD_NUM_LOCK_ON) {
|
|
VKey = NumpadCursorToNumber[i].NumberKey;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// We need to filter out keystrokes that we know are not part of any
|
|
// character set here.
|
|
//
|
|
if((!deadChar)) {
|
|
switch(VKey & 0xff) {
|
|
case VK_CAPITAL:
|
|
if(CurrentLEDs & KEYBOARD_CAPS_LOCK_ON) {
|
|
CurrentLEDs &= ~KEYBOARD_CAPS_LOCK_ON;
|
|
} else {
|
|
CurrentLEDs |= KEYBOARD_CAPS_LOCK_ON;
|
|
}
|
|
spkbdSetLEDs();
|
|
return(0);
|
|
case VK_NUMLOCK:
|
|
if(CurrentLEDs & KEYBOARD_NUM_LOCK_ON) {
|
|
CurrentLEDs &= ~KEYBOARD_NUM_LOCK_ON;
|
|
} else {
|
|
CurrentLEDs |= KEYBOARD_NUM_LOCK_ON;
|
|
}
|
|
spkbdSetLEDs();
|
|
return(0);
|
|
case VK_PRIOR:
|
|
return(KEY_PAGEUP);
|
|
case VK_NEXT:
|
|
return(KEY_PAGEDOWN);
|
|
case VK_UP:
|
|
return(KEY_UP);
|
|
case VK_DOWN:
|
|
return(KEY_DOWN);
|
|
case VK_LEFT:
|
|
return(KEY_LEFT);
|
|
case VK_RIGHT:
|
|
return(KEY_RIGHT);
|
|
case VK_HOME:
|
|
return(KEY_HOME);
|
|
case VK_END:
|
|
return(KEY_END);
|
|
case VK_INSERT:
|
|
return(KEY_INSERT);
|
|
case VK_DELETE:
|
|
return(KEY_DELETE);
|
|
case VK_F1:
|
|
return(KEY_F1);
|
|
case VK_F2:
|
|
return(KEY_F2);
|
|
case VK_F3:
|
|
return(KEY_F3);
|
|
case VK_F4:
|
|
return(KEY_F4);
|
|
case VK_F5:
|
|
return(KEY_F5);
|
|
case VK_F6:
|
|
return(KEY_F6);
|
|
case VK_F7:
|
|
return(KEY_F7);
|
|
case VK_F8:
|
|
return(KEY_F8);
|
|
case VK_F9:
|
|
return(KEY_F9);
|
|
case VK_F10:
|
|
return(KEY_F10);
|
|
case VK_F11:
|
|
return(KEY_F11);
|
|
case VK_F12:
|
|
return(KEY_F12);
|
|
}
|
|
}
|
|
|
|
//
|
|
// We think the character is probably a 'real' character.
|
|
// Scan through all the shift-state tables until a matching Virtual
|
|
// Key is found.
|
|
//
|
|
for(pVKT = KeyboardTable->pVkToWcharTable; pVKT->pVkToWchars; pVKT++) {
|
|
pVK = pVKT->pVkToWchars;
|
|
while(pVK->VirtualKey) {
|
|
if(pVK->VirtualKey == (BYTE)VKey) {
|
|
goto VK_Found;
|
|
}
|
|
pVK = (PVK_TO_WCHARS1)((PBYTE)pVK + pVKT->cbSize);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Key is not valid with requested modifiers.
|
|
//
|
|
return(0);
|
|
|
|
VK_Found:
|
|
|
|
ModBits = ModifierBits;
|
|
|
|
//
|
|
// If CapsLock affects this key and it is on: toggle SHIFT state
|
|
// only if no other state is on.
|
|
// (CapsLock doesn't affect SHIFT state if Ctrl or Alt are down).
|
|
//
|
|
if((pVK->Attributes & CAPLOK) && ((ModBits & ~KBDSHIFT) == 0)
|
|
&& (CurrentLEDs & KEYBOARD_CAPS_LOCK_ON))
|
|
{
|
|
ModBits ^= KBDSHIFT;
|
|
}
|
|
|
|
//
|
|
// Get the modification number.
|
|
//
|
|
if(ModBits > KeyboardTable->pCharModifiers->wMaxModBits) {
|
|
return(0); // invalid keystroke
|
|
}
|
|
|
|
ModNum = KeyboardTable->pCharModifiers->ModNumber[ModBits];
|
|
if(ModNum == SHFT_INVALID) {
|
|
return(0); // invalid keystroke
|
|
}
|
|
|
|
if(ModNum >= pVKT->nModifications) {
|
|
|
|
//
|
|
// Key is not valid with current modifiers.
|
|
// Could still be a control char that we can convert directly.
|
|
//
|
|
if((ModBits == KBDCTRL) || (ModBits == (KBDCTRL | KBDSHIFT))) {
|
|
if(((UCHAR)VKey >= 'A') && ((UCHAR)VKey <= 'Z')) {
|
|
return((ULONG)VKey & 0x1f);
|
|
}
|
|
}
|
|
return(0); // invalid keystroke
|
|
}
|
|
|
|
if(pVK->wch[ModNum] == WCH_NONE) {
|
|
return(0);
|
|
}
|
|
|
|
if((pVK->wch[ModNum] == WCH_DEAD)) {
|
|
|
|
if(!deadChar) {
|
|
//
|
|
// Remember the current dead character, whose value follows
|
|
// the current entry in the modifier mapping table.
|
|
//
|
|
SavedDeadChar = ((PVK_TO_WCHARS1)((PUCHAR)pVK + pVKT->cbSize))->wch[ModNum];
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
//
|
|
// The keyboard layout table contains some dead key mappings.
|
|
// If previous key was a dead key, attempt to compose it with the
|
|
// current character by scanning the keyboard layout table for a match.
|
|
//
|
|
if(deadChar) {
|
|
|
|
ULONG chr;
|
|
PDEADKEY DeadKeyEntry;
|
|
|
|
chr = MAKELONG(pVK->wch[ModNum],deadChar);
|
|
|
|
if(DeadKeyEntry = KeyboardTable->pDeadKey) {
|
|
|
|
while(DeadKeyEntry->dwBoth) {
|
|
|
|
if(DeadKeyEntry->dwBoth == chr) {
|
|
//
|
|
// Got a match. Return the composed character.
|
|
//
|
|
return((ULONG)DeadKeyEntry->wchComposed);
|
|
}
|
|
|
|
DeadKeyEntry++;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we get here, then the previous key was a dead char,
|
|
// but the current key could not be composed with it.
|
|
// So return nothing. Note that the dead key has been forgotten.
|
|
//
|
|
return(0);
|
|
}
|
|
|
|
|
|
return((ULONG)pVK->wch[ModNum]);
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
spkbdSetLEDs(
|
|
VOID
|
|
)
|
|
{
|
|
asyncKbdParams.UnitId = 0;
|
|
asyncKbdParams.LedFlags = CurrentLEDs;
|
|
|
|
ZwDeviceIoControlFile(
|
|
hKeyboard,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&asyncIoStatusBlock,
|
|
IOCTL_KEYBOARD_SET_INDICATORS,
|
|
&asyncKbdParams,
|
|
sizeof(asyncKbdParams),
|
|
NULL,
|
|
0
|
|
);
|
|
}
|
|
|
|
VOID
|
|
SpSelectAndLoadLayoutDll(
|
|
IN PWSTR Directory,
|
|
IN PVOID SifHandle,
|
|
IN BOOLEAN ShowStatus
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Allows the user to select a keyboard layout DLL and loads it.
|
|
|
|
|
|
Arguments:
|
|
|
|
Directory - The setup startup directory
|
|
SifHandle - Handle to txtsetup.sif
|
|
ShowStatus - Whether status should be displayed or not
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
ULONG SelectedLayout;
|
|
ULONG DefLayout = -1;
|
|
PWSTR TempPtr = 0;
|
|
PWSTR LayoutDll = 0;
|
|
NTSTATUS Status;
|
|
|
|
//
|
|
// Get the default layout (index)
|
|
//
|
|
TempPtr = SpGetSectionKeyIndex(SifHandle, SIF_NLS, SIF_DEFAULTLAYOUT, 0);
|
|
|
|
if(!TempPtr) {
|
|
SpFatalSifError(SifHandle, SIF_NLS, SIF_DEFAULTLAYOUT, 0, 0);
|
|
}
|
|
|
|
DefLayout = SpGetKeyIndex(SifHandle, SIF_KEYBOARDLAYOUTDESC, TempPtr);
|
|
|
|
if(DefLayout == -1) {
|
|
SpFatalSifError(SifHandle, SIF_NLS, SIF_DEFAULTLAYOUT, 0, 0);
|
|
}
|
|
|
|
SelectedLayout = -1;
|
|
|
|
//
|
|
// Let the user select the layout which he wants
|
|
//
|
|
if (SpSelectSectionItem(SifHandle, SIF_KEYBOARDLAYOUTDESC,
|
|
SP_SELECT_KBDLAYOUT, DefLayout, &SelectedLayout)) {
|
|
//
|
|
// Load the layout if its not already loaded
|
|
//
|
|
if (DefLayout != SelectedLayout) {
|
|
//
|
|
// get the key
|
|
//
|
|
TempPtr = SpGetKeyName(SifHandle, SIF_KEYBOARDLAYOUTDESC, SelectedLayout);
|
|
|
|
if (TempPtr) {
|
|
//
|
|
// get the KDB layout dll name
|
|
//
|
|
LayoutDll = SpGetSectionKeyIndex(SifHandle, SIF_KEYBOARDLAYOUTFILES,
|
|
TempPtr, 0);
|
|
|
|
if (LayoutDll) {
|
|
if (ShowStatus) {
|
|
SpDisplayStatusText(SP_STAT_LOADING_KBD_LAYOUT,
|
|
DEFAULT_STATUS_ATTRIBUTE, LayoutDll);
|
|
}
|
|
|
|
//
|
|
// Load the new KDB layout dll
|
|
//
|
|
Status = SpLoadKbdLayoutDll(Directory, LayoutDll, &KeyboardTable);
|
|
}
|
|
else
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
CLEAR_ENTIRE_SCREEN();
|
|
SpFatalKbdError(SP_SCRN_KBD_LAYOUT_FAILED, LayoutDll);
|
|
} else {
|
|
if (ShowStatus) {
|
|
//
|
|
// Erase status text line.
|
|
//
|
|
SpDisplayStatusOptions(DEFAULT_STATUS_ATTRIBUTE, 0);
|
|
}
|
|
|
|
//
|
|
// Now that we've loaded the layout,
|
|
// we can start accepting keyboard input.
|
|
//
|
|
START_KEYBOARD_READ();
|
|
KbdLayoutInitialized = TRUE;
|
|
}
|
|
} else {
|
|
CLEAR_ENTIRE_SCREEN();
|
|
SpFatalSifError(SifHandle, SIF_KEYBOARDLAYOUTDESC, 0, 0, 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|