Skip to content

fix(Zotac Zone): fix Steam/QAM/View buttons and duplicate virtual gamepad - #664

Open
whirlwind80 wants to merge 3 commits into
ShadowBlip:mainfrom
whirlwind80:fix/zotac-zone-device-names
Open

fix(Zotac Zone): fix Steam/QAM/View buttons and duplicate virtual gamepad#664
whirlwind80 wants to merge 3 commits into
ShadowBlip:mainfrom
whirlwind80:fix/zotac-zone-device-names

Conversation

@whirlwind80

Copy link
Copy Markdown

Summary

Three related fixes for the Zotac Gaming Zone (board G0A1W/G1A1W), found while
debugging why the physical Steam and QAM buttons stopped working after
switching from an HHD-based image to InputPlumber:

  • Device name mismatch (50-zotac-zone.yaml): the gamepad and keyboard
    evdev source names didn't match what the kernel actually reports
    (Zotac Technology Limited ZOTAC GAMING ZONE[...]), so those source
    devices were never matched at all.
  • Steam/QAM capability bindings swapped (zone_type1.yaml): F17 (Steam
    button) was bound to QuickAccess, which synthesizes the Guide+South
    chord that opens the Quick Access Menu — so the Steam button opened QAM
    instead of the main Steam menu. F18 (QAM button) was bound to
    QuickAccess2, which has no evdev output at all on the xbox-elite target,
    so it did nothing. Swapped so F17 -> Guide and F18 -> QuickAccess,
    matching how every other handheld's single QAM-style shortcut key is
    mapped elsewhere in this repo.
  • Duplicate composite device + missing View button (50-zotac-zone.yaml):
    the gamepad evdev source matched purely by name, but that name is reported
    identically on USB interfaces 1.1, 1.2, and 1.3, causing InputPlumber to
    create three separate CompositeDevices (and three duplicate virtual Xbox
    controllers) for one physical controller. Added phys_path to pin the
    match to interface 1.1 only. Separately, the real face buttons / sticks /
    triggers / View (Select) button are exposed by the kernel's built-in
    xpad driver as a standard joystick on interface 1.0, which wasn't
    referenced by this config at all — Steam was reading that raw joystick
    directly instead of the translated virtual controller, so View never
    reached it. Added that node as a gamepad source so InputPlumber grabs and
    hides it from Steam and translates it like the rest of the controller.

Also corrected the mouse/dial group's evdev name to match the kernel-reported
name. This makes that source device match, but the rear paddle buttons and
the dial still don't produce any events through the nodes InputPlumber can
see — those need a real hidraw report parser (the current zotac_zone
hidraw source is a no-op stub) and are left as follow-up work, not included
here.

Test plan

All verified on real Zotac Gaming Zone (G0A1W) hardware running Bazzite:

  • make test (clippy + full test suite + autostart-rules check) passes
  • Steam button opens the main Steam menu
  • QAM button opens the Quick Access Menu
  • Analog sticks, ABXY, shoulder buttons, triggers, and D-pad work through
    a single virtual gamepad (previously duplicated 3x)
  • View (Select) button works
  • Only one CompositeDevice/virtual gamepad is created per physical
    controller (verified via busctl DBus introspection)

Not fixed / follow-up needed (separate issue, not blocking this PR):

  • Rear paddle (macro) buttons — no signal on any evdev node InputPlumber
    currently sees; needs hidraw reverse-engineering
  • Dial wheels — same underlying cause as paddles

Disclosure

Root-caused, implemented, and verified on hardware in collaboration with
Claude Sonnet 5 (Claude Code), which diagnosed the capability-translation
behavior in xpad.rs/evdev.rs and the duplicate-device/raw-joystick
behavior via evtest/journalctl/DBus introspection on the running device,
and proposed these fixes. I reviewed and understand every changed line.

The evdev name matches for the gamepad and keyboard source devices
didn't match what the kernel actually reports (verified via
/proc/bus/input/devices), so InputPlumber's CompositeDevice could
never match these source devices and the physical Steam/QAM buttons
(routed through the keyboard interface as KEY_F17/KEY_F18) were never
translated to gamepad QuickAccess/QuickAccess2 events.

Co-developed-by: Claude Sonnet 5
The physical Steam button sends KEY_F17 and the physical QAM button
sends KEY_F18, but the capability map had them backwards: F17 was
bound to QuickAccess, and F18 was bound to QuickAccess2.

xpad.rs's write_event() special-cases QuickAccess to synthesize a
Guide+South chord, which gamescope recognizes as "open Quick Access
Menu" — so binding it to the Steam button made that button open QAM
instead of the main Steam menu. QuickAccess2 has no evdev output at
all on the xbox-elite target (event_codes_from_capability returns an
empty code list for it), so binding it to the QAM button made that
button do nothing.

Swap the two: F17 -> Guide (plain press, opens the main Steam menu),
F18 -> QuickAccess (opens the Quick Access Menu). This also matches
how every other handheld's single "quick access" shortcut key is
mapped elsewhere in this repo (Ally, Ayaneo, OneXPlayer, MSI Claw,
AYN, GPD all bind their QAM-style key to QuickAccess, never
QuickAccess2).

Verified on hardware: Steam button now opens the main Steam menu, QAM
button now opens the Quick Access Menu.

Root-caused, fixed, and verified on hardware in collaboration with
Claude Sonnet 5, which diagnosed the xpad.rs/evdev.rs capability
translation behavior and proposed this fix.

Co-developed-by: Claude Sonnet 5 <noreply@anthropic.com>
The gamepad evdev source matched purely by name, but "Zotac Technology
Limited ZOTAC GAMING ZONE" is reported identically on USB interfaces
1.1, 1.2, and 1.3. This made InputPlumber create three separate
CompositeDevices (and three duplicate virtual "Microsoft X-Box One
Elite 2 pad" targets) for one physical controller. Add phys_path to
pin the match to interface 1.1 (the real gamepad node) only.

The physical face buttons, sticks, triggers, and View (Select) button
are exposed by the kernel's built-in xpad driver as a standard
XInput-compatible joystick on interface 1.0, which this config didn't
reference at all. Steam was reading that raw joystick directly instead
of InputPlumber's translated virtual controller, and the View button
in particular never reached the virtual controller (which declares
Select as a capability but had no source producing it, since the
zotac_zone hidraw source is a no-op stub). Add that node as a gamepad
source so InputPlumber grabs it, hiding it from Steam, and translates
it through the same virtual controller as everything else.

Also correct the mouse/dial group's evdev name to what the kernel
actually reports ("...ZONE Mouse", not "...Dials"), so that source
device matches at all. The dial still doesn't produce any relative-
axis events through this node in practice, so dial support remains
broken; that needs a real hidraw report parser and is tracked as a
separate follow-up, same as the rear paddle buttons.

Verified on hardware: sticks, face buttons, shoulder buttons,
triggers, and the View button all work through a single virtual
gamepad; Steam/QAM button behavior is unaffected (no regression).

Root-caused, fixed, and verified on hardware in collaboration with
Claude Sonnet 5, which traced View/duplicate-controller behavior via
evtest/journalctl/DBus introspection on the running device and
proposed this fix.

Co-developed-by: Claude Sonnet 5 <noreply@anthropic.com>
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