Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
# yaml-language-server: $schema=https://raw.githubusercontent.com/ShadowBlip/InputPlumber/main/rootfs/usr/share/inputplumber/schema/capability_map_v2.json
# Schema version number
version: 2

# The type of configuration schema
kind: CapabilityMap

# Name for the device event map
name: Mayflash GameCube Controller Adapter

# Unique identifier of the capability mapping
id: mayflash_gamecube_adapter

# Root cause: this adapter's HID report descriptor declares its top-level

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This comment is over 60 lines long. Nobody is going to read that. Based on the first sentence it is probably not even necessary. Delete it.

# collection as Usage(Joystick) [0x04] rather than Usage(Gamepad) [0x05], with
# its 16 face/shoulder buttons reported under the generic Button usage page
# (0x09, usages 1-16) as a single 16-bit bitmask - not under a labeled gamepad
# usage. Because of this, Linux's kernel HID input core (drivers/hid/hid-input.c)
# assigns the legacy joystick-era evdev codes BTN_TRIGGER (0x120) through
# BTN_DEAD (0x12f) sequentially to these 16 buttons, instead of the modern
# gamepad codes BTN_SOUTH (0x130) etc. that InputPlumber's generic evdev
# translation (src/input/event/evdev.rs) recognizes. Every one of these codes
# currently falls through to the catch-all `_ => Capability::NotImplemented`
# arm, so none of this adapter's face/shoulder buttons produce any usable
# input via the generic gamepad profile.
#
# Bit-to-button mapping below is EMPIRICALLY CONFIRMED against real hardware
# (adapter set to PC/DInput mode - it also has separate Wii U and Pokken
# Tournament modes, selected via button combos, which likely use a
# completely different report layout; this mapping is PC-mode-specific), one
# physical button pressed in complete isolation at a time while watching the
# translated xb360 target device's raw evdev output.
#
# An earlier version of this file assumed the raw bit order followed the
# original Nintendo console-to-controller protocol layout naively (A/B/X/Y
# in that literal order at bits 0-3, Z/R-click/L-click at bits 12-14, and
# Start unambiguous at bit 4). That assumption was WRONG on multiple counts,
# each caught by isolated single-button testing after initial batch tests
# gave misleading results:
# - A/B/X/Y are NOT in that order on the raw bitmask (confirmed by
# isolated presses of each one individually - this actually matches an
# initially-doubted community SDL gamecontrollerdb.txt entry for this
# same adapter, which turned out to be correct after all).
# - Z/R-click/L-click are NOT at bits 12-14 at all - those bits never
# fire no matter what is pressed.
# - Start is NOT at bit 4 - an earlier version of this file concluded
# bit 4 was "shared" between Start and L's digital click because every
# "isolated" Start test up to that point still triggered bit 4, but
# that was because pressing the real Start button on this particular
# unit never actually produced any signal at all through THAT bit -
# what fired was consistently L (confirmed via L's own analog trigger
# axis ramping in lockstep with the bit 4 signal). The real Start
# button was eventually found on bit 9, confirmed by watching the
# DPadDown-diagnostic-mapped ABS_HAT0Y axis move in the positive
# direction exactly when Start (and only Start) was pressed. Once bit 9
# was identified as the true Start bit, bit 4 became unambiguously just
# L's digital click, free to map to LeftBumper like R does to
# RightBumper - there was never a real hardware bit-sharing limitation,
# just a mapping bug in an earlier version of this file.
#
# Complete confirmed bit layout (all 16 bits tested, either directly
# identified or confirmed to produce no signal for anything pressed):
#
# bit 0 (BTN_TRIGGER) = X bit 8 (BTN_BASE3) = unused
# bit 1 (BTN_THUMB) = A bit 9 (BTN_BASE4) = Start
# bit 2 (BTN_THUMB2) = B bit 10 (BTN_BASE5) = unused
# bit 3 (BTN_TOP) = Y bit 11 (BTN_BASE6) = unused
# bit 4 (BTN_TOP2) = L (click) bit 12 (BTN_EXTRA_ONE) = unused
# bit 5 (BTN_PINKIE) = R (click) bit 13 (BTN_EXTRA_TWO) = unused
# bit 6 (BTN_BASE) = unused bit 14 (BTN_EXTRA_THR) = unused
# bit 7 (BTN_BASE2) = Z bit 15 (BTN_DEAD) = unused
#
# L and R are analog triggers (evdev ABS_RX/ABS_RY on this adapter, not the
# more conventional ABS_Z/ABS_RZ) with a mechanical digital click only at
# full travel - a light press produces analog motion only, no KEY event.
# Confirmed by watching each trigger's own axis ramp to max in lockstep
# with its click firing right at the top and releasing right as it comes
# back down.
#
# GameCube button -> InputPlumber's South/East/North/West follows the usual
# cross-platform convention of matching by *function* (South = primary
# confirm, East = secondary/cancel, etc.), not by exact physical diamond
# position, since GameCube's face buttons aren't laid out symmetrically like
# an Xbox pad. Z has no clean Xbox-pad equivalent, so it's mapped to Select
# (unclaimed on this profile's "xb360" target model and present on every
# xb360 model, unlike RightPaddle1/2 which are xbox-elite-only and were
# confirmed to silently produce no output when targeted here).
mapping:
# Physical GameCube A (confirmed via isolated press) is raw bit 1.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

