Skip to content

feat: Decouple drivers from InputPlumber intenal types and expand event filtering - #753

Merged
pastaq merged 21 commits into
mainfrom
pastaq/device_native_output
Oct 21, 2026
Merged

pastaq merged 21 commits into
mainfrom
pastaq/device_native_output

Conversation

@pastaq

@pastaq pastaq commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

Over time some InputPlumber internal types have made their way into device drivers. As the intent for these drivers has always been to crate them off, this needs correcting. Two InputPlumber internals were used directly on device drivers. OutputEvent and Capability.

OutputEvent was used directly from a target device and sent to a source device with no intermediary. Drivers implemented the conversion directly. To resolve this, give each driver a new OutputEvent type enum. In the SourceDriver impl do conversion from the InputPlumber type to the driver type.

While fixing OutputEvent some bugs were discovered in HapticPulseReport events. Fix those in flight.

Capability was used for managing event filtering. The dbus interface would take an event filter by Capability and pass it to the source impl that were called out, then the impl would pass that filter on to the driver. To resolve this, add a new EventCapability trait that contains to_capabiltiy, from_capability, and to_value functions. Each SourceDriver impl now adds an EventCapability mapping of each driver event to its eventual capability. Adding the inverse and to_value functions also allows us to simplify the translate step and reuse the trait. As part of the filter update I added a new emit() pattern to the device drivers that will check if a generated event is in the current filter before pushing it to the events list. This enables us to filter all events produced by a driver without adding additional nesting with an if .. matches for every generation site.

After addressing the drivers that were cross-contaminated, revisit other source drivers with the new patterns to keep everything synchronized

Specifically out of scope: evdev sources were not addressed. Capability maps applying at the composite device level and the source device level made the series significantly longer than intended and with the additional drivers adjusted already scope creep was already quite large.

A local LLM was used for validation and automating some of the changes after a pattern was established.

@pastaq
pastaq force-pushed the pastaq/device_native_output branch from a89cd2a to ae8024b Compare October 18, 2026 04:01
The design philosophy of the drivers was intended to be self contained
so they could be made into crates. Over time this principle has been
violated. Start the process of fixing this by decoupling drivers from
the InputPlumber native OutputEvent class. Each driver now implements its
own OutputEvent enum, and the source implementation does the event
conversion. For DS5 and Deck, permit directly writing the state from the
matching source if the source and target are the same.

While at it, address a 0x8F event PadSide bug. The newer event swaps the
enum meanings for Left/Right, verified in hid-steam.

Also fix the 0x8F event missing from the Deck target.

This patch is a prerequisite for the next patch in the series.

A local LLM was used for searching kernel and SDL sources for
correctness.
Assisted-by: LLM
Improves the haptic click/tick and haptic pulses behavior on non deck
devices. The duration of click/tick events are now set by using the
to_click()/to_tick() impls and Haptic Pulse events will now properly
emit a series of pulses based on the report data.

Assisted-by: LLM
The kernel FF path for the Ally only drives the two main motor
magnitudes in the 0x0D extended rumble report, leaving the trigger
motor magnitudes unused. As ther is currently no ABI in the kernel
for handling impulse trigger events, write the report directly over
hidraw to drive all four motors, matching the kernel report layout
and its force feedback off pattern.

Add the Steam Deck TriggerVibrationExtended report to the uhid target.
The 0xEC report id is a placeholder pending confirmation.

Report layout and field semantics verified against the vendor HID
specification, usbmon captures, and the kernel hid-asus-ally
driver.

Assisted-by: LLM
Introduces an EventCapability trait in the source layer providing
to_capability() and from_capability() conversions between a device's
own event type and the InputPlumber Capability type. This is a
prerequisite for making the drivers standalone. Drivers will list
their own event types in their filters and the source implementation
will perform the translation.

Assisted-by: LLM
@pastaq
pastaq force-pushed the pastaq/device_native_output branch 2 times, most recently from 195b115 to f3dea5b Compare October 21, 2026 08:33
pastaq added 16 commits October 21, 2026 14:43
The Legion Go drivers used the InputPlumber Capability type for event
filtering, which prevented the drivers from being extracted into
crates. The event filter now stores the driver's own Event type and
matches on event kind via variant_eq(). The source implementations
bridge to Capability through the EventCapability trait.

Filtered events are now prevented at generation time by a filtered
emit() helper in the translate loop instead of being discarded after
the fact. This covers the controller and touchpad drivers.

The shared EventCapability impl gains to_value with the per-variant
value normalization, including the touchpad axis and right pad force
normalization, and the sources' translate_event collapses to glue
that calls to_capability and to_value.

