No description
  • C 77.6%
  • Shell 22.4%
Find a file
gabrielgad 273f0b09e8
All checks were successful
CI / build (push) Successful in 4s
Move CI to .forgejo/workflows: the forge is the only CI
2026-10-22 21:35:48 -04:00
.forgejo/workflows Move CI to .forgejo/workflows: the forge is the only CI 2026-10-22 21:35:48 -04:00
.gitignore evjoyd v0.1.0: userspace joydev replacement (CUSE evdev -> js*) 2026-06-24 06:27:30 -04:00
evjoyd-supervisor evjoyd v0.1.0: userspace joydev replacement (CUSE evdev -> js*) 2026-06-24 06:27:30 -04:00
evjoyd.c evjoyd v0.1.0: userspace joydev replacement (CUSE evdev -> js*) 2026-06-24 06:27:30 -04:00
jsprobe.c evjoyd v0.1.0: userspace joydev replacement (CUSE evdev -> js*) 2026-06-24 06:27:30 -04:00
mkpad.c evjoyd v0.1.0: userspace joydev replacement (CUSE evdev -> js*) 2026-06-24 06:27:30 -04:00
openlog.c evjoyd v0.1.0: userspace joydev replacement (CUSE evdev -> js*) 2026-06-24 06:27:30 -04:00
README.md evjoyd v0.1.0: userspace joydev replacement (CUSE evdev -> js*) 2026-06-24 06:27:30 -04:00
validate.sh evjoyd v0.1.0: userspace joydev replacement (CUSE evdev -> js*) 2026-06-24 06:27:30 -04:00

evjoyd — userspace joydev replacement

A CUSE daemon that translates an evdev device (/dev/input/eventN) into a legacy joystick node (/dev/input/jsN), faithfully reproducing joydev's axis/button map, value correction, and js_event/JSIOC* protocol — so consumers that only speak the old /dev/input/js* API see the controller.

Why

This host removed/blacklisted the in-kernel joydev (it mis-enumerated four identical 8BitDo pads as one controller). That left no js* nodes at all, which breaks path-scanning js* consumers — notably the Chromium Gamepad API (browsers read /dev/input/js* directly). evjoyd restores correct, 1:1-unique js* nodes without bringing joydev back.

Scope note: udev-based discovery (SDL2, Unity's modern path) filters SUBSYSTEM=="input", and CUSE nodes are SUBSYSTEM=="cuse", so those consumers never see an evjoyd node regardless of tagging. evjoyd serves the path-scanning consumers (Chromium); evdev-native games are unaffected. For Unity/InControl games that crash on virtual pads, see the sibling project xpad-gadget.

Design

One process owns one js node (crash isolation, no shared state). A supervisor spawns one per matched controller and assigns unique js numbers. joydev itself stays unloaded — this is its replacement, not a companion.

Build

gcc -O2 -Wall evjoyd.c -o evjoyd $(pkg-config --cflags --libs fuse3)
gcc -O2 -Wall jsprobe.c -o jsprobe      # inspect a js node (debug)
gcc -O2 -Wall mkpad.c   -o mkpad        # synthesize a test uinput pad
gcc -O2 -Wall -shared -fPIC openlog.c -o openlog.so   # open-trace shim (debug)

Requires /dev/cuse (CONFIG_CUSE) and libfuse3.

Run

sudo ./evjoyd --evdev /dev/input/event8 --js 0 [--name "..."] [-f]
# or let the supervisor match an allowlist and spawn one per controller:
./evjoyd-supervisor

validate.sh smoke-tests a node against the joydev protocol.

Files

  • evjoyd.c — the CUSE daemon (one evdev → one js node).
  • evjoyd-supervisor — spawns and assigns one evjoyd per allowlisted controller.
  • jsprobe.c, mkpad.c, openlog.c — debugging helpers.
  • validate.sh — protocol smoke test.