[console] Handle remapping of scancode 86

The key with scancode 86 appears in the position between left shift
and Z on a US keyboard, where it typically fails to exist entirely.
Most US keyboard maps define this nonexistent key as generating "\|",
with the notable exception of "loadkeys" which instead reports it as
generating "<>".  Both of these mapping choices duplicate keys that
exist elsewhere in the map, which causes problems for our ASCII-based
remapping mechanism.

Work around these quirks by treating the key as generating "\|" with
the high bit set, and making it subject to remapping.  Where the BIOS
generates "\|" as expected, this allows us to remap to the correct
ASCII value.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
pull/576/head
Michael Brown 2022-02-09 15:43:42 +00:00
parent 3f05a82fec
commit 0bbd896783
28 changed files with 93 additions and 6 deletions

View File

@ -68,6 +68,13 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
*/
#define SCANCODE_RSHIFT 0x36
/** Scancode for the "non-US \ and |" key
*
* This is the key that appears between Left Shift and Z on non-US
* keyboards.
*/
#define SCANCODE_NON_US 0x56
/* Set default console usage if applicable */
#if ! ( defined ( CONSOLE_PCBIOS ) && CONSOLE_EXPLICIT ( CONSOLE_PCBIOS ) )
#undef CONSOLE_PCBIOS
@ -383,6 +390,8 @@ static int bios_getchar ( void ) {
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 {
return character;
}

View File

@ -48,5 +48,8 @@ unsigned int key_remap ( unsigned int character ) {
}
}
/* Clear pseudo key flag */
character &= ~KEYMAP_PSEUDO;
return character;
}

View File

