{"id":48982254,"url":"https://github.com/bananasjim/padctl","last_synced_at":"2026-07-08T11:00:25.943Z","repository":{"id":346278950,"uuid":"1187451278","full_name":"BANANASJIM/padctl","owner":"BANANASJIM","description":"HID gamepad daemon with declarative TOML device config","archived":false,"fork":false,"pushed_at":"2026-07-07T23:55:03.000Z","size":3294,"stargazers_count":70,"open_issues_count":23,"forks_count":8,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-07-08T01:14:20.588Z","etag":null,"topics":["gamepad","hid","hidraw","linux","toml","uinput","zig"],"latest_commit_sha":null,"homepage":null,"language":"Zig","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/BANANASJIM.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-20T18:34:51.000Z","updated_at":"2026-07-07T23:55:06.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/BANANASJIM/padctl","commit_stats":null,"previous_names":["bananasjim/padctl"],"tags_count":18,"template":false,"template_full_name":null,"purl":"pkg:github/BANANASJIM/padctl","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BANANASJIM%2Fpadctl","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BANANASJIM%2Fpadctl/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BANANASJIM%2Fpadctl/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BANANASJIM%2Fpadctl/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/BANANASJIM","download_url":"https://codeload.github.com/BANANASJIM/padctl/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BANANASJIM%2Fpadctl/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35262336,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-08T02:00:06.796Z","response_time":61,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["gamepad","hid","hidraw","linux","toml","uinput","zig"],"created_at":"2026-04-18T11:24:35.429Z","updated_at":"2026-07-08T11:00:25.926Z","avatar_url":"https://github.com/BANANASJIM.png","language":"Zig","funding_links":[],"categories":[],"sub_categories":[],"readme":"# padctl\n\n**Universal Linux gamepad compatibility layer**\n\n\u003e **This project is very much a work in progress.** Feedback, bug reports, and feature requests are welcome — please [open an issue](https://github.com/BANANASJIM/padctl/issues)!\n\n![CI](https://github.com/BANANASJIM/padctl/actions/workflows/ci.yml/badge.svg)\n![License](https://img.shields.io/badge/license-LGPL--2.1--or--later-blue)\n\n## What is padctl\n\npadctl 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.\n\n## Features\n\n- **Declarative device configs** — add new devices with a `.toml` file, no recompilation\n- **Layer system** — hold/toggle/tap-hold layers with independent remaps, gyro, and stick modes\n- **Gyro mouse** — gyro-to-mouse with sensitivity, deadzone, smoothing, and curve controls\n- **Stick mouse/scroll** — left or right stick as mouse or scroll wheel\n- **Macros** — named key sequences bound to any button\n- **Exclusive device grab** — grabs the hidraw/evdev node so the original device is hidden from other processes while padctl is running\n- **Multi-device + hotplug** — automatic device detection and per-device threads via netlink\n- **Hot-reload** — `SIGHUP` re-reads configs without restart, diffed per physical device\n- **Force feedback** — FF_RUMBLE passthrough from uinput to physical device with userspace auto-stop timer (compensates for uinput not using the kernel's ff-memless driver)\n- **Runtime mapping switch** — `padctl switch \u003cname\u003e` changes profiles without restart\n- **Persistent mapping** — `padctl install --mapping \u003cname\u003e` writes a device binding to `/etc/padctl/config.toml` that auto-applies on every boot\n- **User config** — `~/.config/padctl/config.toml` for per-device default mappings (system fallback: `/etc/padctl/config.toml`)\n- **Opt-in diagnostic logging** — `padctl dump enable` turns on a general-purpose, togglable file logger so users can produce a structured log for any class of bug report (force-feedback, input, mapping, hotplug, …). Today it is wired deepest into the rumble/HID path; more subsystems will be instrumented over time. Rotated, bounded on disk, and zero overhead when disabled (default)\n- **CLI tools** — `padctl status`, `padctl devices`, `padctl list-mappings`, `padctl config init/edit/test`, `padctl dump enable/disable/status/export/clear`\n\n## Architecture\n\n```text\n+----------------------------+\n| Physical Device (USB / BT) |\n+----------------------------+\n              |\n      +-------+-------+\n      |               |\n      v               v\n+----------------+  +-------------------+\n| HID / hidraw   |  | Vendor / libusb   |\n| io/hidraw.zig  |  | io/usbraw.zig     |\n+----------------+  +-------------------+\n       \\              /\n        \\            /\n         v          v\n      +--------------------+\n      | DeviceIO (unified) |\n      +--------------------+\n                |\n                v\n      +--------------------+\n      | main loop (ppoll)  |\n      +--------------------+\n                |\n      +---------+---------+\n      |                   |\n      v                   v\n+------------------+  +------------------+\n| config/device.zig|  | io/hotplug.zig   |\n| devices/*.toml   |  | udev monitor     |\n+------------------+  +------------------+\n          |\n          v\n+-----------------------------------------+\n| [input rules] -\u003e interpreter -\u003e state   |\n| [output]     -\u003e OutputConfig            |\n+-----------------------------------------+\n                      |\n                      v\n           +----------------------+\n           | mapper (layer/remap) |\n           +----------------------+\n                |            |\n                v            v\n      +----------------+  +------------------+\n      | gamepad output |  | generic output   |\n      | uinput + aux   |  | generic + touch  |\n      +----------------+  +------------------+\n```\n\n## Supported Devices\n\nShips with production configs for **12 supported device families** across 8\nvendors. `devices/` also contains a Flydigi DInput/2.4G compatibility variant\nplus example/test fixtures, so the raw TOML file count is higher:\n\n**Sony** (3) · **Nintendo** (1) · **Microsoft** (1) · **Valve** (1) · **8BitDo** (1) · **Flydigi** (2) · **HORI** (1) · **Lenovo** (2)\n\n[Full device list with feature matrix →](https://bananasjim.github.io/padctl/devices/)\n\n## Installation\n\n### Arch Linux (AUR)\n\n```sh\nyay -S padctl-bin   # prebuilt binary\nyay -S padctl-git   # build from source\n```\n\nIf you previously installed from source with `sudo padctl install`, remove that\nmanual install before switching to AUR so pacman can own the files:\n\n```sh\nsudo padctl uninstall --prefix /usr --no-immutable\nyay -S padctl-bin   # or: yay -S padctl-git\n```\n\n### Debian / Ubuntu\n\n```sh\ncurl -fLO https://github.com/BANANASJIM/padctl/releases/latest/download/padctl_amd64.deb\nsudo dpkg -i padctl_amd64.deb\n```\n\nFor arm64:\n\n```sh\ncurl -fLO https://github.com/BANANASJIM/padctl/releases/latest/download/padctl_arm64.deb\nsudo dpkg -i padctl_arm64.deb\n```\n\nPackage installs place files on disk; they do not run the live `padctl install`\nphase. Enable the user service from your normal login session:\n\n```sh\nsystemctl --user daemon-reload\nsystemctl --user enable --now padctl.service\nsudo udevadm control --reload-rules\n```\n\nIf your controller config uses `block_kernel_drivers` (currently Flydigi Vader\n5), the driver-block udev rules act on their own: they unbind the kernel driver\nonly while a padctl daemon is running (its control socket exists), so just\nunplug/replug the controller after starting the service.\n\n### From Source\n\nSee [Quick Start](#quick-start) below. For other distros, see [CONTRIBUTING.md](CONTRIBUTING.md#packaging).\n\n## Quick Start\n\n```sh\nzig build                                             # build from source\nsudo zig-out/bin/padctl install                       # install binary, udev rules, service; starts user service when run via sudo\npadctl status                                         # check daemon and detected devices\npadctl config init                                    # create a mapping in ~/.config/padctl/mappings/ interactively\npadctl list-mappings                                  # show generated and installed mapping profiles\npadctl switch \u003cname\u003e                                  # switch mapping profile without restart\n```\n\npadctl runs as a **systemd user service**. The default root install writes the\nsystem-wide user unit under `/usr/lib/systemd/user/` and enables/starts it for\nthe invoking user via `systemctl --user`; immutable and custom-prefix installs\nmay use `/etc/systemd/user/`. The binary and udev rules still require root to\ninstall, but the daemon runs as your own user. Run\n`systemctl --user enable --now padctl.service` manually only if install was run\nwith `--no-enable`, `--no-start`, or without a discoverable `SUDO_USER`.\n\nTo auto-start at boot without an active login session (headless setups, Steam Deck game mode):\n\n```sh\nsudo loginctl enable-linger $USER\n```\n\n\u003e **Bazzite / Steam Deck:** linger behavior depends on the desktop session configuration. Auto-start at boot without login is not verified on these platforms.\n\nSee the [getting started guide](https://bananasjim.github.io/padctl/getting-started.html) for detailed setup.\n\n## CLI Reference\n\n| Command | Description |\n|---------|-------------|\n| `padctl status` | Show daemon state and active devices |\n| `padctl doctor` | Diagnose daemon, service, and device problems with next-step hints |\n| `padctl devices` | List detected HID/USB devices |\n| `padctl list-mappings` | Show available mapping profiles |\n| `padctl switch \u003cname\u003e` | Switch to a named mapping profile |\n| `padctl config init [--device \u003cname\u003e]` | Interactively create a new mapping file in `~/.config/padctl/mappings/` from a template (`default`, `fps`, `racing`, `fighting`) |\n| `padctl config edit \u003cmapping\u003e` | Open mapping in `$VISUAL` or `$EDITOR` |\n| `padctl config test \u003cmapping\u003e` | Live input preview against the mapping (no apply) |\n| `padctl scan` | Re-scan for connected devices |\n| `padctl dump enable\\|disable` | Toggle opt-in diagnostic logging (persists across reboots) |\n| `padctl dump status` | Show logging state, log path, size, and time span |\n| `padctl dump export --period \u003cN\u003em\\|\u003cN\u003eh\\|\u003cN\u003ed [-o file]` | Export recent log window for bug reports |\n| `padctl dump clear` | Delete all log files |\n\n## Build\n\n**Requirements:** Zig 0.15+, libusb-1.0\n\n```sh\nzig build              # build all binaries\nzig build test         # run unit tests\nzig build check-all    # all checks (test + safe + fmt)\n```\n\n| Flag | Default | Effect |\n|------|---------|--------|\n| `-Dlibusb=false` | `true` | Disable libusb linkage (hidraw-only) |\n| `-Dwasm=false` | `true` | Disable WASM plugin runtime |\n\n### Known build issues\n\n**GCC 15 — `R_X86_64_PC64 in .sframe` linker error (issue #147)**\n\n**glibc 2.43+** (shipped on Arch, Artix, and similar bleeding-edge distros) adds `.sframe` sections to `crt1.o` and related startup objects. Zig 0.15.x's linker does not yet handle the `R_X86_64_PC64` relocation type used there, producing:\n\n```\nerror: relocation R_X86_64_PC64 in .sframe section is unsupported\n```\n\nThis is an upstream Zig limitation, not a padctl bug. Workarounds:\n\n1. **Use the canonical Docker image (recommended)** — `./scripts/padctl-docker build` builds inside the Debian bookworm image (glibc 2.36) with the Zig version pinned by `.zigversion`, which is the supported CI build environment. See [Build with Docker](#build-with-docker) below.\n2. **Install Zig 0.15.2 from the official tarball** (`https://ziglang.org/download/`) on a system with **glibc ≤ 2.41** (Debian 12 = glibc 2.36, Ubuntu 22.04 = glibc 2.35, Ubuntu 24.04 = glibc 2.39 all work; Arch with glibc 2.43+ does NOT).\n3. Track upstream fix progress at [ziglang/zig#31272](https://codeberg.org/ziglang/zig/issues/31272).\n\n## Build with Docker\n\nIf you cannot install Zig locally — or hit the glibc 2.43+ linker error above — build padctl inside the canonical Docker image instead. It pins the exact Zig version from `.zigversion` against Debian bookworm (glibc 2.36), so it matches the CI build environment.\n\n```sh\n./scripts/padctl-docker build      # zig build inside the image\n./scripts/padctl-docker test       # zig build test inside the image\n./scripts/padctl-docker shell      # interactive shell for debugging\n```\n\nThe first invocation builds the image (`padctl-build:\u003czig-version\u003e`); later runs reuse it. The repository is bind-mounted at `/src`, so build output lands in your working tree as usual. Requires only Docker — no local Zig toolchain.\n\n## Bazzite / Immutable Distros\n\nOn immutable distributions (Bazzite, Fedora Atomic, etc.) where `/usr` is read-only, use the bootstrap script for a complete one-command setup:\n\n```sh\ncurl -fsSL https://raw.githubusercontent.com/BANANASJIM/padctl/main/scripts/bazzite-setup.sh \\\n  | bash -s -- --mapping vader5\n```\n\nReplace `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.\n\nSee the [Bazzite / Immutable Distros guide](docs/src/immutable-install.md) for full details on what the install does, the `--immutable` flag, security notes, and mapping management.\n\n\u003e **Tested on:** Bazzite (Fedora Atomic / ostree). Other immutable distros may work but are untested.\n\u003e\n\u003e **V2 note:** Bazzite and Steam Deck default linger state has not been verified with the user-service install. `loginctl enable-linger` is required for auto-start without an active session but its interaction with game-mode auto-login is unconfirmed.\n\n## Documentation\n\nFull documentation: [bananasjim.github.io/padctl](https://bananasjim.github.io/padctl/)\n\n- [Getting started](https://bananasjim.github.io/padctl/getting-started.html)\n- [Device config reference](https://bananasjim.github.io/padctl/device-config.html)\n- [Mapping config reference](https://bananasjim.github.io/padctl/mapping-config.html)\n- [Supported devices](https://bananasjim.github.io/padctl/devices/)\n\n## Contributing\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines on adding device configs or contributing code.\n\n## License\n\nLGPL-2.1-or-later — see [LICENSE](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbananasjim%2Fpadctl","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbananasjim%2Fpadctl","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbananasjim%2Fpadctl/lists"}