Skip to content

Input Drivers

opencode-agent[bot] edited this page May 10, 2026 · 1 revision

Input Drivers (Keyboard, Mouse)

Input device drivers with keyboard layouts and mouse protocol handlers.

Overview

JNode's Input Drivers subsystem provides the foundation for keyboard and mouse support, enabling user input to flow from hardware devices through the driver framework into the GUI and shell subsystems. The architecture is split into two distinct areas: keyboard input handling via locale-specific KeyboardInterpreter implementations, and pointer input handling via MouseProtocolHandler plugins. The InputPlugin serves as the plugin entry point, registering two extension points (keyboard-layouts and mouse-protocol-handlers) and binding the KeyboardLayoutManager and MouseProtocolHandlerManager into the InitialNaming namespace.

Keyboard input arrives as raw scancodes from the PS/2 keyboard controller (or USB HID reports translated by a USB driver). These scancodes are passed to a KeyboardInterpreter which maintains modifier state (Shift, Ctrl, Alt, AltGr, CapsLock) and translates scancodes into KeyEvent virtual key codes and character values based on the active keyboard layout. Mouse input arrives as raw packet bytes from the PS/2 mouse port (or USB HID boot protocol). These packets are passed to a MouseProtocolHandler which decodes the packet format and produces PointerEvent objects containing button state and X/Y/Z (wheel) values in either absolute or relative coordinates.

Key Components

Class / File Role
core/src/driver/org/jnode/driver/input/InputPlugin.java Plugin entry point; registers extension points and binds managers to InitialNaming
core/src/driver/org/jnode/driver/input/KeyboardInterpreter.java Interface for translating scancodes to KeyboardEvent; defines Factory inner interface
core/src/driver/org/jnode/driver/input/AbstractKeyboardInterpreter.java Base implementation handling modifier state, extended scancodes (0xE0), dead keys, and KeyEvent construction
core/src/driver/org/jnode/driver/input/KeyboardLayoutManager.java Factory manager for keyboard layouts; resolves locale, plugin extensions, and fallback l10n class loading
core/src/driver/org/jnode/driver/input/KeyboardEvent.java Event wrapping KeyEvent.KEY_PRESSED/KEY_RELEASED, modifiers, virtual key code, and character
core/src/driver/org/jnode/driver/input/Key.java Per-scancode key descriptor holding lower/upper/AltGr characters and virtual key codes
core/src/driver/org/jnode/driver/input/Keys.java Array of 128 key descriptors indexed by scancode; provides getScanCode() reverse lookup
core/src/driver/org/jnode/driver/input/MouseProtocolHandler.java Interface for decoding raw mouse packets into PointerEvent; provides getName(), supportsId(), getPacketSize(), buildEvent()
core/src/driver/org/jnode/driver/input/MouseProtocolHandlerManager.java Manager for mouse protocol handler plugins via the mouse-protocol-handlers extension point
core/src/driver/org/jnode/driver/input/MouseInterpreter.java Driver-side interpreter using MouseProtocolHandlerManager to route packets to the appropriate handler
core/src/driver/org/jnode/driver/input/LogitechProtocol.java MouseProtocolHandler implementation for standard 3-byte Logitech mouse packets
core/src/driver/org/jnode/driver/input/LogitechWheelMouseProtocol.java MouseProtocolHandler for 4-byte Logitech mice with wheel support
core/src/driver/org/jnode/driver/input/PointerEvent.java Event holding button bitmask, X/Y/Z values, and absolute/relative coordinate mode
core/src/driver/org/jnode/driver/input/l10n/KeyboardInterpreter_*.java Locale-specific AbstractKeyboardInterpreter subclasses (US_en, GB_en, DE, FR, IT, ES, HU, RU, etc.)

How It Works

Keyboard Input Flow

PS/2 Keyboard Controller → KeyboardDriver → AbstractKeyboardInterpreter.interpretScancode()
    → KeyboardEvent → KeyboardListener (e.g., AWT KeyboardHandler)

