aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Brown <mcb30@ipxe.org>2022-02-14 16:31:08 +0000
committerMichael Brown <mcb30@ipxe.org>2022-02-15 11:58:50 +0000
commitf2a59d5973da2041f93264609698b9b3f4ec101b (patch)
treed74210c67817a2bc8ddbf92514c18608566317ed
parent871dd236d4aff66e871c25addcf522fe75a4ccd7 (diff)
downloadipxe-f2a59d5973da2041f93264609698b9b3f4ec101b.zip
ipxe-f2a59d5973da2041f93264609698b9b3f4ec101b.tar.gz
ipxe-f2a59d5973da2041f93264609698b9b3f4ec101b.tar.bz2
[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.h3
-rw-r--r--src/arch/x86/interface/pcbios/bios_console.c23
-rw-r--r--src/core/keymap.c41
-rw-r--r--src/drivers/usb/usbkbd.c21
-rw-r--r--src/include/ipxe/keymap.h21
-rw-r--r--src/interface/efi/efi_console.c44
6 files changed, 116 insertions, 37 deletions
diff --git a/src/arch/x86/include/bios.h b/src/arch/x86/include/bios.h
index 14e7acb..3ba8264 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 438a01d..2664ac8 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 c095396..a5209bc 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 6954cd6..516667b 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 a64ab9c..3da2519 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 874f54b..9adce4a 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 ) ) ) {