sgl-input 0.5.0

Caller-polled controller discovery, events, and held state for SGL games
Documentation

SGL input

sgl-input provides caller-polled controller events and current button/stick state without a renderer or Steam dependency. macOS 11.3+ uses Apple's Game Controller framework; Windows/Linux use Gilrs. Construct one Gamepads instance on the window's main thread and poll it while the native event loop runs.

use sgl_input::{Gamepads, Button, EventType};
let mut pads = Gamepads::new()?;
// Once per frame, including while the window is unfocused:
for event in pads.poll() {
    match event.event {
        EventType::ButtonPressed(Button::South) => { /* game action if focused */ }
        EventType::Disconnected => { /* clear that player's held actions */ }
        _ => {}
    }
}
# Ok::<(), String>(())

South/East/West/North are face-button positions. Stick values range from -1 to 1 with positive Y pointing up. LeftTrigger/RightTrigger are shoulder buttons; LeftTrigger2/RightTrigger2 are the lower triggers treated as buttons. Press and release events preserve taps between polls; gamepad(id) exposes the latest state and returns None after disconnection. On Windows, Linux and the web (Gilrs), a button held when a pad connects is not reported, and a deflected stick reads 0 until it changes. IDs are local to this owner and must be discarded on disconnection; backends may reuse them for later connections. They are not saved player identities. macOS supports extended-gamepad profiles and omits optional buttons a device does not provide.

On macOS, the owner enables process-local background event delivery so releases are not lost during focus changes, and the last live owner restores the previous value on drop. It does not change system preferences.

The game owns player assignment, focus gating, action bindings, deadzones, menu navigation and release-before-resume behavior. Poll while unfocused to drain releases/disconnects, but do not dispatch gameplay or menu actions then. Keep one owner per process: on macOS a controller's callbacks go to the owner that most recently connected it, so an older owner still alive gets no input from it, even after the newer owner drops, until the controller reconnects. An owner's drop removes only callbacks it still holds, so replacing an owner keeps the new one's input even if the new one polled first.

Try cargo run -p sgl-input-examples --example controllers with a focused window. The example prints actual connection/button/stick events and updates its window title. Device enumeration alone is not evidence that inputs work. Steam Input's native action API is not implemented; emulated devices still depend on the OS backend. See the input contract.

For dependency setup and other SGL building blocks, see Building games with SGL. Repository checks and platform requirements are in Contributing.