class

Tryst::SDL::Gamepad

Inherits Reference < Object

An SDL3 gamepad: buttons, analog sticks, triggers and rumble.

Wraps SDL's Gamepad API (SDL2's GameController, renamed), which maps physical controls to a fixed Xbox-style layout automatically - higher-level than the raw Joystick API, and what works out of the box for Xbox, PlayStation, Switch Pro and most others.

gp = Tryst::SDL::Gamepad.first
if gp
  puts gp.name
  puts "A pressed: #{gp.button?(:a)}"
  puts "Left stick X: #{gp.axis(:left_x)}"
  gp.destroy
end
Tryst::SDL::Gamepad.on_button do |instance_id, button, pressed|
  puts "#{button} #{pressed ? "pressed" : "released"}"
end

# In a game loop, or a Tk timer:
Tryst::SDL::Gamepad.poll_events

Constants

AXES = [:left_x, :left_y, :right_x, :right_y, :trigger_left, :trigger_right]

Every axis this shard names.

AXIS_MAX = 32767
AXIS_MIN = -32768

Analog stick axis range: -32768..32767 (centred at 0).

AXIS_SYMBOLS = AXIS_VALUES.each_with_object({} of Int32 => Symbol) do |__temp_84, symbols| key, value = __temp_84 symbols[value] = key end
AXIS_VALUES = {:left_x => LibSDL::GAMEPAD_AXIS_LEFTX, :left_y => LibSDL::GAMEPAD_AXIS_LEFTY, :right_x => LibSDL::GAMEPAD_AXIS_RIGHTX, :right_y => LibSDL::GAMEPAD_AXIS_RIGHTY, :trigger_left => LibSDL::GAMEPAD_AXIS_LEFT_TRIGGER, :trigger_right => LibSDL::GAMEPAD_AXIS_RIGHT_TRIGGER} of Symbol => Int32
BUTTON_SYMBOLS = BUTTON_VALUES.each_with_object({} of Int32 => Symbol) do |__temp_83, symbols| key, value = __temp_83 symbols[value] = key end
BUTTON_VALUES = {:a => LibSDL::GAMEPAD_BUTTON_SOUTH, :b => LibSDL::GAMEPAD_BUTTON_EAST, :x => LibSDL::GAMEPAD_BUTTON_WEST, :y => LibSDL::GAMEPAD_BUTTON_NORTH, :back => LibSDL::GAMEPAD_BUTTON_BACK, :guide => LibSDL::GAMEPAD_BUTTON_GUIDE, :start => LibSDL::GAMEPAD_BUTTON_START, :left_stick => LibSDL::GAMEPAD_BUTTON_LEFT_STICK, :right_stick => LibSDL::GAMEPAD_BUTTON_RIGHT_STICK, :left_shoulder => LibSDL::GAMEPAD_BUTTON_LEFT_SHOULDER, :right_shoulder => LibSDL::GAMEPAD_BUTTON_RIGHT_SHOULDER, :dpad_up => LibSDL::GAMEPAD_BUTTON_DPAD_UP, :dpad_down => LibSDL::GAMEPAD_BUTTON_DPAD_DOWN, :dpad_left => LibSDL::GAMEPAD_BUTTON_DPAD_LEFT, :dpad_right => LibSDL::GAMEPAD_BUTTON_DPAD_RIGHT} of Symbol => Int32

button/axis <-> the raw SDL enum value, both ways - a Hash pair each rather than two long case/when chains matching the same data, which is what these actually are.

BUTTONS = [:a, :b, :x, :y, :back, :guide, :start, :left_stick, :right_stick, :left_shoulder, :right_shoulder, :dpad_up, :dpad_down, :dpad_left, :dpad_right]

Every button this shard names. SDL3 itself has more (paddles, a touchpad click, several vendor-specific "misc" buttons) - this is the portable Xbox-style subset every modern pad maps onto, the same set ruby-tryst's SDL2 port exposed.

DEAD_ZONE = 8000

Default dead zone threshold for analog sticks.

TRIGGER_MAX = 32767
TRIGGER_MIN = 0

Trigger axis range: 0..32767 (0 = released, 32767 = fully pressed).

Constructors

new(ptr : Pointer(LibSDL::Gamepad))

@api private - use .open/.first/.all

