XInput - Xbox Controller API

Post your working scripts, libraries and tools.
lexikos
Posts: 9688
Joined: 30 Sep 2013, 04:07
Contact:

XInput - Xbox Controller API

10 Jul 2022, 02:19

XInput

This is a barely-tested direct port of (a slightly modified* version of) XInput.ahk for v1, with only minor changes to run on v2.

Note: As ErrorLevel was removed, XInput_GetState and XInput_GetCapabilities now throw an OSError for any error other than "controller not connected", and XInput_SetState throws an OSError for that as well.

*This version includes minor changes to support retrieving the state of the guide button.


XInput.ahk wraps a few key functions of XInput, a.k.a. the Microsoft Common Controller API. It provides the following capabilities not otherwise accessible to scripts:
  • The official drivers report to Windows one axis for both analog triggers, making it impossible to detect when both triggers are pulled at once. XInput allows one to retrieve the state of each analog trigger individually.
  • Set level of controller vibration - left and right motor speeds.
Other capabilities of the XInput API that this script does not currently support include:
  • Detect input on attached messenger kits (mini keyboards which can be attached to a controller).
  • Retrieve battery levels of wireless controllers.
  • Access the audio input/output of a given controller's headset.
Required: xinput1_*.dll - You probably already have it.

Usage notes are written above each function.

Code: Select all

/*  XInput by Lexikos
 */

#Requires AutoHotkey v2.0-beta.6

/*
    Function: XInput_Init
    
    Initializes XInput.ahk with the given XInput DLL.
    
    Parameters:
        dll     -   The path or name of the XInput DLL to load.
*/
XInput_Init(dll:="")
{
    global
    if _XInput_hm ?? 0
        return
    
    ;======== CONSTANTS DEFINED IN XINPUT.H ========
    
    ; Device types available in XINPUT_CAPABILITIES
    XINPUT_DEVTYPE_GAMEPAD          := 0x01

    ; Device subtypes available in XINPUT_CAPABILITIES
    XINPUT_DEVSUBTYPE_GAMEPAD       := 0x01

    ; Flags for XINPUT_CAPABILITIES
    XINPUT_CAPS_VOICE_SUPPORTED     := 0x0004

    ; Constants for gamepad buttons
    XINPUT_GAMEPAD_DPAD_UP          := 0x0001
    XINPUT_GAMEPAD_DPAD_DOWN        := 0x0002
    XINPUT_GAMEPAD_DPAD_LEFT        := 0x0004
    XINPUT_GAMEPAD_DPAD_RIGHT       := 0x0008
    XINPUT_GAMEPAD_START            := 0x0010
    XINPUT_GAMEPAD_BACK             := 0x0020
    XINPUT_GAMEPAD_LEFT_THUMB       := 0x0040
    XINPUT_GAMEPAD_RIGHT_THUMB      := 0x0080
    XINPUT_GAMEPAD_LEFT_SHOULDER    := 0x0100
    XINPUT_GAMEPAD_RIGHT_SHOULDER   := 0x0200
    XINPUT_GAMEPAD_GUIDE            := 0x0400
    XINPUT_GAMEPAD_A                := 0x1000
    XINPUT_GAMEPAD_B                := 0x2000
    XINPUT_GAMEPAD_X                := 0x4000
    XINPUT_GAMEPAD_Y                := 0x8000

    ; Gamepad thresholds
    XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE  := 7849
    XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE := 8689
    XINPUT_GAMEPAD_TRIGGER_THRESHOLD    := 30

    ; Flags to pass to XInputGetCapabilities
    XINPUT_FLAG_GAMEPAD             := 0x00000001
    
    ;=============== END CONSTANTS =================
    
    if (dll = "")
        Loop Files A_WinDir "\System32\XInput1_*.dll"
            dll := A_LoopFileName
    if (dll = "")
        dll := "XInput1_3.dll"
    
    _XInput_hm := DllCall("LoadLibrary" ,"str",dll ,"ptr")
    
    if !_XInput_hm
        throw Error("Failed to initialize XInput: " dll " not found.")
    
   (_XInput_GetState        := DllCall("GetProcAddress" ,"ptr",_XInput_hm ,"ptr",100 ,"ptr"))
|| (_XInput_GetState        := DllCall("GetProcAddress" ,"ptr",_XInput_hm ,"astr","XInputGetState" ,"ptr"))
    _XInput_SetState        := DllCall("GetProcAddress" ,"ptr",_XInput_hm ,"astr","XInputSetState" ,"ptr")
    _XInput_GetCapabilities := DllCall("GetProcAddress" ,"ptr",_XInput_hm ,"astr","XInputGetCapabilities" ,"ptr")
    
    if !(_XInput_GetState && _XInput_SetState && _XInput_GetCapabilities) {
        XInput_Term()
        throw Error("Failed to initialize XInput: function not found.")
    }
}