All of these button comments describe what we can already read in the config. Delete them.

- name: South Button (GameCube A)
source_events:
- evdev:
event_type: KEY
event_code: BTN_THUMB
value_type: button
target_event:
gamepad:
button: South

# Physical GameCube B (confirmed via isolated press) is raw bit 2.
- name: East Button (GameCube B)
source_events:
- evdev:
event_type: KEY
event_code: BTN_THUMB2
value_type: button
target_event:
gamepad:
button: East

# Physical GameCube X (confirmed via isolated press) is raw bit 0.
- name: West Button (GameCube X)
source_events:
- evdev:
event_type: KEY
event_code: BTN_TRIGGER
value_type: button
target_event:
gamepad:
button: West

# Physical GameCube Y (confirmed via isolated press) is raw bit 3.
- name: North Button (GameCube Y)
source_events:
- evdev:
event_type: KEY
event_code: BTN_TOP
value_type: button
target_event:
gamepad:
button: North

# Confirmed via isolated press to be raw bit 9 - see the detailed
# investigation notes above for why this took multiple rounds to find.
- name: Start Button (GameCube Start)
source_events:
- evdev:
event_type: KEY
event_code: BTN_BASE4
value_type: button
target_event:
gamepad:
button: Start

# L's digital click, raw bit 4 - confirmed via axis-correlated isolated
# press to be exclusively L, not shared with anything else.
- name: Left Bumper (GameCube L digital click)
source_events:
- evdev:
event_type: KEY
event_code: BTN_TOP2
value_type: button
target_event:
gamepad:
button: LeftBumper

# R's digital click, raw bit 5 - confirmed via axis-correlated isolated
# press, no ambiguity.
- name: Right Bumper (GameCube R digital click)
source_events:
- evdev:
event_type: KEY
event_code: BTN_PINKIE
value_type: button
target_event:
gamepad:
button: RightBumper

# Physical GameCube Z (confirmed via isolated press) is raw bit 7.
- name: Z Button (GameCube Z)
source_events:
- evdev:
event_type: KEY
event_code: BTN_BASE2
value_type: button
target_event:
gamepad:
button: Select

# Bits 6, 8, 10-15 confirmed to produce no signal for any button pressed
# during testing - left unmapped. The D-pad is already handled correctly
# via the normal ABS_HAT0X/Y translation path, unrelated to this bitmask.
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# yaml-language-server: $schema=https://raw.githubusercontent.com/ShadowBlip/InputPlumber/main/rootfs/usr/share/inputplumber/schema/composite_device_v1.json
# Schema version number
version: 1

# The type of configuration schema
kind: CompositeDevice

# Name of the composite device mapping
name: Mayflash GameCube Controller Adapter

# This adapter's HID report descriptor declares its top-level collection as

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

More AI slop comments. Delete them.

# Usage(Joystick) [0x04] rather than Usage(Gamepad) [0x05], with its 16
# face/shoulder buttons reported under the generic Button usage page as a
# plain bitmask rather than a labeled gamepad usage. Because of this, the
# Linux kernel assigns the legacy joystick-era evdev codes BTN_TRIGGER
# (0x120) through BTN_DEAD (0x12f) to these buttons instead of the modern
# gamepad codes (BTN_SOUTH/0x130 etc.) that the 65-generic_gamepad.yaml
# fallback profile (and InputPlumber's generic evdev translation) expect -
# so without this profile, none of this adapter's face/shoulder buttons
# produce usable input. See capability_maps/mayflash_gamecube_adapter.yaml
# for the full writeup and the specific button mapping, which is applied
# below via capability_map_id.
#
# Note: 60-xbox_360_gamepad.yaml already lists a vendor/product entry for
# "0079"/"18d4", which does not match this adapter's actual reported product
# ID "1844" (this looks like a transposed-digit typo in that file, unrelated
# to this profile) - even if that typo were fixed, matching the xbox_360
# profile alone would not solve this, since the actual problem is the
# evdev button-code translation, not device selection.
# Empty: this profile is selected purely by the vendor_id/product_id match
# on the source_devices entry below, not by any system-level match (like
# DMI data for handheld-specific profiles).
matches: []

# Maximum number of source devices per CompositeDevice. Each of this
# adapter's 4 ports enumerates as its own separate evdev device with the
# same vendor/product ID; each gets its own CompositeDevice.
maximum_sources: 1