The touchpad source moves into the shared legion_go module as
touchpad.rs since it shares the driver's event type and the module's
EventCapability impl.

Assisted-by: LLM
The IIO IMU driver used the InputPlumber Capability type for event
filtering, which prevented the driver from being extracted into a
crate. The event filter now stores the driver's own Event type and
matches on event kind via variant_eq(). The source implementations
bridge to Capability through the EventCapability trait.

The filter check now runs before the sensor decode so a filtered
channel does not spend cycles decoding and rotating values it will
discard.

The EventCapability impl gains to_value with the per-variant value
normalization, and translate_event collapses to glue that calls
to_capability and to_value.

Assisted-by: LLM
Add event filtering to the DualSense driver so on-demand event
filtering works for DS5 sources. The driver now keeps a list of
filtered events and prevents matching events from being emitted in
its poll loop via an emit helper.

The Event enum gains variant_eq for payload-ignoring kind
comparison, and the input payload structs derive Default so filter
prototypes can be constructed.

The source implements EventCapability for the driver Event type,
bridging the on-demand filter between Capability and the driver's
native event kind. CAPABILITIES moves to the end of the file.

The EventCapability impl gains to_value with the per-variant value
normalization, and translate_event collapses to glue that calls
to_capability and to_value. The per-axis calibration (bias and
raw-to-physical-unit scale) now applies at the driver emit sites, so
InertialInput carries calibrated f64 values and the source-side
Calibration struct is gone.

Assisted-by: LLM
Add event filtering to the Flydigi Vader 4 Pro driver so on-demand
event filtering works for its source. The driver now keeps a list of
filtered events and prevents matching events from being emitted in
its poll loop via an emit helper.

The Event enum gains variant_eq for payload-ignoring kind
comparison, and the input payload structs derive Default so filter
prototypes can be constructed.

The source implements EventCapability for the driver Event type,
bridging the on-demand filter between Capability and the driver's
native event kind.

The EventCapability impl gains to_value with the per-variant value
normalization, and translate_event collapses to glue that calls
to_capability and to_value.

Assisted-by: LLM
Add event filtering to the FTS3528 touchpad driver so on-demand
event filtering works for its source. The driver now keeps a list of
filtered events and prevents matching events from being emitted in
its poll loop via an emit helper.

The Event enum gains variant_eq for payload-ignoring kind
comparison, and the input payload structs derive Default so filter
prototypes can be constructed.

The source implements EventCapability for the driver Event type,
bridging the on-demand filter between Capability and the driver's
native event kind.

The EventCapability impl gains to_value with the per-variant value
normalization, and translate_event collapses to glue that calls
to_capability and to_value.

Assisted-by: LLM
Add event filtering to the GPD device drivers so on-demand event
filtering works for the macro keyboard and touchpad sources. The
drivers now keep a list of filtered events and prevent matching
events from being emitted in their poll loops via an emit helper.

The Event enum gains variant_eq for payload-ignoring kind
comparison, and the input payload structs derive Default so filter
prototypes can be constructed.

The sources move into a hidraw/gpd_device/ subdirectory. The shared
EventCapability impl for the driver Event type, including to_value
with the per-variant value normalization, lives in mod.rs, and each
source's translate_event collapses to glue that calls to_capability
and to_value.

The shared to_value returns None for the touchpad axis and trigger
variants because their normalization constants differ per model. The
2023 and 2024 touchpad sources special-case those variants in
translate_event with their own constants.

Assisted-by: LLM
Add event filtering to the Horipad Steam driver so on-demand
event filtering works for its source. The driver now keeps a list of
filtered events and prevents matching events from being emitted in
its poll loop via an emit helper.

The Event enum gains variant_eq for payload-ignoring kind
comparison, and the input payload structs derive Default so filter
prototypes can be constructed.

The source implements EventCapability for the driver Event type,
bridging the on-demand filter between Capability and the driver's
native event kind.

The EventCapability impl gains to_value with the per-variant value
normalization, and translate_event collapses to glue that calls
to_capability and to_value.

Assisted-by: LLM
Add event filtering to the Legion Go S drivers so on-demand event
filtering works for the IMU, touchpad, and XInput sources. Each of
the three sub-drivers keeps a list of filtered events and prevents
matching events from being emitted in its poll loop via an emit
helper.

The shared Event enum gains variant_eq for payload-ignoring kind
comparison, and the input payload structs derive Default so filter
prototypes can be constructed.

The sources move into a hidraw/legion_go_s/ subdirectory. The shared
EventCapability impl for the driver Event type, including to_value
with the per-variant value normalization, lives in mod.rs, and each
source's translate_event collapses to glue that calls to_capability
and to_value.