@ -114,13 +114,19 @@ static unsigned int usbkbd_map ( unsigned int keycode, unsigned int modifiers,
};
key = keypad[ keycode - USBKBD_KEY_PAD_1 ];
};
} else if ( keycode == USBKBD_KEY_NON_US ) {
/* Non-US \ and | */
key = ( ( modifiers & USBKBD_SHIFT ) ?
( KEYMAP_PSEUDO | '|' ) : ( KEYMAP_PSEUDO | '\\' ) );
} else {
key = 0;
}
/* Remap key if applicable */
if ( keycode < USBKBD_KEY_CAPS_LOCK )
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 ) ) {

View File

@ -75,6 +75,7 @@ enum usb_keycode {
USBKBD_KEY_PAD_ENTER = 0x58,
USBKBD_KEY_PAD_1 = 0x59,
USBKBD_KEY_PAD_DOT = 0x63,
USBKBD_KEY_NON_US = 0x64,
};
/** USB keyboard LEDs */

View File

@ -30,4 +30,6 @@ struct key_mapping al_mapping[] __keymap = {
{ 0x7c, 0x7d }, /* '|' => '}' */
{ 0x7d, 0x27 }, /* '}' => '\'' */
{ 0x7e, 0x7c }, /* '~' => '|' */
{ 0xdc, 0x3c }, /* Pseudo-'\\' => '<' */
{ 0xfc, 0x3e }, /* Pseudo-'|' => '>' */
};

View File

@ -21,4 +21,6 @@ struct key_mapping az_mapping[] __keymap = {
{ 0x40, 0x22 }, /* '@' => '"' */
{ 0x5e, 0x3a }, /* '^' => ':' */
{ 0x7c, 0x2f }, /* '|' => '/' */
{ 0xdc, 0x3c }, /* Pseudo-'\\' => '<' */
{ 0xfc, 0x3e }, /* Pseudo-'|' => '>' */
};

View File

@ -12,4 +12,6 @@ FILE_LICENCE ( PUBLIC_DOMAIN );
/** "by" keyboard mapping */
struct key_mapping by_mapping[] __keymap = {
{ 0xdc, 0x3c }, /* Pseudo-'\\' => '<' */
{ 0xfc, 0x3e }, /* Pseudo-'|' => '>' */
};

View File

@ -36,4 +36,6 @@ struct key_mapping de_mapping[] __keymap = {
{ 0x7a, 0x79 }, /* 'z' => 'y' */
{ 0x7c, 0x27 }, /* '|' => '\'' */
{ 0x7d, 0x2a }, /* '}' => '*' */
{ 0xdc, 0x3c }, /* Pseudo-'\\' => '<' */
{ 0xfc, 0x3e }, /* Pseudo-'|' => '>' */
};

View File

@ -28,4 +28,6 @@ struct key_mapping dk_mapping[] __keymap = {
{ 0x5e, 0x26 }, /* '^' => '&' */
{ 0x5f, 0x3f }, /* '_' => '?' */
{ 0x7c, 0x2a }, /* '|' => '*' */
{ 0xdc, 0x3c }, /* Pseudo-'\\' => '<' */
{ 0xfc, 0x3e }, /* Pseudo-'|' => '>' */
};

View File

@ -28,4 +28,6 @@ struct key_mapping es_mapping[] __keymap = {
{ 0x5e, 0x26 }, /* '^' => '&' */
{ 0x5f, 0x3f }, /* '_' => '?' */
{ 0x7d, 0x2a }, /* '}' => '*' */
{ 0xdc, 0x3c }, /* Pseudo-'\\' => '<' */
{ 0xfc, 0x3e }, /* Pseudo-'|' => '>' */
};

View File

@ -26,4 +26,6 @@ struct key_mapping et_mapping[] __keymap = {
{ 0x5e, 0x26 }, /* '^' => '&' */
{ 0x5f, 0x3f }, /* '_' => '?' */
{ 0x7c, 0x2a }, /* '|' => '*' */
{ 0xdc, 0x3c }, /* Pseudo-'\\' => '<' */
{ 0xfc, 0x3e }, /* Pseudo-'|' => '>' */
};

View File

@ -26,4 +26,6 @@ struct key_mapping fi_mapping[] __keymap = {
{ 0x5e, 0x26 }, /* '^' => '&' */
{ 0x5f, 0x3f }, /* '_' => '?' */
{ 0x7c, 0x2a }, /* '|' => '*' */
{ 0xdc, 0x3c }, /* Pseudo-'\\' => '<' */
{ 0xfc, 0x3e }, /* Pseudo-'|' => '>' */
};

View File

@ -57,4 +57,6 @@ struct key_mapping fr_mapping[] __keymap = {
{ 0x71, 0x61 }, /* 'q' => 'a' */
{ 0x77, 0x7a }, /* 'w' => 'z' */
{ 0x7a, 0x77 }, /* 'z' => 'w' */
{ 0xdc, 0x3c }, /* Pseudo-'\\' => '<' */
{ 0xfc, 0x3e }, /* Pseudo-'|' => '>' */
};

View File

@ -12,4 +12,6 @@ FILE_LICENCE ( PUBLIC_DOMAIN );
/** "gr" keyboard mapping */
struct key_mapping gr_mapping[] __keymap = {
{ 0xdc, 0x3c }, /* Pseudo-'\\' => '<' */
{ 0xfc, 0x3e }, /* Pseudo-'|' => '>' */
};

View File

@ -24,4 +24,6 @@ struct key_mapping il_mapping[] __keymap = {
{ 0x60, 0x3b }, /* '`' => ';' */
{ 0x7b, 0x7d }, /* '{' => '}' */
{ 0x7d, 0x7b }, /* '}' => '{' */
{ 0xdc, 0x3c }, /* Pseudo-'\\' => '<' */
{ 0xfc, 0x3e }, /* Pseudo-'|' => '>' */
};

View File

@ -30,4 +30,6 @@ struct key_mapping it_mapping[] __keymap = {
{ 0x60, 0x5c }, /* '`' => '\\' */
{ 0x7d, 0x2a }, /* '}' => '*' */
{ 0x7e, 0x7c }, /* '~' => '|' */
{ 0xdc, 0x3c }, /* Pseudo-'\\' => '<' */
{ 0xfc, 0x3e }, /* Pseudo-'|' => '>' */
};

View File

@ -12,4 +12,6 @@ FILE_LICENCE ( PUBLIC_DOMAIN );
/** "mk" keyboard mapping */
struct key_mapping mk_mapping[] __keymap = {
{ 0xdc, 0x3c }, /* Pseudo-'\\' => '<' */
{ 0xfc, 0x3e }, /* Pseudo-'|' => '>' */
};

View File

@ -33,4 +33,6 @@ struct key_mapping nl_mapping[] __keymap = {
{ 0x60, 0x40 }, /* '`' => '@' */
{ 0x7c, 0x3e }, /* '|' => '>' */
{ 0x7d, 0x7c }, /* '}' => '|' */
{ 0xdc, 0x5d }, /* Pseudo-'\\' => ']' */
{ 0xfc, 0x5b }, /* Pseudo-'|' => '[' */
};

View File

@ -32,4 +32,6 @@ struct key_mapping no_latin1_mapping[] __keymap = {
{ 0x60, 0x7c }, /* '`' => '|' */
{ 0x7c, 0x2a }, /* '|' => '*' */
{ 0x7d, 0x5e }, /* '}' => '^' */
{ 0xdc, 0x3c }, /* Pseudo-'\\' => '<' */
{ 0xfc, 0x3e }, /* Pseudo-'|' => '>' */
};

View File

@ -30,4 +30,6 @@ struct key_mapping no_mapping[] __keymap = {
{ 0x5f, 0x3f }, /* '_' => '?' */
{ 0x60, 0x7c }, /* '`' => '|' */
{ 0x7c, 0x2a }, /* '|' => '*' */
{ 0xdc, 0x3c }, /* Pseudo-'\\' => '<' */
{ 0xfc, 0x3e }, /* Pseudo-'|' => '>' */
};

View File

@ -12,4 +12,6 @@ FILE_LICENCE ( PUBLIC_DOMAIN );
/** "pl" keyboard mapping */
struct key_mapping pl_mapping[] __keymap = {
{ 0xdc, 0x3c }, /* Pseudo-'\\' => '<' */
{ 0xfc, 0x3e }, /* Pseudo-'|' => '>' */
};

View File

@ -29,4 +29,6 @@ struct key_mapping pt_mapping[] __keymap = {
{ 0x60, 0x5c }, /* '`' => '\\' */
{ 0x7b, 0x2a }, /* '{' => '*' */
{ 0x7e, 0x7c }, /* '~' => '|' */
{ 0xdc, 0x3c }, /* Pseudo-'\\' => '<' */
{ 0xfc, 0x3e }, /* Pseudo-'|' => '>' */
};

View File

@ -13,4 +13,6 @@ FILE_LICENCE ( PUBLIC_DOMAIN );
/** "ru" keyboard mapping */
struct key_mapping ru_mapping[] __keymap = {
{ 0x0d, 0x0a }, /* Ctrl-M => Ctrl-J */
{ 0xdc, 0x3c }, /* Pseudo-'\\' => '<' */
{ 0xfc, 0x3e }, /* Pseudo-'|' => '>' */
};

View File

@ -38,4 +38,6 @@ struct key_mapping sg_mapping[] __keymap = {
{ 0x7a, 0x79 }, /* 'z' => 'y' */
{ 0x7c, 0x24 }, /* '|' => '$' */
{ 0x7d, 0x21 }, /* '}' => '!' */
{ 0xdc, 0x3c }, /* Pseudo-'\\' => '<' */
{ 0xfc, 0x3e }, /* Pseudo-'|' => '>' */
};

View File

@ -12,4 +12,6 @@ FILE_LICENCE ( PUBLIC_DOMAIN );
/** "sr-latin" keyboard mapping */
struct key_mapping sr_latin_mapping[] __keymap = {
{ 0xdc, 0x3c }, /* Pseudo-'\\' => '<' */
{ 0xfc, 0x3e }, /* Pseudo-'|' => '>' */
};

View File

@ -12,4 +12,6 @@ FILE_LICENCE ( PUBLIC_DOMAIN );
/** "ua" keyboard mapping */
struct key_mapping ua_mapping[] __keymap = {
{ 0xdc, 0x3c }, /* Pseudo-'\\' => '<' */
{ 0xfc, 0x3e }, /* Pseudo-'|' => '>' */
};

View File

@ -27,6 +27,9 @@ struct key_mapping {
/** Define a keyboard mapping */
#define __keymap __table_entry ( KEYMAP, 01 )
/** Pseudo key flag */
#define KEYMAP_PSEUDO 0x80
extern unsigned int key_remap ( unsigned int character );
#endif /* _IPXE_KEYMAP_H */

View File

@ -219,12 +219,28 @@ class KeyMapping(UserDict[KeyModifiers, Sequence[Key]]):
class BiosKeyMapping(KeyMapping):
"""Keyboard mapping as used by the BIOS"""
"""Keyboard mapping as used by the BIOS
To allow for remappings of the somewhat interesting key 86, we
arrange for our keyboard drivers to generate this key as "\\|"
with the high bit set.
"""
KEY_PSEUDO: ClassVar[int] = 0x80
"""Flag used to indicate a fake ASCII value"""
KEY_NON_US_UNSHIFTED: ClassVar[str] = chr(KEY_PSEUDO | ord('\\'))
"""Fake ASCII value generated for unshifted key code 86"""
KEY_NON_US_SHIFTED: ClassVar[str] = chr(KEY_PSEUDO | ord('|'))
"""Fake ASCII value generated for shifted key code 86"""
@property
def inverse(self) -> MutableMapping[str, Key]:
inverse = super().inverse
assert len(inverse) == 0x7f
inverse[self.KEY_NON_US_UNSHIFTED] = self.unshifted[self.KEY_NON_US]
inverse[self.KEY_NON_US_SHIFTED] = self.shifted[self.KEY_NON_US]
assert all(x.modifiers in {KeyModifiers.NONE, KeyModifiers.SHIFT,
KeyModifiers.CTRL}
for x in inverse.values())
@ -251,12 +267,13 @@ class KeyRemapping:
raw = {source: self.target[key.modifiers][key.keycode].ascii
for source, key in self.source.inverse.items()}
# Eliminate any null mappings, mappings that attempt to remap
# the backspace key, or identity mappings
# the backspace key, or mappings that would become identity
# mappings after clearing the high bit
table = {source: target for source, target in raw.items()
if target
and ord(source) != 0x7f
and ord(target) != 0x7f
and ord(source) != ord(target)}
and ord(source) & ~BiosKeyMapping.KEY_PSEUDO != ord(target)}
# Recursively delete any mappings that would produce
# unreachable alphanumerics (e.g. the "il" keymap, which maps
# away the whole lower-case alphabet)
@ -281,13 +298,17 @@ class KeyRemapping:
"""C variable name"""
return re.sub(r'\W', '_', self.name) + "_mapping"
@staticmethod
def ascii_name(char: str) -> str:
@classmethod
def ascii_name(cls, char: str) -> str:
"""ASCII character name"""
if char == '\\':
name = "'\\\\'"
elif char == '\'':
name = "'\\\''"
elif ord(char) & BiosKeyMapping.KEY_PSEUDO:
name = "Pseudo-%s" % cls.ascii_name(
chr(ord(char) & ~BiosKeyMapping.KEY_PSEUDO)
)
elif char.isprintable():
name = "'%s'" % char
elif ord(char) <= 0x1a: