Skip to content

uhid: emit per-button Usage items instead of Usage Min/Max range#235

Merged
BANANASJIM merged 3 commits into
BANANASJIM:mainfrom
VaisVaisov:uhid-button-usage-fix
May 25, 2026
Merged

uhid: emit per-button Usage items instead of Usage Min/Max range#235
BANANASJIM merged 3 commits into
BANANASJIM:mainfrom
VaisVaisov:uhid-button-usage-fix

Conversation

@VaisVaisov
Copy link
Copy Markdown
Contributor

@VaisVaisov VaisVaisov commented May 12, 2026

Problem

buildFromOutput emitted Usage Minimum (1) / Usage Maximum (N) for the button collection. This causes the kernel to assign evdev codes in a fixed pattern — BTN_GAMEPAD for usages 1-16, then BTN_MISC for 17-32 — regardless of what is declared in [output.buttons]. Extra buttons like M1-M4 ended up on wrong BTN_* codes and were not visible to SDL/Steam.

Fix

Replace the range with one Usage (N) item per button, where N is derived from the button's configured BTN_* code via the kernel's reverse mapping:

Usage range evdev range
1–16 BTN_GAMEPAD (0x130–0x13F)
17–32 BTN_MISC (0x100–0x10F)
33–48 BTN_MOUSE (0x110–0x11F)
49–64 BTN_JOYSTICK(0x120–0x12F)

Codes outside these ranges (e.g. BTN_TRIGGER_HAPPY) receive the lowest free Usage number so the descriptor remains valid.

Adds btnCodeToHidUsage() to implement the reverse BTN_* → HID Button Page Usage mapping.

Tested

Verified with evtest and Steam on Linux (CachyOS, kernel 7.0.5) using a Flydigi Vader 5 Pro. All buttons including extra paddle/misc buttons now map to the correct BTN_* codes.

Summary by CodeRabbit

  • Bug Fixes
    • Improved gamepad descriptor generation to assign per-button HID usages (with safe fallbacks) so controllers report consistent, valid button layouts.
  • Chores
    • Standardized Flydigi Vader 5 Pro button mappings for M1–M4, O, C, Z, LM, RM to stable hardware codes for more reliable input behavior.

Review Change Stack

@qodo-code-review
Copy link
Copy Markdown

ⓘ You've reached your Qodo monthly free-tier limit. Reviews pause until next month — upgrade your plan to continue now, or link your paid account if you already have one.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 12, 2026

Warning

Review limit reached

@BANANASJIM, we couldn't start this review because you've used your available PR reviews for now.

Your plan includes 1 review of capacity. Refill in 10 minutes and 54 seconds.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more review capacity refills, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than trial, open-source, and free plans. In all cases, review capacity refills continuously over time.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c3449dd9-56ef-4c15-896f-096bc6751f4e

📥 Commits

Reviewing files that changed from the base of the PR and between 7a0464b and af8c195.