/*
    Function: XInput_GetState
    
    Retrieves the current state of the specified controller.

    Parameters:
        UserIndex   -   [in] Index of the user's controller. Can be a value from 0 to 3.
    
    Returns:
        An object with with properties equivalent to merging XINPUT_STATE and XINPUT_GAMEPAD.
        https://docs.microsoft.com/en-us/windows/win32/api/xinput/ns-xinput-xinput_state
    
    Error handling:
        Returns false if the controller is disconnected.
        Throws an OSError for other errors.
*/
XInput_GetState(UserIndex)
{
    global _XInput_GetState
    
    xiState := Buffer(16)

    if err := DllCall(_XInput_GetState ,"uint",UserIndex ,"ptr",xiState) {
        if err = 1167 ; ERROR_DEVICE_NOT_CONNECTED
            return 0
        throw OSError(err, -1)
    }
    
    return {
        dwPacketNumber: NumGet(xiState,  0, "UInt"),
        wButtons:       NumGet(xiState,  4, "UShort"),
        bLeftTrigger:   NumGet(xiState,  6, "UChar"),
        bRightTrigger:  NumGet(xiState,  7, "UChar"),
        sThumbLX:       NumGet(xiState,  8, "Short"),
        sThumbLY:       NumGet(xiState, 10, "Short"),
        sThumbRX:       NumGet(xiState, 12, "Short"),
        sThumbRY:       NumGet(xiState, 14, "Short"),
    }
}

/*
    Function: XInput_SetState
    
    Sends data to a connected controller. This function is used to activate the vibration
    function of a controller.
    
    Parameters:
        UserIndex       -   [in] Index of the user's controller. Can be a value from 0 to 3.
        LeftMotorSpeed  -   [in] Speed of the left motor, between 0 and 65535.
        RightMotorSpeed -   [in] Speed of the right motor, between 0 and 65535.
    
    Error handling:
        Throws an OSError on failure, such as if the controller is not connected.
    
    Remarks:
        The left motor is the low-frequency rumble motor. The right motor is the
        high-frequency rumble motor. The two motors are not the same, and they create
        different vibration effects.
*/
XInput_SetState(UserIndex, LeftMotorSpeed, RightMotorSpeed)
{
    global _XInput_SetState
    if err := DllCall(_XInput_SetState ,"uint",UserIndex ,"uint*",LeftMotorSpeed|RightMotorSpeed<<16)
        throw OSError(err, -1)
}

