diff options
author | Michael Brown <mcb30@ipxe.org> | 2022-02-14 16:31:08 +0000 |
---|---|---|
committer | Michael Brown <mcb30@ipxe.org> | 2022-02-15 11:58:50 +0000 |
commit | f2a59d5973da2041f93264609698b9b3f4ec101b (patch) | |
tree | d74210c67817a2bc8ddbf92514c18608566317ed | |
parent | 871dd236d4aff66e871c25addcf522fe75a4ccd7 (diff) | |
download | ipxe-f2a59d5973da2041f93264609698b9b3f4ec101b.tar.gz |
[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>
-rw-r--r-- | src/arch/x86/include/bios.h | 3 | ||||
-rw-r--r-- | src/arch/x86/interface/pcbios/bios_console.c | 23 | ||||
-rw-r--r-- | src/core/keymap.c | 41 | ||||
-rw-r--r-- | src/drivers/usb/usbkbd.c | 21 | ||||
-rw-r--r-- | src/include/ipxe/keymap.h | 21 | ||||
-rw-r--r-- | src/interface/efi/efi_console.c | 44 |
6 files changed, 116 insertions, 37 deletions
diff --git a/src/arch/x86/include/bios.h b/src/arch/x86/include/bios.h index 14e7acbc7..3ba8264ec 100644 --- a/src/arch/x86/include/bios.h +++ b/src/arch/x86/include/bios.h @@ -6,6 +6,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define BDA_SEG 0x0040 #define BDA_EBDA 0x000e #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_TICKS 0x006c #define BDA_MIDNIGHT 0x0070 diff --git a/src/arch/x86/interface/pcbios/bios_console.c b/src/arch/x86/interface/pcbios/bios_console.c index 438a01d07..2664ac8a5 100644 --- a/src/arch/x86/interface/pcbios/bios_console.c +++ b/src/arch/x86/interface/pcbios/bios_console.c @@ -361,6 +361,7 @@ static const char * bios_ansi_seq ( unsigned int scancode ) { */ static int bios_getchar ( void ) { uint16_t keypress; + uint8_t kb0; unsigned int scancode; unsigned int character; const char *ansi_seq; @@ -385,16 +386,28 @@ static int bios_getchar ( void ) { bios_inject_lock--; scancode = ( keypress >> 8 ); character = ( keypress & 0xff ); + get_real ( kb0, BDA_SEG, BDA_KB0 ); /* If it's a normal character, map (if applicable) and return it */ if ( character && ( character < 0x80 ) ) { - if ( scancode < SCANCODE_RSHIFT ) { - return key_remap ( character ); - } else if ( scancode == SCANCODE_NON_US ) { - return key_remap ( character | KEYMAP_PSEUDO ); - } else { + + /* Handle special scancodes */ + if ( scancode == SCANCODE_NON_US ) { + /* Treat as "\|" with high bit set */ + character |= KEYMAP_PSEUDO; + } else if ( scancode >= SCANCODE_RSHIFT ) { + /* Non-remappable scancode (e.g. numeric keypad) */ 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 */ diff --git a/src/core/keymap.c b/src/core/keymap.c index c0953967a..a5209bc20 100644 --- a/src/core/keymap.c +++ b/src/core/keymap.c @@ -23,6 +23,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +#include <ctype.h> +#include <ipxe/keys.h> #include <ipxe/keymap.h> /** @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 */ static TABLE_START ( keymap_start, KEYMAP ); @@ -41,21 +55,36 @@ static struct keymap *keymap = keymap_start; * Remap a key * * @v character Character read from console - * @ret character Mapped character + * @ret mapped Mapped character */ unsigned int key_remap ( unsigned int character ) { + unsigned int mapped = ( character & KEYMAP_MASK ); struct keymap_key *key; + /* Invert case before remapping if applicable */ + if ( ( character & KEYMAP_CAPSLOCK_UNDO ) && isalpha ( mapped ) ) + mapped ^= CASE_TOGGLE; + /* Remap via table */ for ( key = keymap->basic ; key->from ; key++ ) { - if ( key->from == character ) { - character = key->to; + if ( mapped == key->from ) { + mapped = key->to; break; } } - /* Clear pseudo key flag */ - character &= ~KEYMAP_PSEUDO; + /* Handle Ctrl-<key> and CapsLock */ + if ( isalpha ( mapped ) ) { + if ( character & KEYMAP_CTRL ) { + mapped &= CTRL_MASK; + } else if ( character & KEYMAP_CAPSLOCK ) { + mapped ^= CASE_TOGGLE; + } + } + + /* Clear flags */ + mapped &= ASCII_MASK; - return character; + DBGC2 ( &keymap, "KEYMAP mapped %04x => %02x\n", character, mapped ); + return mapped; } diff --git a/src/drivers/usb/usbkbd.c b/src/drivers/usb/usbkbd.c index 6954cd69b..516667b25 100644 --- a/src/drivers/usb/usbkbd.c +++ b/src/drivers/usb/usbkbd.c @@ -71,6 +71,9 @@ static unsigned int usbkbd_map ( unsigned int keycode, unsigned int modifiers, } else if ( keycode <= USBKBD_KEY_Z ) { /* Alphabetic keys */ key = ( keycode - USBKBD_KEY_A + 'a' ); + if ( modifiers & USBKBD_SHIFT ) { + key -= ( 'a' - 'A' ); + } } else if ( keycode <= USBKBD_KEY_0 ) { /* Numeric key row */ if ( modifiers & USBKBD_SHIFT ) { @@ -125,17 +128,15 @@ static unsigned int usbkbd_map ( unsigned int keycode, unsigned int modifiers, /* Remap key if applicable */ if ( ( keycode < USBKBD_KEY_CAPS_LOCK ) || ( keycode == USBKBD_KEY_NON_US ) ) { - key = key_remap ( key ); - } - /* Handle upper/lower case and Ctrl-<key> */ - if ( islower ( key ) ) { - if ( modifiers & USBKBD_CTRL ) { - key -= ( 'a' - CTRL_A ); - } else if ( ( modifiers & USBKBD_SHIFT ) || - ( leds & USBKBD_LED_CAPS_LOCK ) ) { - key -= ( 'a' - 'A' ); - } + /* Apply modifiers */ + if ( modifiers & USBKBD_CTRL ) + key |= KEYMAP_CTRL; + if ( leds & USBKBD_LED_CAPS_LOCK ) + key |= KEYMAP_CAPSLOCK; + + /* Remap key */ + key = key_remap ( key ); } return key; diff --git a/src/include/ipxe/keymap.h b/src/include/ipxe/keymap.h index a64ab9cd4..3da25190b 100644 --- a/src/include/ipxe/keymap.h +++ b/src/include/ipxe/keymap.h @@ -40,9 +40,30 @@ struct keymap { /** Define a keyboard mapping */ #define __keymap __table_entry ( KEYMAP, 01 ) +/** Mappable character mask */ +#define KEYMAP_MASK 0xff + /** Pseudo key flag */ #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 ); #endif /* _IPXE_KEYMAP_H */ diff --git a/src/interface/efi/efi_console.c b/src/interface/efi/efi_console.c index 874f54b6c..9adce4a9b 100644 --- a/src/interface/efi/efi_console.c +++ b/src/interface/efi/efi_console.c @@ -55,8 +55,6 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define ATTR_DEFAULT ATTR_FCOL_WHITE -#define CTRL_MASK 0x1f - /* Set default console usage if applicable */ #if ! ( defined ( CONSOLE_EFI ) && CONSOLE_EXPLICIT ( 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_EX_PROTOCOL *conin_ex = efi_conin_ex; const char *ansi_seq; + unsigned int character; + unsigned int shift; + unsigned int toggle; EFI_KEY_DATA key; EFI_STATUS efirc; int rc; @@ -318,23 +319,34 @@ static int efi_getchar ( void ) { key.KeyState.KeyToggleState, key.Key.UnicodeChar, key.Key.ScanCode ); - /* Remap key. There is unfortunately no way to avoid - * remapping the numeric keypad, since EFI destroys the scan - * code information that would allow us to differentiate - * between main keyboard and numeric keypad. + /* If key has a Unicode representation, remap and return it. + * There is unfortunately no way to avoid remapping the + * numeric keypad, since EFI destroys the scan code + * 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 ) { + + /* Apply shift state */ + shift = key.KeyState.KeyShiftState; + if ( shift & EFI_SHIFT_STATE_VALID ) { + if ( shift & ( EFI_LEFT_CONTROL_PRESSED | + EFI_RIGHT_CONTROL_PRESSED ) ) { + character |= KEYMAP_CTRL; + } + } - /* Translate Ctrl-<key> */ - if ( ( key.KeyState.KeyShiftState & EFI_SHIFT_STATE_VALID ) && - ( key.KeyState.KeyShiftState & ( EFI_LEFT_CONTROL_PRESSED | - EFI_RIGHT_CONTROL_PRESSED ) ) ) { - key.Key.UnicodeChar &= CTRL_MASK; - } + /* Apply toggle state */ + toggle = key.KeyState.KeyToggleState; + if ( toggle & EFI_TOGGLE_STATE_VALID ) { + if ( toggle & EFI_CAPS_LOCK_ACTIVE ) { + character |= KEYMAP_CAPSLOCK_REDO; + } + } - /* If key has a Unicode representation, return it */ - if ( key.Key.UnicodeChar ) - return key.Key.UnicodeChar; + /* Remap and return key */ + return key_remap ( character ); + } /* Otherwise, check for a special key that we know about */ if ( ( ansi_seq = scancode_to_ansi_seq ( key.Key.ScanCode ) ) ) { |