# NOTE: capability_map_id exists as BOTH a CompositeDeviceConfig-level field
# and a SourceDevice-level field (src/config/mod.rs) - they are NOT the same
# mechanism. The top-level one only affects capability *declaration*
# bookkeeping for legacy V1 chord-style maps (composite_device/mod.rs
# load_capability_map / device.capabilities); it does not translate anything.
# The ACTUAL per-event translation (raw evdev KEY -> Capability, via
# EventTranslator) is wired only from the field on each source_devices[]
# entry below (src/input/source/evdev.rs EventDevice::new, which builds the
# GamepadEventDevice's EventTranslator from THIS field, not the top-level
# one). Setting only the top-level field (as an earlier version of this
# profile did) parses fine and loads a CompositeDevice successfully, but
# silently never translates any button - confirmed live: axis/D-pad events
# passed through correctly while all face-button presses produced zero
# output events. Both fields are set below to be safe.
capability_map_id: mayflash_gamecube_adapter

source_devices:
- group: gamepad
unique: true
capability_map_id: mayflash_gamecube_adapter
evdev:
vendor_id: "0079"
product_id: "1844"
handler: event*

# The target input device(s) to emulate by default
target_devices:
- xb360
- mouse
- keyboard
14 changes: 14 additions & 0 deletions src/config/capability_map/evdev.rs
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,17 @@ pub enum EventCode {
BtnBase4,
BtnBase5,
BtnBase6,
/// evdev code 0x12c. No symbolic name exists in the Linux kernel headers

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

More unnecessary comments.

/// (linux/input-event-codes.h jumps from BTN_BASE6/0x12b straight to

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This should be fixed in the kernel.

/// BTN_DEAD/0x12f), but the code itself is a valid, distinct evdev key
/// code that some hardware (e.g. HID joysticks whose report descriptor
/// declares a generic 16+ button Button usage page under a Joystick
/// rather than Gamepad top-level collection) legitimately reports.
BtnExtraOne,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This has no translation to a NativeEvent. It needs a full path to becoming a Capability or it isn't valid as an event.

It also starts a new convention instead of continuing an existing one, why note ButtonBase7-9?

/// evdev code 0x12d. See [EventCode::BtnExtraOne].
BtnExtraTwo,
/// evdev code 0x12e. See [EventCode::BtnExtraOne].
BtnExtraThree,
BtnDead,
BtnGamepad,
BtnSouth,
Expand Down Expand Up @@ -1090,6 +1101,9 @@ impl EventCode {
EventCode::BtnBase4 => 0x129,
EventCode::BtnBase5 => 0x12a,
EventCode::BtnBase6 => 0x12b,
EventCode::BtnExtraOne => 0x12c,
EventCode::BtnExtraTwo => 0x12d,
EventCode::BtnExtraThree => 0x12e,
EventCode::BtnDead => 0x12f,
EventCode::BtnGamepad => 0x130,
EventCode::BtnSouth => 0x130,
Expand Down
47 changes: 47 additions & 0 deletions src/config/config_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,3 +145,50 @@ async fn check_autostart_rules() -> Result<(), Box<dyn Error>> {

Ok(())
}

/// Quick validation test for the new Mayflash GameCube Controller Adapter

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This doesn't need its own test

/// capability map and device profile added to fix legacy joystick-range
/// evdev button codes (BTN_TRIGGER/0x120 through BTN_DEAD/0x12f) not being
/// recognized as gamepad buttons.
#[test]
fn check_mayflash_gamecube_adapter_configs_parse() {
use crate::config::capability_map::CapabilityMapConfig;

let map = CapabilityMapConfig::from_yaml_file(
"./rootfs/usr/share/inputplumber/capability_maps/mayflash_gamecube_adapter.yaml",
)
.expect("capability map should parse");
assert_eq!(map.id(), "mayflash_gamecube_adapter");

let device_config = CompositeDeviceConfig::from_yaml_file(
"./rootfs/usr/share/inputplumber/devices/55-mayflash_gamecube_adapter.yaml".to_string(),
)
.expect("device profile should parse");
assert_eq!(device_config.name, "Mayflash GameCube Controller Adapter");
assert_eq!(
device_config.capability_map_id.as_deref(),
Some("mayflash_gamecube_adapter"),
"capability_map_id must be set at the CompositeDeviceConfig level to actually apply"
);
assert_eq!(device_config.source_devices.len(), 1);
let evdev = device_config.source_devices[0]
.evdev
.as_ref()
.expect("source device should have an evdev matcher");
assert_eq!(evdev.vendor_id.as_deref(), Some("0079"));
assert_eq!(evdev.product_id.as_deref(), Some("1844"));
// Setting capability_map_id only at the CompositeDeviceConfig level (as
// asserted above) is NOT sufficient to actually translate events - only
// the SourceDevice-level field wires up the EventTranslator that
// performs real-time evdev KEY -> Capability translation
// (src/input/source/evdev.rs EventDevice::new). Confirmed live: without
// this field set here, axis/D-pad events passed through fine but every
// face-button press produced zero output events.
assert_eq!(
device_config.source_devices[0].capability_map_id.as_deref(),
Some("mayflash_gamecube_adapter"),
"capability_map_id must ALSO be set on the source_devices entry itself \
(not just at the CompositeDeviceConfig level) or button translation \
silently does nothing at runtime"
);
}