Universal Linux gamepad compatibility layer
This project is very much a work in progress. Feedback, bug reports, and feature requests are welcome — please open an issue!
padctl is a userspace daemon that maps vendor-specific USB/HID gamepad reports to standard Linux input events via uinput. Device support is driven entirely by declarative TOML configs — no kernel patches, no custom drivers.
- Declarative device configs — add new devices with a
.tomlfile, no recompilation - Layer system — hold/toggle/tap-hold layers with independent remaps, gyro, and stick modes
- Gyro mouse — gyro-to-mouse with sensitivity, deadzone, smoothing, and curve controls
- Stick mouse/scroll — left or right stick as mouse or scroll wheel
- Macros — named key sequences bound to any button
- Exclusive device grab — grabs the hidraw/evdev node so the original device is hidden from other processes while padctl is running
- Multi-device + hotplug — automatic device detection and per-device threads via netlink
- Hot-reload —
SIGHUPre-reads configs without restart, diffed per physical device - Force feedback — FF_RUMBLE passthrough from uinput to physical device
- Runtime mapping switch —
padctl switch <name>changes profiles without restart - User config —
~/.config/padctl/config.tomlfor per-device default mappings - CLI tools —
padctl status,padctl devices,padctl list-mappings,padctl config init/edit/test
+----------------------------+
| Physical Device (USB / BT) |
+----------------------------+
|
+-------+-------+
| |
v v
+----------------+ +-------------------+
| HID / hidraw | | Vendor / libusb |
| io/hidraw.zig | | io/usbraw.zig |
+----------------+ +-------------------+
\ /
\ /
v v
+--------------------+
| DeviceIO (unified) |
+--------------------+
|
v
+--------------------+
| main loop (ppoll) |
+--------------------+
|
+---------+---------+
| |
v v
+------------------+ +------------------+
| config/device.zig| | io/hotplug.zig |
| devices/*.toml | | udev monitor |
+------------------+ +------------------+
|
v
+-----------------------------------------+
| [input rules] -> interpreter -> state |
| [output] -> OutputConfig |
+-----------------------------------------+
|
v
+----------------------+
| mapper (layer/remap) |
+----------------------+
| |
v v
+----------------+ +------------------+
| gamepad output | | generic output |
| uinput + aux | | generic + touch |
+----------------+ +------------------+
Ships with configs for 12 devices across 8 vendors:
Sony (3) · Nintendo (1) · Microsoft (1) · Valve (1) · 8BitDo (1) · Flydigi (2) · HORI (1) · Lenovo (2)
Full device list with feature matrix →
yay -S padctl-bin # prebuilt binary
yay -S padctl-git # build from sourcecurl -fLO https://github.com/BANANASJIM/padctl/releases/latest/download/padctl_0.1.0_amd64.deb
sudo dpkg -i padctl_0.1.0_amd64.debFor arm64, replace amd64 with arm64.
See Quick Start below. For other distros, see CONTRIBUTING.md.
zig build # build from source
sudo zig-out/bin/padctl install # install binary, configs, udev rules, systemd service
sudo systemctl enable --now padctl.service # start daemon with hotplug support
padctl config init # create ~/.config/padctl/config.toml interactively
padctl status # check daemon and detected devices
padctl switch <name> # switch mapping profile without restartSee the getting started guide for detailed setup.
| Command | Description |
|---|---|
padctl status |
Show daemon state and active devices |
padctl devices |
List detected HID/USB devices |
padctl list-mappings |
Show available mapping profiles |
padctl switch <name> |
Switch to a named mapping profile |
padctl config init |
Create user config interactively |
padctl config edit |
Open user config in $EDITOR |
padctl config test |
Validate config without applying |
padctl scan |
Re-scan for connected devices |
Requirements: Zig 0.15+, libusb-1.0
zig build # build all binaries
zig build test # run unit tests
zig build check-all # all checks (test + safe + fmt)| Flag | Default | Effect |
|---|---|---|
-Dlibusb=false |
true |
Disable libusb linkage (hidraw-only) |
-Dwasm=false |
true |
Disable WASM plugin runtime |
On immutable distributions (Bazzite, Fedora Atomic, etc.) where /usr is read-only, use the bootstrap script for a complete one-command setup:
curl -fsSL https://raw.githubusercontent.com/BANANASJIM/padctl/main/scripts/bazzite-setup.sh \
| bash -s -- --mapping vader5Replace vader5 with the mapping for your controller, or omit --mapping to install without a mapping. When run locally (bash scripts/bazzite-setup.sh), the script prompts for mapping selection interactively.
See the Bazzite / Immutable Distros guide for full details on what the install does, the --immutable flag, security notes, and mapping management.
Tested on: Bazzite (Fedora Atomic / ostree). Other immutable distros may work but are untested.
Full documentation: bananasjim.github.io/padctl
See CONTRIBUTING.md for guidelines on adding device configs or contributing code.
LGPL-2.1-or-later — see LICENSE.