The XInput Gamepad Device

Platforms: Windows

class psychopy.iohub.devices.xinput.Gamepad(*args, **kwargs)[source]

Bases: psychopy.iohub.devices.xinput.XInputDevice

The ioHub xinput.Gamepad Device supports monitoring events from a Windows XInput complient device. Due to the nature of the XInput interface, these events can be provided regardless of whether a PsychoPy Window is currently created or has focus.

The ioHub XInput interface supports the full XInput 1.3 specification. XInput events can be read, the capabilities of the connected bevice can be queried, as well as the current battery level for wireless devices. On device hardware that supports vibration / rubble feedback support, this can be controlled via the ioHub xinput.Gamepad.setRumble(lowFrequencyValue,highFrequencyValue,duration) method.

XInput compatible gamepad’s which have been tested are:

  1. XBOX 360 for Windows Wireless controller.+
  2. Logitech F710 Wireless controller.+
  3. Logitech F310 Wired controller.

+ These controllers support the rumble (vibration) setting feature supported by the ioHub XInput Interface.

getButtons(gamepad_state=None)[source]

Returns a dictionary providing the button states from the last GamepadStateChangeEvent read. The dictionary contains the following key: value pairs:

  • time : float. The sec.msecusec time when the button states being reported were read.
  • confidence_interval : float. The sec.msecusec difference between the device poll that contained the event data being used, and the previous device poll time. Therefore, it is possible that the device event time reported could have been between the time reported and reported time - confidence_interval.
  • button_name : button_state. These key: value pair will be included multiple times in the dict; where each key is a button label constant as defined in XInputGamePadConstants, and the value is a bool indicating if the button is Pressed (True) or in a released (False) state.
Args:
None
Returns:
dict: Button state information dict as described in the method doc.
getTriggers(gamepad_state=None)[source]

Returns a dictionary providing the left and right gamepad trigger states from the last GamepadStateChangeEvent read. The dictionary contains the following key: value pairs:

  • time : float. The sec.msecusec time when the trigger states being reported were read.
  • confidence_interval : float. The sec.msecusec difference between the device poll that contained the event data being used, and the previous device poll time. Therefore, it is possible that the device event time reported could have been between the time reported and reported time - confidence_interval.
  • left_trigger: float. The state of the left trigger, normalized between 0.0 and 1.0 . 0.0 indicates the trigger is not pressed in at all; 1.0 indicates the trigger has been fully pulled in. The value has 8 bit resolution.
  • right_trigger: float. The state of the right trigger, normalized between 0.0 and 1.0 . 0.0 indicates the trigger is not pressed in at all; 1.0 indicates the trigger has been fully pulled in. The value has 8 bit resolution.
Args:
None
Returns:
dict: Trigger state information dict as described in the method doc.
getPressedButtonList()[source]

Returns a list of button names, as defined in XInputGamePadConstants, that are currently pressed.

Args:
None
Returns:
list: Names of buttons currently pressed.
getThumbSticks(gamepad_state=None)[source]

Returns a dictionary providing the state of the left and right thumbsticks on the gamepad. based on data from last GamepadStateChangeEvent read. The dictionary contains the following key:value pairs:

  • time : float. The sec.msecusec time when the thumbstick states being reported were read.
  • confidence_interval : float. The sec.msecusec difference between the device poll that contained the event data being used, and the previous device poll time. Therefore, it is possible that the device event time reported could have been between the time reported and reported time - confidence_interval.
  • left_stick: (x, y, magnitude). The state of the left thumbstick as a tuple of three float values, each with 12 bit resolution. x and y can range between -1.0 and 1.0, representing the position each dimention of the current thumbstick position (- values are leftward or downward, + values are rightward or upward). The magnitude can be between 0.0 and 1.0, representing the amount the thumbstick has been moved in the combined 2D movement space. 0.0 equals no movement, 1.0 equals the thumbstick being pushed fully to the edge of motion.
  • right_stick: (x, y, magnitude). The state of the right thumbstick as a tuple of three float values, each with 12 bit resolution. x and y can range between -1.0 and 1.0, representing the position each dimention of the current thumbstick position (- values are leftward or downward, + values are rightward or upward). The magnitude can be between 0.0 and 1.0, representing the amount the thumbstick has been moved in the combined 2D movement space. 0.0 equals no movement, 1.0 equals the thumbstick being pushed fully to the edge of motion.
Args:
None
Returns:
dict: Thumbstick state information dict as described in the method doc.
getRumbleState()[source]

Returns the current amount the low and high frequency rumble motors are engaged as a (low,high) tuple, returned as the % of possible intensitity for each motor. The resolution of the value for each motor is 16 bits.

