[Unreleased]

Added

  • Gamepad.start_link/1: starts a reader that sends each controller event to a :sink, either a pid (sent {:gamepad, event}) or a one-argument function. :source overrides the detected reader: {:helper, path} runs any program that writes Gamepad.Wire lines, and {:linux, glob} reads the joystick device files matching glob. Returns :ignore when the machine has no reader.
  • Gamepad.available?/0 and Gamepad.source/0: whether a reader exists and which one (:linux, {:helper, path} or nil).
  • Gamepad.Wire: the line protocol (device, gone, button, axis) and parse/1, which turns a line into an event map — :connected, :disconnected, :button with pressed, :axis with value — or nil. Buttons and axes use Xbox layout names, listed by buttons/0 and axes/0. Sticks run -1.0..1.0, triggers 0.0..1.0.
  • Linux reader: /dev/input/js* read directly and decoded by Gamepad.Linux (devices/0,1, which takes the glob to list, and decode/2); new device files are picked up every two seconds. The device files must be readable by the user.
  • macOS reader: priv/gamepad_helper, built from c_src/gamepad_helper.swift on GameController.framework by mix compile when swiftc is on PATH. Without swiftc the build skips it and start_link/1 returns :ignore.
  • Windows reader: priv/gamepad_helper.exe, built from c_src/gamepad_helper_win.c on XInput by mix compile through Makefile.win. Requires the Visual Studio Build Tools (cl and nmake).
  • Browser reader: priv/static/gamepad.js, whose Gamepad.watch(sink, options) polls a page's Gamepad API and calls sink with one Gamepad.Wire line per change. options.every polls on a millisecond timer instead of every animation frame, stop() on the returned handle ends it, and Gamepad.available() reports whether the page has the API. Gamepad.browser_script/0 returns the file's path for serving.

Fixed

  • The macOS and Windows helpers exit when the program reading them closes their input, as it does on exiting or crashing. They kept running, one more for every run of the program.
  • On Linux the device readers run inside the VM and end with it.