-
Notifications
You must be signed in to change notification settings - Fork 0
feat: Add Multiple Hide Strategies with Steam Blacklist Support #26
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Refactors the --hide flag from a boolean to an enum supporting multiple hiding strategies. This enables automatic Steam controller blacklist configuration, making CtrlAssist fully functional in Flatpak environments without requiring root access. Changes: - Replace --hide boolean flag with HideType enum (none/steam/system) - Add Steam config.vdf parsing and modification for controller_blacklist - Refactor ScopedDeviceHider to support multiple hide strategies - Add automatic restoration of original configs on exit via RAII - Extract vendor/product IDs directly from evdev::Device::input_id() - Add dirs dependency for cross-platform home directory resolution Hide Strategies: - none (default): No hiding, manual launcher configuration required - steam: Automatically manages Steam's controller_blacklist (Flatpak-compatible) * Blacklists by vendor/product ID (affects all controllers of same model) - system: Restricts device permissions (requires root, original behavior) * Hides specific device instances for granular control Usage: # Steam hiding (recommended for Flatpak) ctrlassist mux --hide steam # System hiding (requires root) sudo ctrlassist mux --hide system BREAKING CHANGE: The --hide flag now requires a value (steam/system/none) instead of being a boolean flag. Migration: --hide → --hide system
as when using steam input hiding, users are more likely to wonder why steam is ignoring the virtual gamepad as well
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR refactors the --hide flag from a boolean to an enum supporting multiple hiding strategies, with a new Steam controller blacklist feature that enables CtrlAssist to work in Flatpak environments without requiring root access. The Steam hiding strategy automatically modifies Steam's config.vdf to blacklist physical controllers and restores the original configuration on exit.
Key Changes:
- Introduced
HideTypeenum with three variants:None(default),Steam, andSystem - Implemented automatic Steam controller blacklist management via VDF config parsing
- Changed
SpoofTargetenum default fromPrimarytoNoneto better complement Steam hiding
Reviewed changes
Copilot reviewed 6 out of 7 changed files in this pull request and generated 11 comments.
Show a summary per file
| File | Description |
|---|---|
| src/main.rs | Adds HideType enum and updates hide logic to dispatch to appropriate strategy; changes SpoofTarget default to None |
| src/udev_helpers.rs | Refactors ScopedDeviceHider to support multiple hiding strategies with separate state tracking and Steam VDF config parsing/modification |
| Cargo.toml | Adds dirs dependency for home directory resolution |
| Cargo.lock | Updates lock file with new dependencies (dirs, dirs-sys, getrandom, libredox, option-ext, redox_users, thiserror, thiserror-impl, wasi) |
| flatpak/io.github.ruffsl.ctrlassist.yml | Adds filesystem permission for Steam config directory (~/.local/share/Steam/config:rw) |
| flatpak/cargo-sources.json | Adds cargo source entries for new dependencies |
| README.md | Updates documentation with new hiding strategies, usage examples, comparison table, and limitations; fixes outdated Flatpak workaround instructions |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Co-authored-by: Copilot <[email protected]>
Co-authored-by: Copilot <[email protected]>
Co-authored-by: Copilot <[email protected]>
Co-authored-by: Copilot <[email protected]>
…n Steam config helpers
Makes ScopedDeviceHider constructor infallible by deferring Steam config path resolution until it's actually needed. This follows Rust idioms of keeping constructors cheap and simple while moving error handling to the point of use. Changes: - Remove Result return type from ScopedDeviceHider::new() - Implement lazy initialization in hide_steam() method - Resolve home directory only when Steam hiding is first attempted - Cache resolved config path in Option<PathBuf> for subsequent uses Benefits: - Simpler API: no error handling required at construction site - No premature failures: constructor succeeds even if home unavailable - Works in headless environments when only system hiding is used - Error occurs at point of use with proper context - More idiomatic Rust: cheap constructors, deferred fallible ops Before: let mut hider = ScopedDeviceHider::new(args.hide.clone())?; After: let mut hider = ScopedDeviceHider::new(args.hide.clone());
Summary
This PR refactors the
--hideflag to support multiple hiding strategies, enabling automatic Steam controller blacklist configuration. This makes CtrlAssist fully functional in Flatpak environments without requiring root access or manual configuration.Follow up to:
Motivation
The current
--hideimplementation uses system-level device permissions, which has limitations:config.vdfto blacklist controllerslsusband reformatting themThis PR automates the Steam configuration workaround and provides a clean abstraction for future hiding strategies.
Changes
Core Changes
Refactored
--hideflag: Changed frombooltoHideTypeenum with three variants:none(default): No hiding appliedsteam: Automatically manages Steam's controller blacklistsystem: Restricts device permissions (original behavior, requires root)New Steam hiding implementation:
~/.local/share/Steam/config/config.vdfevdev::Device::input_id()controller_blacklistin the format Steam expects (045e/02dd,054c/05c4)Enhanced
ScopedDeviceHider:systemandsteamhiding strategiesDropReverts default variant for
SpoofTargetenum toNone:Files Modified
src/main.rs: UpdatedHideTypeenum and hide logicsrc/udev_helpers.rs: RefactoredScopedDeviceHiderwith Steam supportCargo.toml: Addeddirsdependency for home directory resolutionREADME.md: Updated documentation with new hide strategies and comparison tableBenefits
For Flatpak Users
✅ Works without root access
✅ No manual config editing required
✅ Automatic cleanup on exit
✅ Steam restart is the only requirement
For Native Linux Users
✅ Choice between Steam and system-level hiding
✅ Existing
systemhiding still available for per-device control✅ System hiding supports complex scenarios (e.g., 2v1 with same controller models)
✅ Consistent interface across both methods
For All Users
✅ Explicit behavior through enum variants
✅ Automatic restoration prevents orphaned configs
✅ Better error messages and logging
Usage Examples
Steam Hiding (Recommended for Flatpak)
System Hiding (Requires Root)
# Original behavior: restrict device permissions sudo ctrlassist mux --hide systemNo Hiding (Default)
# No automatic hiding, manual launcher configuration needed ctrlassist muxChoosing the Right Strategy
Use Steam Hiding when:
Use System Hiding when:
Use No Hiding when:
Comparison Table
Key Differences
Steam Hiding operates at the vendor/product ID level:
System Hiding operates at the device node level:
/dev/input/event16)Migration Guide
Breaking Change
The
--hideflag now requires a value instead of being a boolean flag.Before:
After:
For Flatpak users who were manually editing config.vdf:
# No more manual editing needed! flatpak run io.github.ruffsl.ctrlassist mux --hide steam --spoof noneTesting
Tested Scenarios
Testing Instructions
Test Steam Hiding
--hide steam~/.local/share/Steam/config/config.vdfcontains newcontroller_blacklistentriesTest System Hiding
sudoand--hide systemls -l /dev/input/event*0600permissions (root-only)0660Test Default Behavior
--hideflagImplementation Notes
RAII Pattern
Both hide strategies use RAII (Resource Acquisition Is Initialization) to ensure cleanup:
hide_gamepad_devices()is calledScopedDeviceHideris droppedGranularity Trade-offs
The two hiding strategies operate at different levels of granularity:
Steam Hiding (Vendor/Product ID Level)
This is because Steam's
controller_blacklistformat only supports vendor/product ID pairs, not specific device paths. While this simplifies configuration, it means all controllers of the same make/model are affected.Scenario where this matters: 2v1 gameplay
--hide systemfor per-device granularitySystem Hiding (Device Node Level)
This introspects the device tree to find all related nodes (input events, hidraw) and hides them individually. Other controllers of the same model remain accessible.
Scenario where this matters: 2v1 gameplay
Steam Config Format
Steam's VDF format requires specific formatting:
The implementation preserves indentation and structure while updating the blacklist value.
Future Enhancements
Potential improvements for future PRs:
--hide steam--hide steam