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