/*
    Function: XInput_GetCapabilities
    
    Retrieves the capabilities and features of a connected controller.
    
    Parameters:
        UserIndex   -   [in] Index of the user's controller. Can be a value in the range 0–3.
        Flags       -   [in] Input flags that identify the controller type.
                                0   - All controllers.
                                1   - XINPUT_FLAG_GAMEPAD: Xbox 360 Controllers only.
    
    Returns:
        An object with properties equivalent to XINPUT_CAPABILITIES.
        https://docs.microsoft.com/en-au/windows/win32/api/xinput/ns-xinput-xinput_capabilities
    
    Error handling:
        Returns false if the controller is disconnected.
        Throws an OSError for other errors.
*/
XInput_GetCapabilities(UserIndex, Flags)
{
    global _XInput_GetCapabilities
    
    xiCaps := Buffer(20)
    
    if err := DllCall(_XInput_GetCapabilities ,"uint",UserIndex ,"uint",Flags ,"ptr",xiCaps) {
        if err = 1167 ; ERROR_DEVICE_NOT_CONNECTED
            return 0
        throw OSError(err, -1)
    }
    
    return {
        Type:                   NumGet(xiCaps,  0, "UChar"),
        SubType:                NumGet(xiCaps,  1, "UChar"),
        Flags:                  NumGet(xiCaps,  2, "UShort"),
        Gamepad: {
            wButtons:           NumGet(xiCaps,  4, "UShort"),
            bLeftTrigger:       NumGet(xiCaps,  6, "UChar"),
            bRightTrigger:      NumGet(xiCaps,  7, "UChar"),
            sThumbLX:           NumGet(xiCaps,  8, "Short"),
            sThumbLY:           NumGet(xiCaps, 10, "Short"),
            sThumbRX:           NumGet(xiCaps, 12, "Short"),
            sThumbRY:           NumGet(xiCaps, 14, "Short")
        },
        Vibration: {
            wLeftMotorSpeed:    NumGet(xiCaps, 16, "UShort"),
            wRightMotorSpeed:   NumGet(xiCaps, 18, "UShort")
        }
    }
}

/*
    Function: XInput_Term
    Unloads the previously loaded XInput DLL.
*/
XInput_Term() {
    global
    if _XInput_hm
        DllCall("FreeLibrary","uint",_XInput_hm), _XInput_hm :=_XInput_GetState :=_XInput_SetState :=_XInput_GetCapabilities :=0
}

; TODO: XInputEnable, 'GetBatteryInformation and 'GetKeystroke.

Code: Select all

; Example: Control the vibration motors using the analog triggers of each controller.
#Include <XInput>
XInput_Init()
Loop {
    Loop 4 {
        if State := XInput_GetState(A_Index-1) {
            LT := State.bLeftTrigger
            RT := State.bRightTrigger
            XInput_SetState(A_Index-1, LT*257, RT*257)
        }
    }
    Sleep 100
}
This example can execute on either AutoHotkey v1 or v2.
lexikos
Posts: 9688
Joined: 30 Sep 2013, 04:07
Contact:

Re: XInput - Xbox Controller API

10 Jul 2022, 02:31