Source
open(instance_id : UInt32) : Gamepad

Opens the gamepad with the given instance id (see .ids).

Source

Class methods

all

Opens every connected gamepad.

Source
apply_dead_zone(value : Int32, threshold : Int32 = DEAD_ZONE) : Int32

Zero if value's magnitude is below threshold, otherwise value unchanged - the small resting drift a real stick reports at rest is what this is for.

Source
attach_virtual

Attaches a virtual gamepad device - no hardware required - so the whole surface above can be exercised in a headless CI container. Returns the instance id (.open takes it, same as any real device). Raises if one is already attached.

Source
axis_symbol(value : Int32) : Symbol | Nil
Source
axis_value(axis : Symbol) : Int32
Source
button_symbol(value : Int32) : Symbol | Nil
Source
button_value(button : Symbol) : Int32
Source
count

The number of connected gamepads.

Source
detach_virtual

Removes the virtual gamepad .attach_virtual created. A no-op if none is attached.

Source
first

Opens the first available gamepad, or nil if none are connected.

Source
ids

Instance ids of every device SDL currently recognizes as a gamepad - what SDL3 hands #open directly, unlike SDL2's separate device-index/instance-id pair (there is only one id now).

Source
init_subsystem

Brings up the gamepad subsystem. Called automatically by every other class method - only useful to call early, e.g. before the first #poll_events so a hot-plug of an already-connected pad at startup is not missed.

Source
on_added

Registers a block for a newly connected gamepad, seen through .poll_events. The instance id it is called with is what .open takes.

Source
on_axis

Registers a block for analog stick/trigger motion, seen through .poll_events.

Source
on_button

Registers a block for button press/release events, seen through .poll_events. Replaces any block registered earlier.

Source
on_removed

Registers a block for a disconnected gamepad, seen through .poll_events.

Source
poll_events

Pumps SDL's event queue and dispatches gamepad events to whichever callbacks (#on_button, #on_axis, #on_added, #on_removed) are registered. Returns how many gamepad events were processed.

Call this periodically (e.g. every 16-50ms, from a Tk timer or a game loop) for event-driven input.

SDL_PollEvent pumps the platform event loop, which on macOS is the Cocoa run loop - shared with Tk's own Aqua backend. Use .update_state instead when only fresh #button?/#axis values are needed and event callbacks are not, to avoid contending with Tk for that run loop.

Source
shutdown_subsystem

Shuts the gamepad subsystem down on its own, leaving audio/video (if either is up) running. Existing Gamepad objects become unusable.

Source
update_state

Refreshes every open gamepad's state WITHOUT pumping the platform event loop - SDL_UpdateGamepads only, none of SDL_PollEvent's SDL_PumpEvents call. After this, #button?/#axis answer with fresh values; event callbacks do not fire - use .poll_events for those.

Source
virtual_id

The virtual gamepad's instance id, or nil if none is attached.

Source

Instance methods

attached?

Whether the controller is still physically connected.

Source
axis(axis : Symbol) : Int32

The current value of axis: AXIS_MIN..AXIS_MAX for a stick, TRIGGER_MIN..TRIGGER_MAX for a trigger. See AXES for the valid symbols.

Source
button?(button : Symbol) : Bool

Whether button is currently pressed. See BUTTONS for the valid symbols.

Source
destroy

Closes the controller. Further use raises. Safe to call twice.

Source
destroyed?
Source
guid

A GUID string identifying this controller's model - the same model always reports the same GUID, unlike #instance_id which changes across unplug/replug. Useful as a config key for persisting per-controller settings.

Source
instance_id
Source
name

The controller's human-readable name (e.g. "Xbox One Controller").

Source
rumble(low_freq : Int, high_freq : Int, duration_ms : Int) : Bool

Triggers haptic feedback (rumble). low_freq/high_freq are motor intensities (0..65535), duration_ms how long. Returns whether the controller supports it - not every controller does, and that is not an error.

Source
set_virtual_axis(axis : Symbol, value : Int) : Nil

Sets an axis's value on a virtual gamepad (see .attach_virtual). Raises on a real device.

Source
set_virtual_button(button : Symbol, pressed : Bool) : Nil

Sets a button's state on a virtual gamepad (see .attach_virtual). Raises on a real device - there is nothing to set.

Source