The AbstractKeyboardInterpreter is stateful. It tracks modifier flags (SHIFT_DOWN_MASK, CTRL_DOWN_MASK, ALT_DOWN_MASK, ALT_GRAPH_DOWN_MASK) updated on each keypress, and handles CapsLock with a 4-state cycle. Extended scancodes (prefix 0xE0) set an extendedMode flag; the next scancode is interpreted as an extended key (arrows, Home/End, Insert/Delete, etc.). The interpretExtendedScanCode() method handles dead keys (accent marks like acute, grave, circumflex) by throwing DeadKeyException to buffer the dead key state, then combining it with the next key to produce the accented character.

Layout selection uses the KeyboardLayout resource bundle to resolve the locale (defaultCountry, defaultRegion, defaultVariant). KeyboardLayoutManager.createDefaultKeyboardInterpreter() builds an ID like US_en or HU_hu_101 and looks up the factory from the keyboard-layouts extension point. If no plugin extension is found, it falls back to dynamically loading org.jnode.driver.input.l10n.KeyboardInterpreter_<id> by class name.

Mouse Input Flow

PS/2 Mouse Port → MouseDriver → MouseInterpreter → MouseProtocolHandlerManager
    → MouseProtocolHandler.buildEvent() → PointerEvent → PointerListener (e.g., AWT MouseHandler)

MouseProtocolHandlerManager discovers handlers via the mouse-protocol-handlers extension point. Each handler implements supportsId(int id) to match the device's identification byte(s) and getPacketSize() to indicate how many bytes form a complete packet. The buildEvent() method decodes the packet bytes into a PointerEvent with button state (left/middle/right as bitmask) and X/Y/Z values. The LogitechProtocol uses a 3-byte format where bit 0-2 are buttons, bits 4-5 are sign bits for X/Y overflow detection, and bits 6-7 indicate X/Y overflow. LogitechWheelMouseProtocol extends this to 4 bytes with a wheel delta in byte 3.

InputPlugin Startup

protected void startPlugin() throws PluginException {
    KeyboardLayoutManager klmgr = new KeyboardLayoutManager(
        descriptor.getExtensionPoint("keyboard-layouts"));
    InitialNaming.bind(KeyboardLayoutManager.NAME, klmgr);

    MouseProtocolHandlerManager mphmgr = new MouseProtocolHandlerManager(
        descriptor.getExtensionPoint("mouse-protocol-handlers"));
    InitialNaming.bind(MouseProtocolHandlerManager.NAME, mphmgr);
}

Gotchas & Non-Obvious Behavior

  • Keyboard layouts vary by locale: JNode ships with ~20 locale-specific interpreters (KeyboardInterpreter_US_en, KeyboardInterpreter_DE, etc.). A missing or misconfigured KeyboardLayout resource bundle causes fallback to US_en.

  • Dead key handling via exceptions: interpretExtendedScanCode() uses UnsupportedKeyException and DeadKeyException for control flow. The comment in AbstractKeyboardInterpreter notes this is "an evil hack" to mitigate performance issues from exception-heavy paths.

  • Mouse protocol detection is ID-based: MouseProtocolHandler.supportsId() is checked against the device's identification byte. If no handler claims the ID, the mouse will not function. The LogitechProtocol only supports ID 0, meaning standard Logitech mice will work but extended-mode devices may not.

  • Extended scancode prefix: The 0xE0 prefix scancode returns null from interpretScancode() and sets extendedMode = true; the next scancode is processed in extended mode. This means the caller must handle the two-scancode sequence for arrow keys, etc.

  • Input events must be synchronized with GUI rendering: Input listeners (AWT peers) receive events on the same thread that the driver calls back on. In JNode's event-driven GUI model, this requires careful synchronization to avoid race conditions between input processing and rendering.

  • CapsLock state machine: CapsLock toggles via a 4-state counter (capsLock cycles 0→1→2→3→0), where states 0 and 3 apply SHIFT_DOWN_MASK, and states 1 and 2 clear it. This unusual design requires two key-down events before the toggle is complete.

  • Keyboard interpreter instances are stateful: KeyboardInterpreter objects maintain modifier state and dead-key state per keyboard device. Sharing a single interpreter across multiple keyboards causes input cross-talk.

Related Pages

  • Driver-Framework — Base driver model that input devices plug into
  • GUI-AWT — Where keyboard and mouse events are consumed by the AWT peer system
  • Bus-Drivers — USB and PS/2 bus drivers that produce raw input data for these handlers
  • Video-Driver-Architecture — Video subsystem that coordinates with input for cursor and focus

Clone this wiki locally