Args:
None
Returns:
tuple: (low_frequency, high_frequency) rumble motor stats on devices that support vibration / rumble settings. Each value is returned as a percentage between 0.0 and 100.0.
setRumble(lowFrequencyValue=0, highFrequencyValue=0, duration=1.0)[source]

Sets the xinput.Gamepad device’s vibration / rumble feedback state for each of the low and high requency vibration motors. This ethod is only supported on devices that physically support rumble setting.

By using different low and high drequence settings and duraations, different vibration feedback effects can be achieved. This can be further enhanced by adjusting each motors output state in steps over a period of time.

The percentage provided for each vibration motor indicates how string the motor should be enabled relative to the full range of vitration strengths supported.

This method runs asyncronously to the PsychoPy experiment script, so regardless of the duration set, the method returns when the vibration command has been issued to the device by the ioHub Process.

If the experiment script sets the rumble state again before the last rumble state’s duration is complete, then the new setRumble updates the rumble state and the previous state is ended prior to the full duration specified by the earlier command.

Args:

lowFrequencyValue (float): Percentage that the low frequency rumble motor should be set to within it’s possible output range. 0.0 is Off. 100.0 is full power. The underlying rumble API uses 16 bit resolution values for setting rumble state.

highFrequencyValue (float): Percentage that the high frequency rumble motor should be set to within it’s possible output range. 0.0 is Off. 100.0 is full power. The underlying rumble API uses 16 bit resolution values for setting rumble state.

duration (float): sec.msec duration that the rumble settings should be acctive for. When the duration had passed, the rumble states are set to 0. REgardless of the duration value, the method is run asyncronously and returns to the PsychoPy script as soon as the sate changes have been issues to the native device.

Returns:
(float,float): (command_return_time, command_call_duration), where command_return_time is the sec.msec time that the call to the native device to update vibration setting returned to the ioHub process, and command_call_duration is the sec.msec time taken for the native device call to return to the ioHub process.
updateBatteryInformation()[source]

Informs the xinput gamepad to read the battery status information from the native device and return the latest battery status information.

Given the possible uses of the returned information, it is unlikely that the status needs to be read from the native device every time the calling program needs battery status. Therefore using the getLastReadBatteryInfo() can be called instead as needed.

Args: None

Returns:
tuple: (battery_level, battery_type) where battery_level is a string constant indicating if the device’s battery is at a low, medium, or high charge level. battery_type indicates if the gamepad actually uses a battery, or if it is a wired device and is powered via USB. In the latter case, [‘BATTERY_LEVEL_FULL’, ‘BATTERY_TYPE_WIRED’] is always returned.
getLastReadBatteryInfo()[source]

Return the last read battery status information from the xinput.Gamepad device. This method does not query the native device for the most recent battery status data, and instead returns the values read the last time the native device was actually queried.

To have the battery status information updated and return this latest status data, use updateBatteryInformation()

Args: None

Returns:
tuple: (battery_level, battery_type) where battery_level is a string constant indicating if the device’s battery was at a low, medium, or high charge level the last time the device was queried for this information. battery_type indicates if the gamepad actually uses a battery, or if it is a wired device and is powered via USB. In the latter case, [‘BATTERY_LEVEL_FULL’, ‘BATTERY_TYPE_WIRED’] is always returned.
updateCapabilitiesInformation()[source]

Informs the xinput gamepad to read the capability information available from the native device and return this data in a dictionary format.

Given the possible uses of the returned information, it is unlikely that the capability information needs to be read from the native device every time the calling program needs to know what capabilities the device supports. Therefore using the getLastReadCapabilitiesInfo() method will likely be a better choice to use during the runtime of your program. An even better option would be to read the device capabilities from the ioHub Process once, cache them in your script as local variables, and use the local variables you created during the rest of the experiment. ;)

The return value is a dict, keys represent different reported capability types, and the associated value is what the current deive supports for the given capability:

  • type : either XBOX360_GAMEPAD or OTHER_XINPUT_GAMEPAD
  • subtype : either XINPUT_GAMEPAD or XINPUT_UNKNOWN_SUBTYPE
  • supported_buttons : list of the supported buttons in string form, as specified in the XInputGamePadConstants class.
  • left_trigger_support : True == supported, False otherwise
  • right_trigger_support : True == supported, False otherwise
  • low_frequency_vibration_support : True == supported, False otherwise
  • high_frequency_vibration_support : True == supported, False otherwise

Args: None

Returns:
tuple: The device capabilities in dict format.
getLastReadCapabilitiesInfo()[source]

