-
Notifications
You must be signed in to change notification settings - Fork 0
Keyboard Layouts
Locale-specific keyboard interpreters that translate hardware scancodes into virtual key codes and characters for a given language/keyboard.
JNode's Keyboard Layouts subsystem provides locale-specific KeyboardInterpreter implementations that map hardware scancodes (from PS/2 keyboards or translated USB HID reports) to Java KeyEvent virtual key codes and character values. Each layout extends AbstractKeyboardInterpreter and overrides initKeys() to populate a scancode-to-key mapping with appropriate character translations, dead keys, and modifier states for a specific locale.
The layouts live in core/src/driver/org/jnode/driver/input/l10n/ and are selected at runtime via KeyboardLayoutManager, which resolves the active locale and instantiates the matching interpreter.
| Class / File | Role |
|---|---|
core/src/driver/org/jnode/driver/input/AbstractKeyboardInterpreter.java |
Base class: modifier state, interpretScancode(), dead key handling, extended scancode processing |
core/src/driver/org/jnode/driver/input/Key.java |
Per-scancode descriptor: lowerChar/upperChar/altGrChar, lowerVirtuelKey/upperVirtuelKey/altGrVirtuelKey, dead key maps |
core/src/driver/org/jnode/driver/input/Keys.java |
Array of 128 Key objects indexed by scancode; getScanCode(int keycode) for reverse lookup |
core/src/driver/org/jnode/driver/input/KeyboardLayoutManager.java |
Factory manager resolving locale to layout ID, plugin extension point, and fallback class loading |
core/src/driver/org/jnode/driver/input/KeyboardInterpreter.java |
Interface defining Factory inner interface and interpretScancode(int) / interpretKeycode(int) methods |
core/src/driver/org/jnode/driver/input/KeyboardEvent.java |
Event wrapper: key type (KEY_PRESSED/KEY_RELEASED), timestamp, modifiers, virtual key code, character |
l10n/KeyboardInterpreter_US_en.java |
US English QWERTY layout; base reference layout |
l10n/KeyboardInterpreter_DE.java |
German QWERTZ layout; dead key support for umlauts (ä/ö/ü), Euro sign (\u20ac), sharp S (\u00df) |
l10n/KeyboardInterpreter_GB_en.java |
British English layout; slightly different punctuation mapping vs US |
l10n/KeyboardInterpreter_FR_fr.java |
French AZERTY layout; dead keys for accents, currency symbols |
l10n/KeyboardInterpreter_IT_it.java |
Italian layout |
l10n/KeyboardInterpreter_ES.java |
Spanish layout |
l10n/KeyboardInterpreter_HU.java |
Hungarian layout (standard) |
l10n/KeyboardInterpreter_HU_101.java |
Hungarian 101-key layout variant |
l10n/KeyboardInterpreter_HU_hu.java |
Hungarian with language tag |
l10n/KeyboardInterpreter_HU_hu_101.java |
Hungarian 101-key with language tag |
l10n/KeyboardInterpreter_RU.java |
Russian layout (Cyrillic) |
l10n/KeyboardInterpreter_SE.java |
Swedish layout |
l10n/KeyboardInterpreter_DK.java |
Danish layout |
l10n/KeyboardInterpreter_NO.java |
Norwegian layout |
l10n/KeyboardInterpreter_DV.java |
Dvorak-style layout variant |
l10n/KeyboardInterpreter_BE_fr.java |
Belgian French layout |
l10n/KeyboardInterpreter_CH_fr.java |
Swiss French layout |
Each layout extends AbstractKeyboardInterpreter and overrides initKeys(Keys keys) to populate the per-scancode Key descriptors:
public class KeyboardInterpreter_DE extends AbstractKeyboardInterpreter {
protected void initKeys(Keys keys) {
keys.setKey(1, new Key('^', '\u00b0')); // scancode 1 → '^' lower, '\u00b0' upper
keys.setKey(2, new Key('1', KeyEvent.VK_1, '!', KeyEvent.VK_EXCLAMATION_MARK));
keys.setKey(3, new Key('2', KeyEvent.VK_2, '"', KeyEvent.VK_QUOTEDBL));
keys.setKey(12, new Key('\u00df', KeyEvent.VK_PLUS, '?', KeyEvent.VK_UNDEFINED, '\\', KeyEvent.VK_BACK_SLASH));
keys.setKey(16, new Key('q', KeyEvent.VK_Q, 'Q', KeyEvent.VK_Q, '@', KeyEvent.VK_AT));
keys.setKey(42, new Key(KeyEvent.VK_SHIFT));
keys.setKey(54, new Key(KeyEvent.VK_SHIFT));
// ... 128-entry array populated by scancode
}
}The Key class supports:
- Lower/upper characters: Plain text and shifted character
- AltGr character: Character produced with AltGr modifier
-
Virtual key codes:
KeyEvent.VK_*constants for the virtual key -
Dead key associations: Accented character combinations via
addDeadKeyChar(int vk, char[] chars)
Dead keys (accent marks that combine with a following letter) are handled via exception-based control flow in interpretExtendedScanCode():
switch (vk) {
case KeyEvent.VK_DEAD_ACUTE:
case KeyEvent.VK_DEAD_GRAVE:
case KeyEvent.VK_DEAD_CIRCUMFLEX:
case KeyEvent.VK_DEAD_DIAERESIS:
case KeyEvent.VK_DEAD_TILDE:
lastDeadVK = vk;
throw deadKeyException; // buffer the dead key state
}
// On next keypress, combine dead state with key:
if (lastDeadVK != -1) {
char[] deadChars = key.getDeadKeyChar(lastDeadVK);
if (flags == SHIFT_DOWN_MASK) return deadChars[1]; // uppercase accented char
else return deadChars[0]; // lowercase accented char
}The German layout (KeyboardInterpreter_DE) demonstrates dead key support:
key = keys.getKey(18); // 'E' key
key.addDeadKeyChar(KeyEvent.VK_DEAD_ACUTE, new char[]{'\u00e9', '\u00c9'}); // é, É
key.addDeadKeyChar(KeyEvent.VK_DEAD_GRAVE, new char[]{'\u00e8', '\u00c8'}); // è, È
key.addDeadKeyChar(KeyEvent.VK_DEAD_DIAERESIS, new char[]{'\u00eb', '\u00cb'}); // ë, Ë
key.addDeadKeyChar(KeyEvent.VK_DEAD_CIRCUMFLEX, new char[]{'\u00ea', '\u00ca'}); // ê, ÊKeyboardLayoutManager.createDefaultKeyboardInterpreter() resolves the locale using ResourceBundle named org.jnode.driver.input.KeyboardLayout with keys defaultCountry, defaultRegion, defaultVariant. It builds an ID like US_en, HU_hu_101, or DE and looks up the factory via the keyboard-layouts extension point. Fallback loads org.jnode.driver.input.l10n.KeyboardInterpreter_<id> by class name:
final String classI10N = "org.jnode.driver.input.l10n.KeyboardInterpreter_" + id;
return new KIClassWrapper(classI10N).create();AbstractKeyboardInterpreter.adjustFlags() tracks modifier state via a bitmask field. CapsLock uses a 4-state counter where states 0 and 3 apply SHIFT_DOWN_MASK, states 1 and 2 clear it — requiring two key-down events per toggle. Extended scancodes (0xE0 prefix) set extendedMode = true; the next scancode is processed as an extended key (arrows, Home/End, etc.).
Layouts are registered via the org.jnode.driver.input.keyboard-layouts extension point in plugin descriptors:
<extension-point name="keyboard-layouts"/>
<extension point="org.jnode.driver.input.keyboard-layouts">
<layout name="US_en" class="org.jnode.driver.input.l10n.KeyboardInterpreter_US_en"/>
<layout name="DE" class="org.jnode.driver.input.l10n.KeyboardInterpreter_DE"/>
<!-- ... -->
</extension>-
19 layouts, inconsistent coverage: Layouts exist for US, GB, DE, FR, IT, ES, HU (4 variants), RU, SE, DK, NO, DV, BE, CH. Other locales (e.g., PT, PL, JP, CN) have no layout.
-
Dead key performance: Exception-based control flow (
DeadKeyException,UnsupportedKeyException) is acknowledged as "an evil hack" in the code. Each dead key throw creates an exception object per invocation. -
CapsLock 4-state cycle: The unusual 4-state counter means CapsLock requires two key-down events. Applications that monitor CapsLock via
KeyEvent.VK_CAPS_LOCKmay not see expected behavior. -
Stateful interpreter instances: Each
KeyboardInterpretermaintains per-keyboard modifier state and dead-key state. Sharing an instance across multiple keyboards causes input cross-talk. -
No Unicode beyond BMP: All characters use 16-bit
char. No surrogate pair handling for non-BMP Unicode. -
Limited numpad support: Numpad keys (71-83, 86-110 in scancode map) are mapped but numlock state is not tracked.
-
AltGr is just a modifier flag:
ALT_GRAPH_DOWN_MASKis set when AltGr is pressed; the layout's AltGr character is then returned if that flag is set. There is no separate AltGr key symbol.
- Input-Drivers — Parent page covering the full input subsystem (keyboard and mouse)
- Driver-Framework — Base driver model that keyboard layouts plug into
-
GUI-AWT — AWT consumes keyboard events via
KeyboardHandler -
InitialNaming-Service-Registry —
KeyboardLayoutManager.NAMEbinding mechanism