If you just want to get the state of a controller, you may use this simplified version (but you'll need to look up the right button numbers for wButtons):

Code: Select all

#Requires AutoHotkey v2.0-beta.6

Loop {
    if State := XInputState(0) {
        LT := State.bLeftTrigger
        RT := State.bRightTrigger
        ToolTip LT "," RT
    }
    Sleep 100
}

#DllLoad XInput1_4.dll
XInputState(UserIndex) {
    xiState := Buffer(16)
    if err := DllCall("XInput1_4\XInputGetState", "uint", UserIndex, "ptr", xiState) {
        if err = 1167 ; ERROR_DEVICE_NOT_CONNECTED
            return 0
        throw OSError(err, -1)
    }
    return {
        dwPacketNumber: NumGet(xiState,  0, "UInt"),
        wButtons:       NumGet(xiState,  4, "UShort"),
        bLeftTrigger:   NumGet(xiState,  6, "UChar"),
        bRightTrigger:  NumGet(xiState,  7, "UChar"),
        sThumbLX:       NumGet(xiState,  8, "Short"),
        sThumbLY:       NumGet(xiState, 10, "Short"),
        sThumbRX:       NumGet(xiState, 12, "Short"),
        sThumbRY:       NumGet(xiState, 14, "Short"),
    }
}
XInput1_4.dll is included with Windows 10 and later. For earlier operating systems, you may need to replace 1_4 with 1_3. Hard-coding the dll name allows the script to take advantage of AutoHotkey's built-in optimizations.
neogna2
Posts: 600
Joined: 15 Sep 2016, 15:44

Re: XInput - Xbox Controller API

15 Jan 2023, 14:27

This works for me in Windows 10 with both old Xbox360 controllers and newer Xbox controllers. Thank you Lexikos.

To get the Guide button state requires the library and its _XInput_GetState step.
In contrast we cannot read Guide state by just adding State.wButtons & 0x0400 to the simplified version script above.

@lexikos I'd like to learn more about this part which enables Guide button state reading
(_XInput_GetState := DllCall("GetProcAddress" ,"ptr",_XInput_hm ,"ptr",100 ,"ptr"))
|| (_XInput_GetState := DllCall("GetProcAddress" ,"ptr",_XInput_hm ,"astr","XInputGetState" ,"ptr"))


What's that 100 parameter? Is it documented somewhere?

Do you think there is a way to also get the Share button state?
This StackOverflow answer says xinput API does not support the Share button. But the answer links this XINPUT_STATE doc page as evidence and that doc doesn't mention XINPUT_GAMEPAD_GUIDE either, but that's still readable, so I don't know how certain that answer is.
lexikos
Posts: 9688
Joined: 30 Sep 2013, 04:07
Contact:

Re: XInput - Xbox Controller API

15 Jan 2023, 22:46

100 is the original number of a function that retrieves the state including the guide button. As far as I am aware it is undocumented, and retrieving the state of the guide button is not officially supported.

I know nothing about the share button.
maffle
Posts: 4
Joined: 12 Mar 2016, 08:07

Re: XInput - Xbox Controller API

28 Aug 2023, 00:40

Why is there not a simple example code how to use this and bind keyboard keys to one of the buttons for example? Also it seems the xbox key and the share key of s/x controllers are missing. can someone please give a short example how you would bind a keyboard key to xbox and share buttons and leave the other buttons as they are in a game?
User avatar
boiler
Posts: 17356
Joined: 21 Dec 2014, 02:44

Re: XInput - Xbox Controller API

28 Aug 2023, 03:07

maffle wrote: Why is there not a simple example code how to use this and bind keyboard keys to one of the buttons for example?
Because it doesn’t do that. Your script needs to continuously check to see if a button is pressed like you would using GetKeyState. The example in the second post shows how you check the state of the triggers. You would do the same for other buttons.
neogna2
Posts: 600
Joined: 15 Sep 2016, 15:44

Re: XInput - Xbox Controller API

28 Aug 2023, 05:34

maffle wrote:
28 Aug 2023, 00:40
it seems the xbox key and the share key of s/x controllers are missing.
In addition to what boiler wrote above: The xbox button is named guide button ( XINPUT_GAMEPAD_GUIDE ) in xinput. There is no known way to use xinput to detect share button presses.
maffle
Posts: 4
Joined: 12 Mar 2016, 08:07

Re: XInput - Xbox Controller API

06 Sep 2023, 11:24

@neogna2 How do other tools detect it though? For example reWASD can detect the share button press and you can rebind other buttons or commands on it. I am looking for a free alternative since some time. I wish someone would make a simple little AHK script where you could just rebind all Xbox controller buttons. All I want is to to bind a keyboard command, for example alt+f5 to the share button.

A better thing would be to have some OSD you could bind to xbox button for example, and then you could select with the xbox controller through that OSD and select things like "send button x, send button y".
User avatar
MrDodel
Posts: 96
Joined: 28 Apr 2021, 09:03
Location: Event Horizon

Re: XInput - Xbox Controller API

07 Sep 2023, 04:14

maffle wrote:
06 Sep 2023, 11:24
How do other tools detect it though? For example reWASD
reWASD uses its own kernel drivers written by the reWASD devs, hence why it also costs money to buy.
So much universe, and so little time. GNU Sir Terry.

Return to “Scripts and Functions (v2)”

Who is online

Users browsing this forum: No registered users and 28 guests