Assisted-by: LLM
Add event filtering to the MSI Claw driver so on-demand event
filtering works for its source. The driver now keeps a list of
filtered events and prevents matching events from being emitted in
its poll loop via an emit helper.

The Event enum gains variant_eq for payload-ignoring kind
comparison, and the input payload structs derive Default so filter
prototypes can be constructed.

The source implements EventCapability for the driver Event type,
bridging the on-demand filter between Capability and the driver's
native event kind.

The EventCapability impl gains to_value with the per-variant value
normalization, and translate_event collapses to glue that calls
to_capability and to_value.

Assisted-by: LLM
Add event filtering to the Opineo touchpad driver so on-demand
event filtering works for its source. The driver now keeps a list of
filtered events and prevents matching events from being emitted in
its poll loop via an emit helper.

The Event enum gains variant_eq for payload-ignoring kind
comparison, and the input payload structs derive Default so filter
prototypes can be constructed.

The source implements EventCapability for the driver Event type,
bridging the on-demand filter between Capability and the driver's
native event kind.

The EventCapability impl gains to_value with the per-variant value
normalization, and translate_event collapses to glue that calls
to_capability and to_value.

Assisted-by: LLM
Add event filtering to the OXP HID driver so on-demand event
filtering works for its source. The driver now keeps a list of
filtered events and prevents matching events from being emitted in
its poll loop via an emit helper.

The Event enum gains variant_eq for payload-ignoring kind
comparison, and the input payload structs derive Default so filter
prototypes can be constructed.

The source implements EventCapability for the driver Event type,
bridging the on-demand filter between Capability and the driver's
native event kind.

The EventCapability impl gains to_value with the per-variant value
normalization, and translate_event collapses to glue that calls
to_capability and to_value.

Assisted-by: LLM
Add event filtering to the OXP TTY driver so on-demand event
filtering works for the OneXFly serial and OXP X1 serial sources.
The driver now keeps a list of filtered events and prevents matching
events from being emitted in its poll loop via an emit helper.

The Event enum gains variant_eq for payload-ignoring kind
comparison, and the GamepadButtonEvent gains pressed for value
extraction.

The sources move into a tty/oxp_serial/ subdirectory holding onexfly
and x1. The shared EventCapability impl for the driver Event type,
including to_value with the per-variant axis normalization, lives in
mod.rs, and each source's translate_event collapses to glue that
calls to_capability and to_value.

Assisted-by: LLM
Add event filtering to the Steam Deck driver so on-demand event
filtering works for its source. The driver now keeps a list of
filtered events and prevents matching events from being emitted in
its poll loop via an emit helper. The haptic state machine paths are
untouched.

The Event enum gains variant_eq for payload-ignoring kind
comparison, and the input payload structs derive Default so filter
prototypes can be constructed.

The source implements EventCapability for the driver Event type,
bridging the on-demand filter between Capability and the driver's
native event kind.

The EventCapability impl gains to_value with the per-variant value
normalization, and translate_event collapses to glue that calls
to_capability and to_value.

Assisted-by: LLM
Add event filtering to the Ultimate 2 Wireless driver so on-demand
event filtering works for its source. The driver now keeps a list of
filtered events and prevents matching events from being emitted in
its poll loop via an emit helper.

The Event enum gains variant_eq for payload-ignoring kind
comparison, and the input payload structs derive Default so filter
prototypes can be constructed.

The source implements EventCapability for the driver Event type,
bridging the on-demand filter between Capability and the driver's
native event kind.

The EventCapability impl gains to_value with the per-variant value
normalization, and translate_event collapses to glue that calls
to_capability and to_value.

Assisted-by: LLM
Add event filtering to the XPad UHID driver so on-demand event
filtering works for its source. The driver now keeps a list of
filtered events and prevents matching events from being emitted in
its poll loop via an emit helper.

The Event enum gains variant_eq for payload-ignoring kind
comparison, and the input payload structs derive Default so filter
prototypes can be constructed.

The source implements EventCapability for the driver Event type,
bridging the on-demand filter between Capability and the driver's
native event kind.

The EventCapability impl gains to_value with the per-variant value
normalization, and translate_event collapses to glue that calls
to_capability and to_value.

Assisted-by: LLM
All changes are formatting only: line wrapping and one trailing
whitespace removal. Clippy required no code changes.
@pastaq
pastaq force-pushed the pastaq/device_native_output branch from f3dea5b to 0acbc13 Compare October 21, 2026 21:44
@pastaq
pastaq merged commit d2ecc32 into main Oct 21, 2026
6 checks passed
@pastaq
pastaq deleted the pastaq/device_native_output branch October 21, 2026 21:44
@github-actions

Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 0.83.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant