]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[console] Centralise handling of key modifiers
authorMichael Brown <mcb30@ipxe.org>
Mon, 14 Feb 2022 16:31:08 +0000 (16:31 +0000)
committerMichael Brown <mcb30@ipxe.org>
Tue, 15 Feb 2022 11:58:50 +0000 (11:58 +0000)
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>
src/arch/x86/include/bios.h
src/arch/x86/interface/pcbios/bios_console.c
src/core/keymap.c
src/drivers/usb/usbkbd.c
src/include/ipxe/keymap.h
src/interface/efi/efi_console.c

index 14e7acbc79bf4161f5c19d33c0509207a5677619..3ba8264ecc7f417c5f692d6a36a69ffc14049930 100644 (file)
@@ -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
index 438a01d0718bf863eb4e3cacba8f62769b97c0e2..2664ac8a516e549413fa701cab5d2550fbc69ad3 100644 (file)
@@ -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 */
index c0953967a511e55e74d36a5ca3819370d5c20954..a5209bc20a399c8253e662f72906b514112ba4b5 100644 (file)
@@ -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;
 }
index 6954cd69bc2e869108ef07d1e681f82722462a7c..516667b25e855671e5f33134c399b78510d608c5 100644 (file)
@@ -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;
index a64ab9cd4de05f7c022f29ce2c547c0957c39669..3da25190bc822dd016f060678fa29947ace8ad2d 100644 (file)
@@ -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 */
index 874f54b6c4e2ddb23f706f5f7504a57048c8614e..9adce4a9b4ac02a933e4d5ea1ded21056f01aff6 100644 (file)
@@ -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 ) ) ) {