Return the last read capability information for the xinput.Gamepad device connected.

This method does not query the native device for this data, and instead returns the data read the last time the native device was actually queried, using updateCapabilitiesInformation().

Given the possible uses of the returned information, it is unlikely that the capability information needs to be read from the native device every time the calling program needs to know what capabilities the device supports. Therefore using this method conpared to updateCapabilitiesInformation() will likely be a better choice during the runtime of your program.

An even better option would be to read the device capabilities from the ioHub Process once, cache them in your script as local variables, and use the local variables you created during the rest of the experiment. ;)

The return value is a dict, keys represent different reported capability types, and the associated value is what the current deive supports for the given capability:

  • type : either XBOX360_GAMEPAD or OTHER_XINPUT_GAMEPAD
  • subtype : either XINPUT_GAMEPAD or XINPUT_UNKNOWN_SUBTYPE
  • supported_buttons : list of the supported buttons in string form, as specified in the XInputGamePadConstants class.
  • left_trigger_support : True == supported, False otherwise
  • right_trigger_support : True == supported, False otherwise
  • low_frequency_vibration_support : True == supported, False otherwise
  • high_frequency_vibration_support : True == supported, False otherwise

Args: None

Returns:
tuple: The device capabilities in dict format.
clearEvents()

Clears any DeviceEvents that have occurred since the last call to the device’s getEvents(), or clearEvents() methods.

Note that calling clearEvents() atthe device level only clears the given device’s event buffer. The ioHub Process’s Global Event Buffer is unchanged.

Args:
None
Returns:
None
enableEventReporting(enabled=True)

Specifies if the device should be reporting events to the ioHub Process (enabled=True) or whether the device should stop reporting events to the ioHub Process (enabled=False).

Args:
enabled (bool): True (default) == Start to report device events to the ioHub Process. False == Stop Reporting Events to the ioHub Process. Most Device types automatically start sending events to the ioHUb Process, however some devices like the EyeTracker and AnlogInput device’s do not. The setting to control this behavour is ‘auto_report_events’
Returns:
bool: The current reporting state.
getConfiguration()

