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
- Controllers are assigned 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
โฌ๏ธ 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
Add the --force flag to upgrade to latest version:
๐ฆ Flatpak
- Runtime dependencies
- Flatpak (likely already installed)
Download latest bundle from releases page and install:
export VERSION=v0.3.0 wget https://github.com/ruffsl/ctrlassist/releases/download/$VERSION/ctrlassist.flatpak flatpak install --user ctrlassist.flatpak
Run and test via Flatpak using the application ID:
flatpak run io.github.ruffsl.ctrlassist --help
Or launch as system tray via installed desktop icon.
๐ Usage
Use the --help flag for information on each CLI subcommand:
$ ctrlassist --help Multiplex multiple controllers into virtual gamepad Usage: ctrlassist <COMMAND> Commands: list List all detected controllers and respective IDs mux Multiplex connected controllers into virtual gamepad tray Launch system tray app for graphical control help Print this message or the help of the given subcommand(s) Options: -h, --help Print help -V, --version Print version
๐ฑ๏ธ tray
Launch the system tray app for graphical control:
$ ctrlassist tray
CtrlAssist system tray started
Configure and control the mux from your system tray
Press Ctrl+C to exitThe 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
Options are greyed out while the mux is running but show current active selections.
๐งพ list
List all detected controllers and respective IDs:
$ ctrlassist list (0) Microsoft Xbox One (1) PS4 Controller
๐ mux
Multiplex first two detected controllers by default:
$ ctrlassist mux Primary: (0) Microsoft Xbox One Assist: (1) PS4 Controller ... Mux Active. Press Ctrl+C to exit.
๐ฎ Primary Assist Mapping
Manually specify Primary and Assist controllers via IDs:
$ ctrlassist mux --primary 1 --assist 0 Primary: (1) PS4 Controller Assist: (0) Microsoft Xbox One ...
๐๏ธ Mux Mode Selection
Manually specify mode for merging controllers:
$ ctrlassist mux --mode priority ...
๐น๏ธ Spoof Virtual Device
Mimic controller hardware for in-game layout recognition:
$ ctrlassist mux --spoof primary Primary: (0) Microsoft Xbox One Assist: (1) PS4 Controller Virtual: (2) Microsoft X-Box One pad (Firmware 2015)
Warning
Combining spoofing with some hiding strategies may also hide the virtual device.
๐ซจ Rumble Pass-Through
Target force feedback to either or both physical controllers:
$ ctrlassist mux --rumble both ...
๐ Hide Physical Devices
There are multiple hiding strategies 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 |
For example, use Steam when running CtrlAssist via Flatpak. For 2v1 scenarios, where the 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:
$ ctrlassist mux --hide steam ...
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:
$ sudo ctrlassist mux --hide system ...
Note
Restart game/launcher to force rediscovery; CtrlAssist reverts change on exit.
Important
Not possible via Flatpak sandbox for security. Use --hide steam instead.
โ๏ธ Configuration
The system tray saves settings to $XDG_CONFIG_HOME/ctrlassist/config.toml:
# Last selected controllers (by name for best-effort matching) primary_name = "Microsoft Xbox One" assist_name = "PS4 Controller" # Mux configuration mode = "Priority" hide = "Steam" spoof = "None" rumble = "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
- 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