⛔ Files ignored due to path filters (1)
  • src/test/fixtures/golden/steam_deck_hid_descriptor.bin is excluded by !**/*.bin
📒 Files selected for processing (3)
  • devices/flydigi/vader5.toml
  • src/io/uhid_descriptor.zig
  • src/test/fixtures/golden/steam_deck_hid_descriptor.annotated.txt
📝 Walkthrough

Walkthrough

Emit HID Button Page usages per-button by resolving configured evdev BTN_* codes via a new mapper, assign fallback usages for unresolved buttons, and update Flydigi Vader5 device output button mappings to explicit BTN_* codes.

Changes

Button Descriptor Per-Usage Resolution

Layer / File(s) Summary
Import input_codes & per-button descriptor emission
src/io/uhid_descriptor.zig
Adds input_codes import and reworks face-button HID emission to resolve each ButtonId to a HID Usage, pack usages in declaration order, track allocated usages with a bitset, fill unresolved slots with lowest free usages (1..64), and emit one Usage (u8) item per active button slot instead of a contiguous Usage Minimum/Maximum range.
HID button code-to-usage mapper
src/io/uhid_descriptor.zig
New btnCodeToHidUsage(code: u16) ?u8 helper maps specific Linux BTN_* ranges to HID Button Page usage numbers 1–64 and returns null for unsupported codes.

Flydigi Vader5 device mapping

Layer / File(s) Summary
Flydigi Vader5 TOML button mappings
devices/flydigi/vader5.toml
Replaces BTN_TRIGGER_HAPPY*-based mappings with explicit standard BTN_0BTN_8 and BTN_4BTN_7 assignments for multiple output buttons, and removes the commented remapping guidance.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~40 minutes

Possibly related PRs

  • BANANASJIM/padctl#132: Modifies src/io/uhid_descriptor.zig and earlier descriptor emission logic related to button usages.
  • BANANASJIM/padctl#36: Extends input_codes resolution for BTN_* codes that this PR consumes.

Poem

🐰 I hopped through mappings, one by one,
Turning BTN to HID 'til the job was done.
Usages placed in tidy rows,
Fallbacks found where mystery grows,
A happy hop—descriptor fun!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: replacing Usage Min/Max range with per-button Usage items in HID button descriptors.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/io/uhid_descriptor.zig`:
- Around line 247-269: The button_usages array is declared uninitialized and
later fully iterated, which can cause undefined reads; zero-initialize
button_usages (or explicitly fill it with 0) before the enum walk so any slots
not assigned remain 0 and the subsequent `for (button_usages[0..button_count])`
loop and `next_free` allocation see defined values; update the declaration/early
code around `var button_usages: [64]u8`, `occupied`, and the enum `inline for`
block to ensure `button_usages` is initialized before partial population.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3e63ff8e-3994-4bd5-afa5-28c814c48593

📥 Commits

Reviewing files that changed from the base of the PR and between fe6166f and e0d45fe.

📒 Files selected for processing (1)
  • src/io/uhid_descriptor.zig

Comment thread src/io/uhid_descriptor.zig Outdated
@BANANASJIM
Copy link
Copy Markdown
Owner

BANANASJIM commented May 15, 2026

Thanks @VaisVaisov. The per-button Usage approach is the right direction. One thing to fix before it lands.

The paddle Usage numbers are off. padctl emits a Game Pad collection, so the kernel HID_GD_GAMEPAD path is code = usage - 1, then +BTN_GAMEPAD if code <= 0xf else +0x2b0. M1 to M4 (Usage 17 to 20) therefore land on BTN_TRIGGER_HAPPY1..4, but SDL only detects Xbox Elite paddles at BTN_TRIGGER_HAPPY5..8 (SDL_sysjoystick.c). So paddles will not be recognized as written. (The btnCodeToHidUsage comment about a BTN_MISC/MOUSE/JOYSTICK split is also inaccurate; the gamepad page has no such split.)

Minimal fix: map M1 to M4 to Usage 21 to 24 (these resolve to BTN_TRIGGER_HAPPY5..8). Add a BTN_TRIGGER_HAPPY branch to btnCodeToHidUsage, and set the [output.buttons] paddles in vader5.toml to BTN_TRIGGER_HAPPY5..8 (already in input_codes.zig). Also please refresh the stale [report.button_group] comment that still says C/Z/LM/RM are disabled.

VaisVaisov and others added 3 commits May 24, 2026 19:32
Emitting a contiguous Usage Minimum/Maximum range caused the kernel to
assign BTN_GAMEPAD+N for the first 16 buttons and then wrap to BTN_MISC,
regardless of the evdev codes declared in [output.buttons]. As a result,
extra buttons (M1-M4 etc.) landed on wrong BTN_* codes and were invisible
to SDL/Steam.

Replace the range with one Usage (N) item per button, where N is derived
from the button's configured BTN_* code via the kernel's four-group mapping
(BTN_GAMEPAD→1-16, BTN_MISC→17-32, BTN_MOUSE→33-48, BTN_JOYSTICK→49-64).
Codes outside those groups (e.g. BTN_TRIGGER_HAPPY) receive the lowest
free Usage so the descriptor stays valid.

Add btnCodeToHidUsage() to implement the reverse BTN_*→Usage mapping.
Replace BTN_TRIGGER_HAPPY* with BTN_MISC (BTN_0-BTN_8) codes.
BTN_TRIGGER_HAPPY falls outside the kernel's four HID Button Page
groups, so with the UHID backend it received arbitrary Usage numbers
rather than stable ones derived from the evdev code.

BTN_MISC (0x100-0x10F) maps to Usage 17-32, giving each button a
deterministic Usage that SDL can rely on. Also enable C/Z/LM/RM by
default — they are standard Vader 5 Pro buttons with known bit positions.
@BANANASJIM BANANASJIM force-pushed the uhid-button-usage-fix branch from 7a0464b to af8c195 Compare May 25, 2026 02:51
@BANANASJIM BANANASJIM merged commit 1535d8a into BANANASJIM:main May 25, 2026
27 checks passed
BANANASJIM added a commit that referenced this pull request May 27, 2026
…slots) (#353)

## Summary

- Xbox Elite Series 2 evdev spec reserves `BTN_TRIGGER_HAPPY1..4` for back paddles P1..P4. padctl masquerades Vader 5 Pro as Xbox Elite Series 2 (vid=0x045e pid=0x0b00), so the same slot reservation applies.
- PR #235 (1535d8a) enabled `C`/`Z`/`LM`/`RM` in `[output.buttons]` and placed them at `HAPPY1..4`, displacing real back paddles `M1..M4` to `HAPPY5..8` which Steam/games do not recognize as paddles. User-observed regression: pressing extra face buttons (C/Z) appeared as back-paddle input; pressing real back paddles produced no input.
- Move `M1..M4` to `HAPPY1..4` (preserving the M2/M3 swap per PR #323's intent so Vader M2 lands at the Steam Elite P3 slot). Push `C/Z/LM/RM/O` to `HAPPY9..13` — stable evdev codes but no longer in the paddle reserved range.
- Update companion test in `src/io/uhid_descriptor.zig` whose hardcoded HAPPY values and USAGE-number offsets propagated from the TOML.
- Add regression test in `src/test/validate_e2e_test.zig`: (1) positive lock M1..M4 → HAPPY1..4 with M2/M3 swap; (2) structural invariant — none of C/Z/LM/RM/O fall in HAPPY1..4 paddle range.

## Test plan

- [x] `zig build test` green in canonical Docker image (1558 tests, 7 skipped)
- [x] Falsifiability: setting `M1 = BTN_TRIGGER_HAPPY5` fires the positive-lock test and the uhid_descriptor companion test
- [x] User local hardware verification (Flydigi Vader 5 Pro) with `evtest /dev/input/event26`: physical M1..M4 → HAPPY1..4 confirmed, physical C/Z/LM/RM → HAPPY9..12 confirmed
- [x] CI: build / coverage / lean / e2e / install / pkg / systemd hardening compat / distro-check (debian-12 + fedora-41) / tsan all green

Refs #235
@BANANASJIM BANANASJIM mentioned this pull request May 27, 2026
2 tasks
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.

2 participants