Retrieve the configuration settings information used to create the device instance. This will be a combination of the default settings for the device (found in iohub.devices.<device_name>.default_,defice_name>.yaml, plus any device settings specified by the experiment author within an iohub_config.yaml file if the ioHubExperimentRuntime is being used to define the experiment logic, or if using the iohub.launchHubProcess() function in the experriment script, as device settings in dictionary form.

Changing any values in the returned dictionary has no effect on the device state.

Args:
None
Returns:
(dict): The dictionary of the device configuration settings used to create the device.
getEvents(*args, **kwargs)

Retrieve any DeviceEvents that have occurred since the last call to the device’s getEvents() or clearEvents() methods.

Note that calling getEvents() at a device level does not change the Global Event Buffer’s contents.

Args:

event_type_id (int): If specified, provides the ioHub DeviceEvent ID for which events should be returned for. Events that have occurred but do not match the event ID specified are ignored. Event type ID’s can be accessed via the EventConstants class; all available event types are class atttributes of EventConstants.

clearEvents (int): Can be used to indicate if the events being returned should also be removed from the device event buffer. True (the defualt) indicates to remove events being returned. False results in events being left in the device event buffer.

asType (str): Optional kwarg giving the object type to return events as. Valid values are ‘namedtuple’ (the default), ‘dict’, ‘list’, or ‘object’.

Returns:
(list): New events that the ioHub has received since the last getEvents() or clearEvents() call to the device. Events are ordered by the ioHub time of each event, older event at index 0. The event object type is determined by the asType parameter passed to the method. By default a namedtuple object is returned for each event.
isReportingEvents()

Returns whether a Device is currently reporting events to the ioHub Process.

Args: None

Returns:
(bool): Current reporting state.

GamePad Device Configuration Settings

# This section includes all valid xinput.Gamepad Device
# settings that can be specified in an iohub_config.yaml
# or in Python dictionary form and passed to the launchHubProcess function
# Any device parameters not specified when the device class is
# created by the ioHub Process will be assigned the default value
# indicated here.
#
xinput.Gamepad:
    # name: The name you want to assign the xinput.Gamepad device for the experiment
    #   This name is what will be used to access the device within the experiment
    #   script via the devices.[device_name] property of the ioHubConnection or
    #   ioHubExperimentRuntime classes.
    #
    name: gamepad

    # enable: Specifies if the device should be enabled by ioHub and monitored
    #   for events.
    #   True = Enable the device on the ioHub Server Process
    #   False = Disable the device on the ioHub Server Process. No xinput.Gamepad events will
    #   be reported by the ioHub Server.
    #
    enable: True
 
    # device_number: Up to 4 XInput 'users' can be connected to the computer at one time. The
    #   gamepad's user ID is based on how many other gamepads are already connected to the
    #   computer when the new gamepad turns on. If a gamepad with a lower user id
    #   then disconnects from the computer, other active gamepad user ids remain the same.
    #   Therefore XInput user id is not equal to the index of the gamepad in a set 
    #   of gamepads. If the experiment should connect to the first active gamepad found, enter -1.
    #   Otherwise enter the user_id (1-4) of the gamepad user ID you wish to connect to.
    device_number: -1

    # monitor_event_types: *If* the ioHubDataStore is enabled for the experiment, then
    #   indicate if events for this device should be saved to the
    #   monitor_event_types: Specified which xinput.Gamepad Event types should be monitored
    #   for and therefore saved to the DataStore or sent to the Experiment Process.
    #
    monitor_event_types:  [GamepadStateChangeEvent,]

    # save_events: Save xinput.Gamepad events to the data_collection/xinput.Gamepad event 
    #   group in the hdf5 event file.
    #   True = Save events for this device to the ioDataStore.
    #   False = Do not save events for this device in the ioDataStore.
    #
    save_events: True

    # streamEvents: Indicate if events from this device should be made available
    #   during experiment runtime to the Experiment / PsychoPy Process.
    #   True = Send events for this device to  the Experiment Process in real-time.
    #   False = Do *not* send events for this device to the Experiment Process in real-time.
    #
    stream_events: True

    # auto_report_events: Indicate if events from this device should start being
    #   processed by the ioHub as soon as the device is loaded at the start of an experiment,
    #   or if events should only start to be monitored on the device when a call to the
    #   device's enableEventReporting method is made with a parameter value of True.
    #   True = Automatically start reporting events for this device when the experiment starts.
    #   False = Do not start reporting events for this device until enableEventReporting(True)
    #       is set for the device during experiment runtime.
    #
    auto_report_events: True

    # event_buffer_length: Specify the maximum number of events (for each
    #   event type the device produces) that can be stored by the ioHub Server
    #   before each new event results in the oldest event of the same type being
    #   discarded from the ioHub device event buffer.
    #
    event_buffer_length: 256

    # device_timer: Devices that require polling to read any new native events
    #   that have become available must specifiy a device_timer property, with an
    #   interval sub proerty that specifies how frequently the device would be polled
    #   by the ioHub Server. The time is specified in sec.msec format and is a 'requested'
    #   polling interval. The actual polling interval will vary from this somewhat, the 
    #   magnitude of which depends on the computer hardware and OS being used and the
    #   number of other polled devices being monitored. The 'configdence_interval'
    #   attribute of events that have a parent device that is polled often can be used to
    #   determine the actual polling rate being achieved by the ioHub Process.
    device_timer:
        interval: 0.005

    # serial_number: The serial number for the specific isnstance of device used
    #   can be specified here. It is not used by the ioHub, so is FYI only.
    #
    serial_number: N/A

    # manufacture_date: The date of manufactiurer of the device 
    # can be specified here. It is not used by the ioHub,
    # so is FYI only.
    #   
    manufacture_date: DD-MM-YYYY

    # The device manufacturer's name.
    #   It is not used by the ioHub, so is FYI only.
    #
    manufacturer_name: N/A

    # model_name: The device model name can be specified here.
    #   It is not used by the ioHub, so is FYI only.
    #
    model_name: N/A

    # model_number: The device model number can be specified here.
    #   It is not used by the ioHub, so is FYI only.
    #
    model_number: N/A
    
    # software_version: The device driver and / or SDK software version number.
    #   This field is not used by ioHub, so is FYI only. 
    #
    software_version: N/A

    # hardware_version: The device's hardware version can be specified here.
    #   It is not used by the ioHub, so is FYI only.
    #
    hardware_version: N/A
    
    # firmware_version: If the device has firmware, its revision number
    #   can be indicated here. It is not used by the ioHub, so is FYI only.
    #
    firmware_version: N/A
    

GamePad Event Types

class psychopy.iohub.devices.xinput.GamepadStateChangeEvent(*args, **kwargs)[source]

Bases: psychopy.iohub.devices.DeviceEvent

GamepadStateChangeEvent events are generated when ever any aspect of the gamepad’s state changes (i.e. when a button event occurs, and trigger value changes, or a thumbstick value changes).

The event includes fields to hold the updated information for all the possible state changes that can occur. Fields that have a value of 0.0 indicate no change has occurred for that aspect of the gamepad’s fucntionality. Non zero values indicate the gamepad controls that have had a state change.

Note that multiple gamepad controls can report a state change in one event. For example, the left thumbstick can move, the right trigger can be released a bit, and a button can be pressed all at the same time; in which case a GamepadStateChangeEvent will be returned that represents all of these changes and the time the device reported all of the changes as occurring simultaniously.

Event Type ID: EventConstants.GAMEPAD_STATE_CHANGE

Event Type String: ‘GAMEPAD_STATE_CHANGE’

button_ids

All the buttons that were pressed at the time of the state change, represented as an unsigned int of the pressed button ID constants logically & together.

buttons

All the buttons that were pressed at the time of the state change, represented as a list of button name string constants.

left_thumb_stick

The state of the left thumbstick if it changed at the time the device event was created. Thumbstick data is represented in normalized units between -1.0 and 1.0 for x direction, y direction and between 0.0 and 1.0 for magnitude of the thumbstick. x and y are the horizontal and vertical position of the stick around the center point. Magnitude is how far from the center point the stick location is:

  • 0.0 equals the stick being in the center resting position.
  • -1.0 indicates the stick is pushed all the way to the outer rail of the thumbstick movement circle (left for x dimension, bottom for y dimension).
  • 1.0 indicates the stick is pushed all the way to the outer rail of the thumbstick movement circle (right for x dimension, top for y dimension).

The resolution of each thumbstick is about 16 bits for each measure.

right_thumb_stick

The state of the right thumbstick if it changed at the time the device event was created. Thumbstick data is represented in normalized units between -1.0 and 1.0 for x direction, y direction and between 0.0 and 1.0 for magnitude of the thumbstick. x and y are the horizontal and vertical position of the stick around the center point. Magnitude is how far from the center point the stick location is:

  • 0.0 equals the stick being in the center resting position.
  • -1.0 indicates the stick is pushed all the way to the outer rail of the thumbstick movement circle (left for x dimension, bottom for y dimension).
  • 1.0 indicates the stick is pushed all the way to the outer rail of the thumbstick movement circle (right for x dimension, top for y dimension).

The resolution of each thumbstick is about 16 bits for each measure.

left_trigger

The state of the left trigger if it changed at the time the device event was created. A trigger is a pistol finger analog buttons on XInput gamepads. The position value is normalized to between 0.0 (not pressed) and 1.0 (fully depressed). The resolution of the trigger position is 8 bits.

right_trigger

The state of the right trigger if it changed at the time the device event was created. A trigger is a pistol finger analog buttons on XInput gamepads. The position value is normalized to between 0.0 (not pressed) and 1.0 (fully depressed). The resolution of the trigger position is 8 bits.

Notes and Considerations

..note:: If gamepad thumbstick position data is going to be used to control

the position of a stim object on the PsychoPy Window, the following equation can be used to convert the normalized thumbstick data to Display coordinates:

def normalizedValue2Coord(normalized_position,normalized_magnitude,display_coord_dim_size):
    x,y=normalized_position[0]*normalized_magnitude,normalized_position[1]*normalized_magnitude
    w,h=display_coord_dim_size
    return x*(w/2.0),y*(h/2.0)

# example usage:

display=io.devices.display
gamepad=io.devices.gamepad
keyboard=io.devices.keyboard

# create a PsychoPy stim to move with each thumb stick
#
# thumb_left_stim = .......
# thumb_right_stim = .......

dl,dt,dr,db=display.getCoordBounds()
coord_size=dr-dl,dt-db

io.clearEvents('all')

while not keyboard.getEvents():
    # thumb stick state is returned as a 3 item lists (x , y , magnitude)
    x,y,mag=gamepad.getThumbSticks()['right_stick']
    xx,yy=self.normalizedValue2Coord((x,y),mag,coord_size)
    thumb_right_stim.setPos((xx, yy))

    # thumb stick state is returned as a 3 item lists (x , y , magnitude)
    x,y,mag=gamepad.getThumbSticks()['left_stick'] # sticks are 3 item lists (x,y,magnitude)
    xx,yy=self.normalizedValue2Coord((x,y),mag,coord_size)
    thumb_left_stim.setPos((xx, yy))

    thumb_right_stim.draw()
    thumb_left_stim.draw()

    io.clearEvents('all')

    window.flip()
  • Ensure that XInput version 1.3 is installed on your computer.
  • If using a wireless gamepad, ensure the gamepad has been powered on befor stating the experiment.
  • For the supported Logitech gamepads, be sure that the switch on the gamepad is set to the ‘X’ position, indicating that the gamepad will use the XInput protocal.