Skip to content

Add Linux keyboard support via evdev#9

Open
killerra wants to merge 4 commits into
memflow:mainfrom
killerra:linux-keyboard
Open

Add Linux keyboard support via evdev#9
killerra wants to merge 4 commits into
memflow:mainfrom
killerra:linux-keyboard

Conversation

@killerra

Copy link
Copy Markdown

Stacked on #8 — review only the last commit (Add Linux keyboard support via evdev); the earlier commits belong to #8 and this branch will be rebased once it merges.

What

Implements OsKeyboard for LinuxOs, bringing the Linux backend to feature parity with the Windows backend's keyboard support:

  • LinuxKeyboard / LinuxKeyboardState poll the pressed-key bitmap via the EVIOCGKEY ioctl (evdev's get_key_state) on /dev/input/event* — the direct analog of GetKeyboardState, reading state without consuming input events.
  • is_down() accepts Microsoft virtual-key codes, same as the Windows backend, translated through a static VK → evdev table (US layout), so cross-platform consumers work unchanged. Unmapped VKs cleanly report not-pressed.
    • Side-agnostic modifiers (VK_SHIFT/VK_CONTROL/VK_MENU) check both left and right keys; VK_RETURN covers main and keypad enter.
    • Mouse buttons are supported like GetKeyState does, including mouse4/mouse5 in both evdev report styles (BTN_SIDE/BTN_EXTRA and BTN_BACK/BTN_FORWARD).
  • State is ORed across all keyboard-capable devices (filtered by a representative key set, so power buttons/lid switches are excluded; pointer devices are admitted for the mouse-button VKs). Unplugged devices are dropped on the fly with a one-shot re-enumeration when none remain.
  • set_down stays a stub, matching the Windows backend.

Permissions

Reading /dev/input/event* requires root or membership in the input group (udev default: root:input 0660). With no readable device, keyboard() returns a descriptive NotFound error stating exactly that.

Testing

  • Unit tests for the VK→evdev table and the state bitset (cargo test --all).
  • cargo build with --all-features and --no-default-features, cargo fmt --check, cargo clippy --all-targets --all-features all clean.
  • Note for CI: runners have no input devices, so no non-ignored test touches /dev/input.

killerra added 4 commits June 10, 2026 19:03
- Implement Process::state() via /proc/<pid>/stat: zombie/dead states map
  to ProcessState::Dead (with exit_code when readable), a vanished PID
  maps to Dead, and permission errors stay Unknown.
- Use the module base address as the opaque module handle instead of an
  index into a freshly parsed maps list, eliminating the race between
  module_address_list_callback and module_by_address.
- Resolve primary_module_address() through /proc/<pid>/exe instead of
  assuming the 0th mapping, falling back to the first mapping.
- Report real in-process addresses for environment variables using
  env_start from /proc/<pid>/stat, and return the env block address from
  environment_block_address() (plugin API 2).
After a partial process_vm_readv/writev, the retry syscall resumes at
iov offset `offset`, but the result-dispatch loop iterated the local/
remote iovecs and temp_meta from index 0. On every pass after the first,
already-reported entries were re-reported with the wrong metadata and
local slices, the entries the retry actually transferred were never
reported, byte accounting used the wrong iov_lens, and out_fail flagged
the wrong element. This corrupted result attribution for any batched
read/write spanning an unmapped hole.

Align dispatch and accounting with the syscall window by skipping the
first `win` entries on all three iterators. Add a regression test that
batches [valid, unmapped, valid] reads against our own PID; it fails
against the previous code (first region duplicated, third dropped).
Follow-ups from the Linux backend soundness audit:

- Decode the waitpid(2)-style status word from /proc/<pid>/stat into a
  real exit code in Process::state(): normal exits report the exit(3)
  code, signal deaths report the negated signal number. Previously the
  raw status word leaked through (exit(3) surfaced as Dead(768)).
- Derive OsInfo.arch and ProcessInfo::{sys_arch,proc_arch} from the
  compile target (x86_64/x86/aarch64) instead of hardcoding x86-64, and
  make the process module list callback emit the same arch field that
  module_by_address keys on.
- Replace OS kernel-module handles (indices into a name-sorted snapshot
  that shift on module load/unload) with a stable hash of the module
  name (std DefaultHasher, fixed-seeded), resolved against the live
  snapshot.
- Report the real process state in process_info_by_pid via the shared
  process_state() helper instead of hardcoding Alive.
Implement OsKeyboard for LinuxOs with LinuxKeyboard/LinuxKeyboardState,
polling key state through the EVIOCGKEY ioctl (evdev's get_key_state) on
/dev/input/event* devices - the direct analog of GetKeyboardState on
Windows. Requires root or membership in the 'input' group.

is_down() accepts Microsoft virtual-key codes like the Windows backend,
translated through a static VK -> evdev table (US layout). Side-agnostic
modifiers check both sides, VK_RETURN covers both enter keys, and mouse
buttons cover both side-button report styles (BTN_SIDE/BTN_EXTRA and
BTN_BACK/BTN_FORWARD). Unmapped VKs report not-pressed, matching Windows.

Key state is ORed across all keyboard-capable devices; unplugged devices
are dropped on the fly with a one-shot re-enumeration when none remain.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant