A 4x4 membrane keypad scan is us-timing-critical: firmware drives one
row LOW and reads the columns back within the same loop pass, far
faster than the browser<->QEMU websocket round trip, so the browser-side
matrix simulation could never register a key on ESP32 boards.
Worker: new 'matrix-keypad' sensor type (rows/cols GPIO lists, pressed
key set). _on_dir_change/_on_pin_change recompute the column levels
synchronously on the QEMU thread — the pinMode(OUTPUT) transition is
the trigger on steady-state passes, since the row's output latch stays
LOW and the digitalWrite fires no change callback. Async gpio_in writes
to keypad-owned columns are ignored so the browser's generic matrix
logic can't clobber them.
Frontend: SimulatorCanvas attaches the keypad as a matrix-keypad sensor
when every R/C pin resolves to an ESP32-family board GPIO, and streams
the pressed-key set from the element's button-press/release events.