[console] Centralise handling of key modifiers

Handle Ctrl and CapsLock key modifiers within key_remap(), to provide
consistent behaviour across different console types.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
pull/591/head
Michael Brown 2022-02-14 16:31:08 +00:00
parent 871dd236d4
commit f2a59d5973
6 changed files with 116 additions and 37 deletions

View File

@ -6,6 +6,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#define BDA_SEG 0x0040 #define BDA_SEG 0x0040
#define BDA_EBDA 0x000e #define BDA_EBDA 0x000e
#define BDA_EQUIPMENT_WORD 0x0010 #define BDA_EQUIPMENT_WORD 0x0010
#define BDA_KB0 0x0017
#define BDA_KB0_CTRL 0x04
#define BDA_KB0_CAPSLOCK 0x040
#define BDA_FBMS 0x0013 #define BDA_FBMS 0x0013
#define BDA_TICKS 0x006c #define BDA_TICKS 0x006c
#define BDA_MIDNIGHT 0x0070 #define BDA_MIDNIGHT 0x0070

View File

@ -361,6 +361,7 @@ static const char * bios_ansi_seq ( unsigned int scancode ) {
*/ */
static int bios_getchar ( void ) { static int bios_getchar ( void ) {
uint16_t keypress; uint16_t keypress;
uint8_t kb0;
unsigned int scancode; unsigned int scancode;
unsigned int character; unsigned int character;
const char *ansi_seq; const char *ansi_seq;
@ -385,16 +386,28 @@ static int bios_getchar ( void ) {
bios_inject_lock--; bios_inject_lock--;
scancode = ( keypress >> 8 ); scancode = ( keypress >> 8 );
character = ( keypress & 0xff ); character = ( keypress & 0xff );
get_real ( kb0, BDA_SEG, BDA_KB0 );
/* If it's a normal character, map (if applicable) and return it */ /* If it's a normal character, map (if applicable) and return it */
if ( character && ( character < 0x80 ) ) { if ( character && ( character < 0x80 ) ) {
if ( scancode < SCANCODE_RSHIFT ) {
return key_remap ( character ); /* Handle special scancodes */
} else if ( scancode == SCANCODE_NON_US ) { if ( scancode == SCANCODE_NON_US ) {
return key_remap ( character | KEYMAP_PSEUDO ); /* Treat as "\|" with high bit set */
} else { character |= KEYMAP_PSEUDO;
} else if ( scancode >= SCANCODE_RSHIFT ) {
/* Non-remappable scancode (e.g. numeric keypad) */
return character; return character;
} }
/* Apply modifiers */
if ( kb0 & BDA_KB0_CTRL )
character |= KEYMAP_CTRL;
if ( kb0 & BDA_KB0_CAPSLOCK )
character |= KEYMAP_CAPSLOCK_REDO;
/* Map and return */
return key_remap ( character );
} }
/* Otherwise, check for a special key that we know about */ /* Otherwise, check for a special key that we know about */

View File

@ -23,6 +23,8 @@
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ctype.h>
#include <ipxe/keys.h>
#include <ipxe/keymap.h> #include <ipxe/keymap.h>
/** @file /** @file
@ -31,6 +33,18 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
* *
*/ */
/** ASCII character mask */
#define ASCII_MASK 0x7f
/** Control character mask */
#define CTRL_MASK 0x1f
/** Upper case character mask */
#define UPPER_MASK 0x5f
/** Case toggle bit */
#define CASE_TOGGLE ( ASCII_MASK & ~UPPER_MASK )
/** Default keyboard mapping */ /** Default keyboard mapping */
static TABLE_START ( keymap_start, KEYMAP ); static TABLE_START ( keymap_start, KEYMAP );
@ -41,21 +55,36 @@ static struct keymap *keymap = keymap_start;
* Remap a key * Remap a key
* *
* @v character Character read from console * @v character Character read from console
* @ret character Mapped character * @ret mapped Mapped character
*/ */
unsigned int key_remap ( unsigned int character ) { unsigned int key_remap ( unsigned int character ) {
unsigned int mapped = ( character & KEYMAP_MASK );
struct keymap_key *key; struct keymap_key *key;
/* Invert case before remapping if applicable */
if ( ( character & KEYMAP_CAPSLOCK_UNDO ) && isalpha ( mapped ) )
mapped ^= CASE_TOGGLE;
/* Remap via table */ /* Remap via table */
for ( key = keymap->basic ; key->from ; key++ ) { for ( key = keymap->basic ; key->from ; key++ ) {
if ( key->from == character ) { if ( mapped == key->from ) {
character = key->to; mapped = key->to;
break; break;
} }
} }
/* Clear pseudo key flag */ /* Handle Ctrl-<key> and CapsLock */
character &= ~KEYMAP_PSEUDO; if ( isalpha ( mapped ) ) {
if ( character & KEYMAP_CTRL ) {
mapped &= CTRL_MASK;
} else if ( character & KEYMAP_CAPSLOCK ) {
mapped ^= CASE_TOGGLE;
}
}
return character; /* Clear flags */
mapped &= ASCII_MASK;
DBGC2 ( &keymap, "KEYMAP mapped %04x => %02x\n", character, mapped );
return mapped;
} }

View File

@ -71,6 +71,9 @@ static unsigned int usbkbd_map ( unsigned int keycode, unsigned int modifiers,
} else if ( keycode <= USBKBD_KEY_Z ) { } else if ( keycode <= USBKBD_KEY_Z ) {
/* Alphabetic keys */ /* Alphabetic keys */
key = ( keycode - USBKBD_KEY_A + 'a' ); key = ( keycode - USBKBD_KEY_A + 'a' );
if ( modifiers & USBKBD_SHIFT ) {
key -= ( 'a' - 'A' );
}
} else if ( keycode <= USBKBD_KEY_0 ) { } else if ( keycode <= USBKBD_KEY_0 ) {
/* Numeric key row */ /* Numeric key row */
if ( modifiers & USBKBD_SHIFT ) { if ( modifiers & USBKBD_SHIFT ) {
@ -125,17 +128,15 @@ static unsigned int usbkbd_map ( unsigned int keycode, unsigned int modifiers,
/* Remap key if applicable */ /* Remap key if applicable */
if ( ( keycode < USBKBD_KEY_CAPS_LOCK ) || if ( ( keycode < USBKBD_KEY_CAPS_LOCK ) ||
( keycode == USBKBD_KEY_NON_US ) ) { ( keycode == USBKBD_KEY_NON_US ) ) {
key = key_remap ( key );
}
/* Handle upper/lower case and Ctrl-<key> */ /* Apply modifiers */
if ( islower ( key ) ) { if ( modifiers & USBKBD_CTRL )
if ( modifiers & USBKBD_CTRL ) { key |= KEYMAP_CTRL;
key -= ( 'a' - CTRL_A ); if ( leds & USBKBD_LED_CAPS_LOCK )
} else if ( ( modifiers & USBKBD_SHIFT ) || key |= KEYMAP_CAPSLOCK;
( leds & USBKBD_LED_CAPS_LOCK ) ) {
key -= ( 'a' - 'A' ); /* Remap key */
} key = key_remap ( key );
} }
return key; return key;

View File

@ -40,9 +40,30 @@ struct keymap {
/** Define a keyboard mapping */ /** Define a keyboard mapping */
#define __keymap __table_entry ( KEYMAP, 01 ) #define __keymap __table_entry ( KEYMAP, 01 )
/** Mappable character mask */
#define KEYMAP_MASK 0xff
/** Pseudo key flag */ /** Pseudo key flag */
#define KEYMAP_PSEUDO 0x80 #define KEYMAP_PSEUDO 0x80
/** Ctrl key flag */
#define KEYMAP_CTRL 0x0100
/** CapsLock key flag */
#define KEYMAP_CAPSLOCK 0x0200
/** Undo CapsLock key flag
*
* Used when the keyboard driver has already interpreted the CapsLock
* key, in which case the effect needs to be undone before remapping
* in order to correctly handle keyboard mappings that swap alphabetic
* and non-alphabetic keys.
*/
#define KEYMAP_CAPSLOCK_UNDO 0x0400
/** Undo and redo CapsLock key flags */
#define KEYMAP_CAPSLOCK_REDO ( KEYMAP_CAPSLOCK | KEYMAP_CAPSLOCK_UNDO )
extern unsigned int key_remap ( unsigned int character ); extern unsigned int key_remap ( unsigned int character );
#endif /* _IPXE_KEYMAP_H */ #endif /* _IPXE_KEYMAP_H */

View File

@ -55,8 +55,6 @@ FILE_LICENCE ( GPL2_OR_LATER );
#define ATTR_DEFAULT ATTR_FCOL_WHITE #define ATTR_DEFAULT ATTR_FCOL_WHITE
#define CTRL_MASK 0x1f
/* Set default console usage if applicable */ /* Set default console usage if applicable */
#if ! ( defined ( CONSOLE_EFI ) && CONSOLE_EXPLICIT ( CONSOLE_EFI ) ) #if ! ( defined ( CONSOLE_EFI ) && CONSOLE_EXPLICIT ( CONSOLE_EFI ) )
#undef CONSOLE_EFI #undef CONSOLE_EFI
@ -286,6 +284,9 @@ static int efi_getchar ( void ) {
EFI_SIMPLE_TEXT_INPUT_PROTOCOL *conin = efi_systab->ConIn; EFI_SIMPLE_TEXT_INPUT_PROTOCOL *conin = efi_systab->ConIn;
EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *conin_ex = efi_conin_ex; EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *conin_ex = efi_conin_ex;
const char *ansi_seq; const char *ansi_seq;
unsigned int character;
unsigned int shift;
unsigned int toggle;
EFI_KEY_DATA key; EFI_KEY_DATA key;
EFI_STATUS efirc; EFI_STATUS efirc;
int rc; int rc;
@ -318,24 +319,35 @@ static int efi_getchar ( void ) {
key.KeyState.KeyToggleState, key.Key.UnicodeChar, key.KeyState.KeyToggleState, key.Key.UnicodeChar,
key.Key.ScanCode ); key.Key.ScanCode );
/* Remap key. There is unfortunately no way to avoid /* If key has a Unicode representation, remap and return it.
* remapping the numeric keypad, since EFI destroys the scan * There is unfortunately no way to avoid remapping the
* code information that would allow us to differentiate * numeric keypad, since EFI destroys the scan code
* between main keyboard and numeric keypad. * information that would allow us to differentiate between
* main keyboard and numeric keypad.
*/ */
key.Key.UnicodeChar = key_remap ( key.Key.UnicodeChar ); if ( ( character = key.Key.UnicodeChar ) != 0 ) {
/* Translate Ctrl-<key> */ /* Apply shift state */
if ( ( key.KeyState.KeyShiftState & EFI_SHIFT_STATE_VALID ) && shift = key.KeyState.KeyShiftState;
( key.KeyState.KeyShiftState & ( EFI_LEFT_CONTROL_PRESSED | if ( shift & EFI_SHIFT_STATE_VALID ) {
EFI_RIGHT_CONTROL_PRESSED ) ) ) { if ( shift & ( EFI_LEFT_CONTROL_PRESSED |
key.Key.UnicodeChar &= CTRL_MASK; EFI_RIGHT_CONTROL_PRESSED ) ) {
character |= KEYMAP_CTRL;
}
}
/* Apply toggle state */
toggle = key.KeyState.KeyToggleState;
if ( toggle & EFI_TOGGLE_STATE_VALID ) {
if ( toggle & EFI_CAPS_LOCK_ACTIVE ) {
character |= KEYMAP_CAPSLOCK_REDO;
}
}
/* Remap and return key */
return key_remap ( character );
} }
/* If key has a Unicode representation, return it */
if ( key.Key.UnicodeChar )
return key.Key.UnicodeChar;
/* Otherwise, check for a special key that we know about */ /* Otherwise, check for a special key that we know about */
if ( ( ansi_seq = scancode_to_ansi_seq ( key.Key.ScanCode ) ) ) { if ( ( ansi_seq = scancode_to_ansi_seq ( key.Key.ScanCode ) ) ) {
/* Start of escape sequence: return ESC (0x1b) */ /* Start of escape sequence: return ESC (0x1b) */