
CtrlAssist brings "controller assist" functionality to Linux gaming by allowing multiple physical controllers to operate as a single virtual input device. This enables collaborative play and customizable gamepad setups, making it easier for players of all ages and abilities to enjoy games together. While similar features exist on modern game consoles, CtrlAssist is an open source project that enhances accessibility for PC gaming, offering additional quality-of-life improvements through virtual input devices on Linux.
โจ Features
- ๐ฎ Combine physical controllers into one virtual gamepad
- Assign controllers as either Primary or Assist
- ๐๏ธ Customizable multiplexing modes for buttons and axes
- Logically merging or preempting events is flexible
- ๐ Hide physical controllers for improved game compatibility
- Multiple hiding strategies for avoiding interference
- ๐น๏ธ Spoof gamepad vendor for in-game layout recognition
- Mimic either Primary or Assist controller hardware
- ๐ซจ Rumble pass-through from virtual to physical devices
- Forward force feedback to either or both controllers
- ๐ฑ๏ธ System tray interface for graphical desktop environments
- Configure controllers and mux options via the taskbar
- Start/stop/alter muxing with live status notifications
- Persistent user settings across session restarts

๐๏ธ Modes
- ๐ Priority (default): Assist controller overrides when active
- Axes: Prioritize Assist when active (exceeds deadzone)
- Buttons: Prioritize Assist when button released
- Triggers: Prioritize largest value from either
- Ideal for partial and asynchronous assistance
- E.g. Assist for movement while Primary for actions
- Axes: Prioritize Assist when active (exceeds deadzone)
- โ๏ธ Average: Blend weighted inputs from both controllers
- Axes: Averaged when both are active (exceed deadzone)
- Buttons: logically OR'ed between pressed controllers
- Triggers: Averaged when both are active (exceed deadzone)
- Ideal for cooperative input and subtle corrections
- E.g. For counter steer/brake assist in racing games
- Axes: Averaged when both are active (exceed deadzone)
- ๐ Toggle: Switch Active controller on demand
- All inputs forwarded from currently active controller
- Toggle Active controller via the Mode button on Assist
- Immediately synchronizes input to current Active state
- Ideal when fine-grain conflict-free control is needed
- E.g. Game menu navigation or precise interventions
- All inputs forwarded from currently active controller
Screencast_20251230_070245.webm
โฌ๏ธ Install
The following installation methods are available:
- ๐ฆ Cargo (Rust package manager)
- Ideal for customization and unsandboxed use
- Suitable for development and contributing
- E.g. fork custom features and upstream fixes
- ๐ฆ Flatpak (Linux application sandbox)
- Ideal for easy install on SteamOS, Bazzite, etc.
- Suitable for immutable Linux distributions
- E.g. where installing build tools is a hassle
๐ฆ Cargo
- Build dependencies:
- Rust toolchain:
- https://rust-lang.org/tools/install/
- configure
PATHper Notes linked above
Install or upgrade to the latest version:
๐ฆ Flatpak
- Runtime dependency:
- Flatpak (likely already installed)
Download latest bundle from releases page and install:
Run and test via Flatpak using the application ID:
Or launch the system tray via the installed desktop icon.
๐ Usage
Use the --help flag for information on each CLI subcommand:
)
๐ฑ๏ธ tray
Launch the system tray app for graphical control:
The system tray provides:
- Controller selection menus for Primary and Assist
- Configuration options for mux mode, hiding, spoofing, and rumble
- Start/Stop buttons with visual feedback
- Live status indicator in the tray icon
- Desktop notifications for status changes
- Persistent settings saved to disk on use
Device invariant options can be altered while the mux is running; all other options are disabled (greyed out) until the mux is stopped.
๐งพ list
List all detected controllers and respective IDs:
()
()
๐ mux
Multiplex first two detected controllers by default:
)
)
๐ฎ Primary Assist Mapping
Manually specify Primary and Assist controllers via IDs:
)
)
๐๏ธ Mux Mode Selection
Manually specify mode for merging controllers:
๐น๏ธ Spoof Virtual Device
Mimic controller hardware for in-game layout recognition:
)
)
) )
[!WARNING] Combining spoofing with some hiding strategies may also hide the virtual device.
๐ซจ Rumble Pass-Through
Target force feedback to either, none, or both physical controllers:
๐ Hide Physical Devices
Multiple hiding strategies are available to avoid input conflicts:
| Strategy | Access/Compatibility | Granularity | Restart Required |
|---|---|---|---|
| Steam | No root, Flatpak compatible | Vendor/Product ID | Steam only |
| System | Root required, no Flatpak | Per-device | Game/Launcher |
Use Steam hiding when running CtrlAssist via Flatpak. For 2v1 scenarios, where a third player not using CtrlAssist shares the same controller make and model, use System to avoid hiding the third player's gamepad.
Steam Input
Automatically configure Steam's controller blacklist:
[!NOTE] Restart Steam for blacklist to take effect; CtrlAssist reverts config on exit.
[!WARNING] Combining this hiding strategy with spoofing may also hide the virtual device.
System Level
Restrict device tree permissions system-wide:
[!NOTE] Restart game/launcher to force rediscovery; CtrlAssist reverts change on exit.
[!IMPORTANT] Not possible via Flatpak sandbox for security. Use
--hide steaminstead.
โ๏ธ Configuration
The system tray saves settings to $XDG_CONFIG_HOME/ctrlassist/config.toml:
# Last selected controllers (by name for best-effort matching)
= "Microsoft Xbox One"
= "PS4 Controller"
# Mux configuration
= "Priority"
= "Steam"
= "None"
= "Both"
Settings are loaded on startup and saved when using the mux. Controllers are matched by name (best-effort) if IDs change between sessions.
โ ๏ธ Limitations
- System hiding requires root access (not available in Flatpak)
- Temporarily modifies group permissions for selected devices
- Hiding must be done before starting games or launchers
- Processes with open file handles may retain device access
- Reconnecting a hidden controller may revert its visibility
- Steam hiding persists across reconnects while CtrlAssist is running
- System hiding: custom udev rules needed for persistent permissions
- Steam hiding affects all controllers of the same make and model
- Blacklists by vendor/product ID, not individual devices
- Steam hiding requires Steam restart
- Steam only checks controller_blacklist config on startup
- Toggle mode requires pressing all buttons and axes after startup
- gilrs lazily initializes gamepad state used for synchronization