The methods are Xinput and PureBasic (DirectInput). The DirectInput method has two different types supported.
The mappings are based on my Speedlink STRIKE FX Gamepad (Model SL-6537-BK) which is a Xinput gamepad with a mode switch to make it a DirectInput gamepad (and the ability to toggle digital/analog in that mode). I do not have other gamepads to test with sadly.
The three examples uses a XINPUT_NORMALIZE structure that present the gamepad state the same way with all three methods, thus one should easily be able to let the user choose which mapping/gamepad mode they wish to use.
The structure is normalized to the floating point range -1.0 to +1.0 with 0.0 being center. In cases like Left and Right Trigger the value range is 0.0 to 1.0, but in the case of a DirectInput (Type 1) the value is either 0.0 or 1.0 (no range) as they are considered digital buttons.
Other buttons are digital only and a button mask is used for those, a handy macro makes it easy to get the state of a button.
The Xinput code also has basic rumble/forcefeedback.
Do note that the DirectInput (PureBasic native) mapping is based on the physical layout of the gamepad controller, and depending on if this is a DirectInput gamepad or a Xinput gamepad the Type1 and Type2 methods vary widely (reversed, rotated or mapped rather weird).
It is possible that other gamepads out there would require another type of mapping, if that is the case then please post those in this thread and call them Type3 and Type4 etc.
Hopefully Type1 and Type2 are the two most common ones.
Please note that the test gamepad was properly calibrated in Windows first, and that this was on Windows 7 and it seems like Windows might have applied some calibration adjustments (the gamepad had what appeared to be 0 deadzones so that may have helped as well).
No special gamepad drivers/software was installed, only whatever drives Windows found on it's own.
Also note that a gamepad may not be able to run the low and high motors at very low speeds, the low frequency motor and high frequency motors may have different minimum values as well.
No XInput caps are applied for two reasons, for one not all gamepads provide them thus the hardcoded defaults are used instead, secondly on the test gamepad I found the thumb stick range to not really match (a larger range of values was returned than the caps info stated there should be), this could be a calibration vs uncalibrated difference, but I don't think that is the issue.
For the DirectInput Type1 and Type2 methods there is also a small "hack" I added to handle a centering issue, the issue could be just this gamepad model but I seem to recall the same issue on a previous gamepad I had a long time ago. The hack is mostly harmless though and the user/gamer will never notice it. It is only the Z axis (aka throttle) that behaves this way. The range is -32768 to +32767 and normally 0 is center, but in this case both 1 and 0 are center, but DirectInput reports it as +1 instead, so if your ship or car seems to drive or drift on it's own in some games, then this may be the reason.
Tip! Allow the user/gamer to set and adjust deadzone. Remember that a stick for example has a center deadzone and a edge deadzone and they should be possible to set independently (ideally).
Code: Select all
;xinput.pb
;(c) 2014 Roger Hågensen
;zlib License, http://en.wikipedia.org/wiki/Zlib_License
;This code is what you would use if the user selects "Xinput" controller mapping in your software.
;References
;http://msdn.microsoft.com/en-us/library/windows/desktop/hh405053.aspx
EnableExplicit
Prototype XInputGetState(dwUserIndex.l, *pState)
Prototype XInputSetState(dwUserIndex.l, *pVibration)
#XINPUT_GAMEPAD_DPAD_UP = $0001
#XINPUT_GAMEPAD_DPAD_DOWN = $0002
#XINPUT_GAMEPAD_DPAD_LEFT = $0004
#XINPUT_GAMEPAD_DPAD_RIGHT = $0008
#XINPUT_GAMEPAD_START = $0010
#XINPUT_GAMEPAD_BACK = $0020
#XINPUT_GAMEPAD_LEFT_THUMB = $0040
#XINPUT_GAMEPAD_RIGHT_THUMB = $0080
#XINPUT_GAMEPAD_LEFT_SHOULDER = $0100
#XINPUT_GAMEPAD_RIGHT_SHOULDER = $0200
#XINPUT_GAMEPAD_A = $1000
#XINPUT_GAMEPAD_B = $2000
#XINPUT_GAMEPAD_X = $4000
#XINPUT_GAMEPAD_Y = $8000
Structure XINPUT_GAMEPAD
wButtons.w ;See #XINPUT_GAMEPAD_ masks.
bLeftTrigger.a ;A value of 0 to 255.
bRightTrigger.a ;A value of 0 to 255.
sThumbLX.w ;Left thumbstick x-axis value, -32768 to +32767, 0 is centered.
sThumbLY.w ;Left thumbstick y-axis value, -32768 to +32767, 0 is centered.
sThumbRX.w ;Right thumbstick x-axis value, -32768 to +32767, 0 is centered.
sThumbRY.w ;Right thumbstick y-axis value, -32768 to +32767, 0 is centered.
EndStructure
Structure XINPUT_STATE
dwPacketNumber.l
Gamepad.XINPUT_GAMEPAD
EndStructure
Structure XINPUT_VIBRATION
wLeftMotorSpeed.w ;Low frequency rumble, 0 to 65535, 0 is off and 65535 is 100% speed.
wRightMotorSpeed.w ;High frequency rumble, 0 to 65535, 0 is off and 65535 is 100% speed.
EndStructure
Structure XINPUT_NORMALIZED
buttons.l ;See #XINPUT_GAMEPAD_ masks.
triggerl.f ;0.0 to 1.0
triggerr.f ;0.0 to 1.0
thumblx.f ;Left thumbstick x-axis value, -1.0 to +1.0, 0.0 is centered.
thumbly.f ;Left thumbstick y-axis value, -1.0 to +1.0, 0.0 is centered.
thumbrx.f ;Right thumbstick x-axis value, -1.0 to +1.0, 0.0 is centered.
thumbry.f ;Right thumbstick y-axis value, -1.0 to +1.0, 0.0 is centered.
EndStructure
Structure XINPUT_XINPUT
dll.i
version.i
*getstate.XInputGetState
*setstate.XInputSetState
state.XINPUT_STATE[4]
normalized.XINPUT_NORMALIZED[4]
EndStructure
Macro Xinput_Button(buttons, mask)
Bool(buttons & mask)
EndMacro
Procedure.i Xinput_Init()
Protected *xinput.XINPUT_XINPUT
*xinput = AllocateMemory(SizeOf(XINPUT_XINPUT))
If *xinput = #False
ProcedureReturn #Null
EndIf
*xinput\dll = OpenLibrary(#PB_Any, "xinput1_4.dll") ;Windows 8+
*xinput\version = 14
If *xinput\dll = #False
*xinput\dll = OpenLibrary(#PB_Any, "xinput1_3.dll") ;DirectX SDK
*xinput\version = 13
EndIf
If *xinput\dll = #False
*xinput\dll = OpenLibrary(#PB_Any, "xinput1_2.dll") ;DirectX SDK
*xinput\version = 12
EndIf
If *xinput\dll = #False
*xinput\dll = OpenLibrary(#PB_Any, "xinput1_1.dll") ;DirectX SDK
*xinput\version = 11
EndIf
If *xinput\dll = #False
*xinput\dll = OpenLibrary(#PB_Any, "xinput9_1_0.dll") ;Windows Vista/7/8
*xinput\version = 10
EndIf
If *xinput\dll = #False
*xinput\version = 0
EndIf
*xinput\getstate = GetFunction(*xinput\dll, "XInputGetState")
If *xinput\getstate = #Null
*xinput\version = 0
EndIf
*xinput\setstate = GetFunction(*xinput\dll, "XInputSetState")
If *xinput\setstate = #Null
*xinput\version = 0
EndIf
If (*xinput\version = 0) And (*xinput\dll <> #Null)
CloseLibrary(*xinput\dll)
EndIf
If *xinput\version = 0
FreeMemory(*xinput)
*xinput = #Null
EndIf
ProcedureReturn *xinput
EndProcedure
Procedure.i Xinput_Free(*xinput.XINPUT_XINPUT)
If *xinput = #Null
ProcedureReturn #Null
EndIf
If *xinput\dll <> #Null
If IsLibrary(*xinput\dll)
CloseLibrary(*xinput\dll)
EndIf
*xinput\dll = #Null
EndIf
FreeMemory(*xinput)
ProcedureReturn #Null
EndProcedure
Procedure.i Xinput_Version(*xinput.XINPUT_XINPUT)
ProcedureReturn *xinput\version
EndProcedure
#ERROR_DEVICE_NOT_CONNECTED = 1167
Procedure.i Xinput_Get(*xinput.XINPUT_XINPUT, index.l)
Protected error.i
If (index < 0) Or (index > 3)
ProcedureReturn #Null
EndIf
ZeroMemory_(*xinput\state[index], SizeOf(XINPUT_STATE))
ZeroMemory_(*xinput\normalized[index], SizeOf(XINPUT_NORMALIZED))
error = *xinput\getstate(index, *xinput\state[index])
If error <> #ERROR_SUCCESS
ProcedureReturn #Null
EndIf
*xinput\normalized[index]\buttons = *xinput\state[index]\Gamepad\wButtons
*xinput\normalized[index]\triggerl = *xinput\state[index]\Gamepad\bLeftTrigger / 255.0
*xinput\normalized[index]\triggerr = *xinput\state[index]\Gamepad\bRightTrigger / 255.0
If *xinput\state[index]\Gamepad\sThumbLX < 0
*xinput\normalized[index]\thumblx = *xinput\state[index]\Gamepad\sThumbLX / 32768.0
Else
*xinput\normalized[index]\thumblx = *xinput\state[index]\Gamepad\sThumbLX / 32767.0
EndIf
If *xinput\state[index]\Gamepad\sThumbLY < 0
*xinput\normalized[index]\thumbly = *xinput\state[index]\Gamepad\sThumbLY / 32768.0
Else
*xinput\normalized[index]\thumbly = *xinput\state[index]\Gamepad\sThumbLY / 32767.0
EndIf
If *xinput\state[index]\Gamepad\sThumbRX < 0
*xinput\normalized[index]\thumbrx = *xinput\state[index]\Gamepad\sThumbRX / 32768.0
Else
*xinput\normalized[index]\thumbrx = *xinput\state[index]\Gamepad\sThumbRX / 32767.0
EndIf
If *xinput\state[index]\Gamepad\sThumbRY < 0
*xinput\normalized[index]\thumbry = *xinput\state[index]\Gamepad\sThumbRY / 32768.0
Else
*xinput\normalized[index]\thumbry = *xinput\state[index]\Gamepad\sThumbRY / 32767.0
EndIf
ProcedureReturn *xinput\normalized[index]
EndProcedure
Procedure.i Xinput_Vibrate(*xinput.XINPUT_XINPUT, index.l, low.f, high.f)
Protected error.i, motor.XINPUT_VIBRATION
If (index < 0) Or (index > 3)
ProcedureReturn #Null
EndIf
motor\wLeftMotorSpeed = low * 65535.0
motor\wRightMotorSpeed = high * 65535.0
error = *xinput\setstate(index, motor)
If error <> #ERROR_SUCCESS
ProcedureReturn #Null
EndIf
ProcedureReturn #ERROR_SUCCESS
EndProcedure
;************************************************************************
;Example
Define *xinput, *state.XINPUT_NORMALIZED
If OpenConsole("Xinput Test") = #False
End
EndIf
EnableGraphicalConsole(#True)
*xinput = Xinput_Init()
If *xinput = #Null
PrintN("Xinput_Init() failed.")
End
EndIf
;Note that with DirectInput the Left and Right triggers are considered being on the same (Z) axis, with Xinput they are not.
; If a Xinput gamepad is connected,
; the expected results with Xinput_Get() are...
; Button A: 0 or 1
; Button B: 0 or 1
; Button X: 0 or 1
; Button Y: 0 or 1
; Button Left Shoulder: 0 or 1
; Button Right Shoulder: 0 or 1
; Button Back: 0 or 1
; Button Start: 0 or 1
; Button Left Thumb: 0 or 1
; Button Right Thumb: 0 or 1
; Button D-Pad Left: 0 or 1
; Button D-Pad Up: 0 or 1
; Button D-Pad Right: 0 or 1
; Button D-Pad Down: 0 or 1
; Left Trigger: 0.0 to 1.0
; Right Trigger: 0.0 to 1.0
; Left Stick X: -1.0 (left) to 0.0 (center) to 1.0 (right)
; Left Stick Y: -1.0 (down) to 0.0 (center) to 1.0 (up)
; Right Stick X: -1.0 (left) to 0.0 (center) to 1.0 (right)
; Right Stick Y: -1.0 (down) to 0.0 (center) to 1.0 (up)
Define timeold.l, timenew.l, vibrate.i
timenew = ElapsedMilliseconds()
timeold = timenew
Repeat
timenew = ElapsedMilliseconds()
*state = Xinput_Get(*xinput, 0)
If *state
ClearConsole()
PrintN("Button A: " + Str(Xinput_Button(*state\buttons, #XINPUT_GAMEPAD_A)))
PrintN("Button B: " + Str(Xinput_Button(*state\buttons, #XINPUT_GAMEPAD_B)))
PrintN("Button X: " + Str(Xinput_Button(*state\buttons, #XINPUT_GAMEPAD_X)))
PrintN("Button Y: " + Str(Xinput_Button(*state\buttons, #XINPUT_GAMEPAD_Y)))
PrintN("Button Left Shoulder: " + Str(Xinput_Button(*state\buttons, #XINPUT_GAMEPAD_LEFT_SHOULDER)))
PrintN("Button Right Shoulder: " + Str(Xinput_Button(*state\buttons, #XINPUT_GAMEPAD_RIGHT_SHOULDER)))
PrintN("Button Back: " + Str(Xinput_Button(*state\buttons, #XINPUT_GAMEPAD_BACK)))
PrintN("Button Start: " + Str(Xinput_Button(*state\buttons, #XINPUT_GAMEPAD_START)))
PrintN("Button D-Pad Left: " + Str(Xinput_Button(*state\buttons, #XINPUT_GAMEPAD_DPAD_LEFT)))
PrintN("Button D-Pad Up: " + Str(Xinput_Button(*state\buttons, #XINPUT_GAMEPAD_DPAD_UP)))
PrintN("Button D-Pad Right: " + Str(Xinput_Button(*state\buttons, #XINPUT_GAMEPAD_DPAD_RIGHT)))
PrintN("Button D-Pad Down: " + Str(Xinput_Button(*state\buttons, #XINPUT_GAMEPAD_DPAD_DOWN)))
PrintN("Button Left Thumb: " + Str(Xinput_Button(*state\buttons, #XINPUT_GAMEPAD_LEFT_THUMB)))
PrintN("Button Right Thumb: " + Str(Xinput_Button(*state\buttons, #XINPUT_GAMEPAD_RIGHT_THUMB)))
PrintN("Left Trigger: " + StrF(*state\triggerl, 7) )
PrintN("Right Trigger: " + StrF(*state\triggerr, 7))
PrintN("Left Stick X: " + StrF(*state\thumblx, 7))
PrintN("Left Stick Y: " + StrF(*state\thumbly, 7))
PrintN("Right Stick X: " + StrF(*state\thumbrx, 7))
PrintN("Right Stick Y: " + StrF(*state\thumbry, 7))
PrintN("")
PrintN("Press enter to stop.")
Else
PrintN("Error")
Break
EndIf
If (timenew - timeold) >= 2000
timeold = timenew
If vibrate = 1
Xinput_Vibrate(*xinput, 0, 0.0, 0.025)
vibrate = 0
Else
Xinput_Vibrate(*xinput, 0, 0.075, 0.0)
vibrate = 1
EndIf
EndIf
Delay(100)
Until InKey() <> ""
Xinput_Vibrate(*xinput, 0, 0.0, 0.0)
PrintN("")
PrintN("Xinput DLL Version: " + StrD(Xinput_Version(*xinput) * 0.1, 1))
PrintN("Press enter to quit.")
Input()
*xinput = Xinput_Free(*xinput)
CloseConsole()
End
Code: Select all
;pbinput1.pb
;(c) 2014 Roger Hågensen
;zlib License, http://en.wikipedia.org/wiki/Zlib_License
;This code is what you would use if the user selects "Type 1" controller mapping in your software.
EnableExplicit
#XINPUT_GAMEPAD_DPAD_UP = $0001
#XINPUT_GAMEPAD_DPAD_DOWN = $0002
#XINPUT_GAMEPAD_DPAD_LEFT = $0004
#XINPUT_GAMEPAD_DPAD_RIGHT = $0008
#XINPUT_GAMEPAD_START = $0010
#XINPUT_GAMEPAD_BACK = $0020
#XINPUT_GAMEPAD_LEFT_THUMB = $0040
#XINPUT_GAMEPAD_RIGHT_THUMB = $0080
#XINPUT_GAMEPAD_LEFT_SHOULDER = $0100
#XINPUT_GAMEPAD_RIGHT_SHOULDER = $0200
#XINPUT_GAMEPAD_A = $1000
#XINPUT_GAMEPAD_B = $2000
#XINPUT_GAMEPAD_X = $4000
#XINPUT_GAMEPAD_Y = $8000
Structure XINPUT_NORMALIZED
buttons.l ;See #XINPUT_GAMEPAD_ masks.
triggerl.f ;0.0 to 1.0
triggerr.f ;0.0 to 1.0
thumblx.f ;Left thumbstick x-axis value, -1.0 to +1.0, 0.0 is centered.
thumbly.f ;Left thumbstick y-axis value, -1.0 to +1.0, 0.0 is centered.
thumbrx.f ;Right thumbstick x-axis value, -1.0 to +1.0, 0.0 is centered.
thumbry.f ;Right thumbstick y-axis value, -1.0 to +1.0, 0.0 is centered.
EndStructure
Structure PBINPUT_PBINPUT
dll.i
version.i
joysticks.i
normalized.XINPUT_NORMALIZED
EndStructure
Macro Xinput_Button(buttons, mask)
Bool(buttons & mask)
EndMacro
Procedure.i PBinput_Init()
Protected *pbinput.PBINPUT_PBINPUT
*pbinput = AllocateMemory(SizeOf(PBINPUT_PBINPUT))
If *pbinput = #False
ProcedureReturn #Null
EndIf
*pbinput\joysticks = InitJoystick()
ProcedureReturn *pbinput
EndProcedure
Procedure.i PBinput_Free(*pbinput.PBINPUT_PBINPUT)
If *pbinput = #Null
ProcedureReturn #Null
EndIf
FreeMemory(*pbinput)
ProcedureReturn #Null
EndProcedure
;A centering issue seems to occur on some gamepads, possible cause is that the game pad reports a signed value but
;the center is both 0 and 1 or 0 and -1, instead of just 0, after calibration the 1 or -1 may be reversed,
;due to this the value 1 and -1 are treated as being 0.
;Ideally this fix should be a user option. The fix is possibly needed for all analog inputs.
;This center issue does not seem to happen with Xinput.
Procedure.i PBinput_GetType1(*pbinput.PBINPUT_PBINPUT, index.l) ;Old gamepad mapping (analog and digital).
Protected error.i, result.i, buttons.l
If (index < 0) Or (index >= *pbinput\joysticks)
ProcedureReturn #Null
EndIf
If ExamineJoystick(index) = #False
ProcedureReturn #Null
EndIf
ZeroMemory_(*pbinput\normalized, SizeOf(XINPUT_NORMALIZED))
If JoystickButton(index, 1)
buttons | #XINPUT_GAMEPAD_A
EndIf
If JoystickButton(index, 2)
buttons | #XINPUT_GAMEPAD_B
EndIf
If JoystickButton(index, 3)
buttons | #XINPUT_GAMEPAD_X
EndIf
If JoystickButton(index, 4)
buttons | #XINPUT_GAMEPAD_Y
EndIf
If JoystickButton(index, 5)
buttons | #XINPUT_GAMEPAD_LEFT_SHOULDER
EndIf
If JoystickButton(index, 6)
buttons | #XINPUT_GAMEPAD_RIGHT_SHOULDER
EndIf
If JoystickButton(index, 7) ;On the test gamepad this is Left Trigger.
;This is technically wrong, but no other logical way we can map this and still let it make sense to the user.
*pbinput\normalized\triggerl = 1.0
EndIf
If JoystickButton(index, 8) ;On the test gamepad this is Right Trigger.
;This is technically wrong, but no other logical way we can map this and still let it make sense to the user.
*pbinput\normalized\triggerr = 1.0
EndIf
If JoystickButton(index, 9)
buttons | #XINPUT_GAMEPAD_BACK
EndIf
If JoystickButton(index, 10)
buttons | #XINPUT_GAMEPAD_START
EndIf
If JoystickButton(index, 11)
buttons | #XINPUT_GAMEPAD_LEFT_THUMB
EndIf
If JoystickButton(index, 12)
buttons | #XINPUT_GAMEPAD_RIGHT_THUMB
EndIf
;The D-Pad is another weird mapping that is technically wrong, but this is the only way to map it so it makes sense to the user.
result = JoystickAxisX(index, 2, #PB_Relative)
If (result = 1) Or (result = -1) ;center fix
result = 0
EndIf
If result < 0
buttons | #XINPUT_GAMEPAD_DPAD_LEFT
EndIf
If result > 0
buttons | #XINPUT_GAMEPAD_DPAD_RIGHT
EndIf
result = JoystickAxisY(index, 2, #PB_Relative)
If (result = 1) Or (result = -1) ;center fix
result = 0
EndIf
If result > 0
buttons | #XINPUT_GAMEPAD_DPAD_UP
EndIf
If result < 0
buttons | #XINPUT_GAMEPAD_DPAD_DOWN
EndIf
*pbinput\normalized\buttons = buttons
result = JoystickAxisZ(index, 0, #PB_Relative) ;This is the horizontal of the right thumbstick.
If (result = 1) Or (result = -1) ;center fix
result = 0
EndIf
*pbinput\normalized\thumbrx = result * 0.001
result = JoystickAxisZ(index, 1, #PB_Relative) ;This is the vertical of the right thumbstick.
If (result = 1) Or (result = -1) ;center fix
result = 0
EndIf
*pbinput\normalized\thumbry = (-result) * 0.001
result = JoystickAxisX(index, 0, #PB_Relative)
If (result = 1) Or (result = -1) ;center fix
result = 0
EndIf
*pbinput\normalized\thumblx = result * 0.001
result = JoystickAxisY(index, 0, #PB_Relative)
If (result = 1) Or (result = -1) ;center fix
result = 0
EndIf
*pbinput\normalized\thumbly = (-result) * 0.001
ProcedureReturn *pbinput\normalized
EndProcedure
;************************************************************************
;Example
Define *pbinput, *state.XINPUT_NORMALIZED
If OpenConsole("PBinput1 Test") = #False
End
EndIf
EnableGraphicalConsole(#True)
*pbinput = PBinput_Init()
If *pbinput = #Null
PrintN("PBinput_Init() failed.")
End
EndIf
;Note that with DirectInput the Left and Right triggers are considered being on the same (Z) axis, with Xinput they are not.
; If a DirectInput gamepad is connected,
; Expected results with PBinput_GetType1() are...
; Button A: 0 or 1
; Button B: 0 or 1
; Button X: 0 or 1
; Button Y: 0 or 1
; Button Left Shoulder: 0 or 1
; Button Right Shoulder: 0 or 1
; Button Back: 0 or 1
; Button Start: 0 or 1
; Button Left Thumb: 0 or 1
; Button Right Thumb: 0 or 1
; Button D-Pad Left: 0 or 1
; Button D-Pad Up: 0 or 1
; Button D-Pad Right: 0 or 1
; Button D-Pad Down: 0 or 1
; Left Trigger: 0.0 or 1.0
; Right Trigger: 0.0 or 1.0
; Left Stick X: -1.0 (left) to 0.0 (center) to 1.0 (right)
; Left Stick Y: -1.0 (down) to 0.0 (center) to 1.0 (up)
; Right Stick X: -1.0 (left) to 0.0 (center) to 1.0 (right)
; Right Stick Y: -1.0 (down) to 0.0 (center) to 1.0 (up)
Repeat
*state = PBinput_GetType1(*pbinput, 0)
If *state
ClearConsole()
PrintN("Button A: " + Str(Xinput_Button(*state\buttons, #XINPUT_GAMEPAD_A)))
PrintN("Button B: " + Str(Xinput_Button(*state\buttons, #XINPUT_GAMEPAD_B)))
PrintN("Button X: " + Str(Xinput_Button(*state\buttons, #XINPUT_GAMEPAD_X)))
PrintN("Button Y: " + Str(Xinput_Button(*state\buttons, #XINPUT_GAMEPAD_Y)))
PrintN("Button Left Shoulder: " + Str(Xinput_Button(*state\buttons, #XINPUT_GAMEPAD_LEFT_SHOULDER)))
PrintN("Button Right Shoulder: " + Str(Xinput_Button(*state\buttons, #XINPUT_GAMEPAD_RIGHT_SHOULDER)))
PrintN("Button Back: " + Str(Xinput_Button(*state\buttons, #XINPUT_GAMEPAD_BACK)))
PrintN("Button Start: " + Str(Xinput_Button(*state\buttons, #XINPUT_GAMEPAD_START)))
PrintN("Button Left Thumb: " + Str(Xinput_Button(*state\buttons, #XINPUT_GAMEPAD_LEFT_THUMB)))
PrintN("Button Right Thumb: " + Str(Xinput_Button(*state\buttons, #XINPUT_GAMEPAD_RIGHT_THUMB)))
PrintN("Button D-Pad Left: " + Str(Xinput_Button(*state\buttons, #XINPUT_GAMEPAD_DPAD_LEFT)))
PrintN("Button D-Pad Up: " + Str(Xinput_Button(*state\buttons, #XINPUT_GAMEPAD_DPAD_UP)))
PrintN("Button D-Pad Right: " + Str(Xinput_Button(*state\buttons, #XINPUT_GAMEPAD_DPAD_RIGHT)))
PrintN("Button D-Pad Down: " + Str(Xinput_Button(*state\buttons, #XINPUT_GAMEPAD_DPAD_DOWN)))
PrintN("Left Trigger: " + StrF(*state\triggerl, 7) )
PrintN("Right Trigger: " + StrF(*state\triggerr, 7))
PrintN("Left Stick X: " + StrF(*state\thumblx, 7))
PrintN("Left Stick Y: " + StrF(*state\thumbly, 7))
PrintN("Right Stick X: " + StrF(*state\thumbrx, 7))
PrintN("Right Stick Y: " + StrF(*state\thumbry, 7))
PrintN("")
PrintN("Press enter to stop.")
Else
PrintN("Error")
Break
EndIf
Delay(100)
Until InKey() <> ""
PrintN("")
PrintN("Press enter to quit.")
Input()
*pbinput = PBinput_Free(*pbinput)
CloseConsole()
End
Code: Select all
;pbinput2.pb
;(c) 2014 Roger Hågensen
;zlib License, http://en.wikipedia.org/wiki/Zlib_License
;This code is what you would use if the user selects "Type 2" controller mapping in your software.
EnableExplicit
#XINPUT_GAMEPAD_DPAD_UP = $0001
#XINPUT_GAMEPAD_DPAD_DOWN = $0002
#XINPUT_GAMEPAD_DPAD_LEFT = $0004
#XINPUT_GAMEPAD_DPAD_RIGHT = $0008
#XINPUT_GAMEPAD_START = $0010
#XINPUT_GAMEPAD_BACK = $0020
#XINPUT_GAMEPAD_LEFT_THUMB = $0040
#XINPUT_GAMEPAD_RIGHT_THUMB = $0080
#XINPUT_GAMEPAD_LEFT_SHOULDER = $0100
#XINPUT_GAMEPAD_RIGHT_SHOULDER = $0200
#XINPUT_GAMEPAD_A = $1000
#XINPUT_GAMEPAD_B = $2000
#XINPUT_GAMEPAD_X = $4000
#XINPUT_GAMEPAD_Y = $8000
Structure XINPUT_NORMALIZED
buttons.l ;See #XINPUT_GAMEPAD_ masks.
triggerl.f ;0.0 to 1.0
triggerr.f ;0.0 to 1.0
thumblx.f ;Left thumbstick x-axis value, -1.0 to +1.0, 0.0 is centered.
thumbly.f ;Left thumbstick y-axis value, -1.0 to +1.0, 0.0 is centered.
thumbrx.f ;Right thumbstick x-axis value, -1.0 to +1.0, 0.0 is centered.
thumbry.f ;Right thumbstick y-axis value, -1.0 to +1.0, 0.0 is centered.
EndStructure
Structure PBINPUT_PBINPUT
dll.i
version.i
joysticks.i
normalized.XINPUT_NORMALIZED
EndStructure
Macro Xinput_Button(buttons, mask)
Bool(buttons & mask)
EndMacro
Procedure.i PBinput_Init()
Protected *pbinput.PBINPUT_PBINPUT
*pbinput = AllocateMemory(SizeOf(PBINPUT_PBINPUT))
If *pbinput = #False
ProcedureReturn #Null
EndIf
*pbinput\joysticks = InitJoystick()
ProcedureReturn *pbinput
EndProcedure
Procedure.i PBinput_Free(*pbinput.PBINPUT_PBINPUT)
If *pbinput = #Null
ProcedureReturn #Null
EndIf
FreeMemory(*pbinput)
ProcedureReturn #Null
EndProcedure
;A centering issue seems to occur on some gamepads, possible cause is that the game pad reports a signed value but
;the center is both 0 and 1 or 0 and -1, instead of just 0, after calibration the 1 or -1 may be reversed,
;due to this the value 1 and -1 are treated as being 0.
;Ideally this fix should be a user option. The fix is possibly needed for all analog inputs.
;This center issue does not seem to happen with Xinput.
Procedure.i PBinput_GetType2(*pbinput.PBINPUT_PBINPUT, index.l) ;Xinput compatible gamepad controller layout/mapping.
Protected error.i, result.i, buttons.l
If (index < 0) Or (index >= *pbinput\joysticks)
ProcedureReturn #Null
EndIf
If ExamineJoystick(index) = #False
ProcedureReturn #Null
EndIf
ZeroMemory_(*pbinput\normalized, SizeOf(XINPUT_NORMALIZED))
If JoystickButton(index, 1)
buttons | #XINPUT_GAMEPAD_A
EndIf
If JoystickButton(index, 2)
buttons | #XINPUT_GAMEPAD_B
EndIf
If JoystickButton(index, 3)
buttons | #XINPUT_GAMEPAD_X
EndIf
If JoystickButton(index, 4)
buttons | #XINPUT_GAMEPAD_Y
EndIf
If JoystickButton(index, 5)
buttons | #XINPUT_GAMEPAD_LEFT_SHOULDER
EndIf
If JoystickButton(index, 6)
buttons | #XINPUT_GAMEPAD_RIGHT_SHOULDER
EndIf
If JoystickButton(index, 7)
buttons | #XINPUT_GAMEPAD_BACK
EndIf
If JoystickButton(index, 8)
buttons | #XINPUT_GAMEPAD_START
EndIf
If JoystickButton(index, 9)
buttons | #XINPUT_GAMEPAD_LEFT_THUMB
EndIf
If JoystickButton(index, 10)
buttons | #XINPUT_GAMEPAD_RIGHT_THUMB
EndIf
;The D-Pad is another weird mapping that is technically wrong, but this is the only way to map it so it makes sense to the user.
result = JoystickAxisX(index, 2, #PB_Relative)
If (result = 1) Or (result = -1) ;center fix
result = 0
EndIf
If result < 0
buttons | #XINPUT_GAMEPAD_DPAD_LEFT
EndIf
If result > 0
buttons | #XINPUT_GAMEPAD_DPAD_RIGHT
EndIf
result = JoystickAxisY(index, 2, #PB_Relative)
If (result = 1) Or (result = -1) ;center fix
result = 0
EndIf
If result > 0
buttons | #XINPUT_GAMEPAD_DPAD_UP
EndIf
If result < 0
buttons | #XINPUT_GAMEPAD_DPAD_DOWN
EndIf
*pbinput\normalized\buttons = buttons
result = JoystickAxisZ(index, 0, #PB_Relative)
If (result = 1) Or (result = -1) ;center fix
result = 0
EndIf
If result < 0
*pbinput\normalized\triggerr = (-result) * 0.001
ElseIf result > 0
*pbinput\normalized\triggerl = result * 0.001
EndIf
result = JoystickAxisX(index, 0, #PB_Relative)
If (result = 1) Or (result = -1) ;center fix
result = 0
EndIf
*pbinput\normalized\thumblx = result * 0.001
result = JoystickAxisY(index, 0, #PB_Relative)
If (result = 1) Or (result = -1) ;center fix
result = 0
EndIf
*pbinput\normalized\thumbly = (-result) * 0.001
result = JoystickAxisX(index, 1, #PB_Relative)
If (result = 1) Or (result = -1) ;center fix
result = 0
EndIf
*pbinput\normalized\thumbrx = result * 0.001
result = JoystickAxisY(index, 1, #PB_Relative)
If (result = 1) Or (result = -1) ;center fix
result = 0
EndIf
*pbinput\normalized\thumbry = (-result) * 0.001
ProcedureReturn *pbinput\normalized
EndProcedure
;************************************************************************
;Example
Define *pbinput, *state.XINPUT_NORMALIZED
If OpenConsole("PBinput2 Test") = #False
End
EndIf
EnableGraphicalConsole(#True)
*pbinput = PBinput_Init()
If *pbinput = #Null
PrintN("PBinput_Init() failed.")
End
EndIf
;Note that with DirectInput the Left and Right triggers are considered being on the same (Z) axis, with Xinput they are not.
; If a Xinput gamepad is connected,
; the expected results with PBinput_Get2() are...
; Button A: 0 or 1
; Button B: 0 or 1
; Button X: 0 or 1
; Button Y: 0 or 1
; Button Left Shoulder: 0 or 1
; Button Right Shoulder: 0 or 1
; Button Back: 0 or 1
; Button Start: 0 or 1
; Button Left Thumb: 0 or 1
; Button Right Thumb: 0 or 1
; Button D-Pad Left: 0 or 1
; Button D-Pad Up: 0 or 1
; Button D-Pad Right: 0 or 1
; Button D-Pad Down: 0 or 1
; Left Trigger: 0.0 to 1.0
; Right Trigger: 0.0 to 1.0
; Left Stick X: -1.0 (left) to 0.0 (center) to 1.0 (right)
; Left Stick Y: -1.0 (down) to 0.0 (center) to 1.0 (up)
; Right Stick X: -1.0 (left) to 0.0 (center) to 1.0 (right)
; Right Stick Y: -1.0 (down) to 0.0 (center) to 1.0 (up)
Repeat
*state = PBinput_GetType2(*pbinput, 0)
If *state
ClearConsole()
PrintN("Button A: " + Str(Xinput_Button(*state\buttons, #XINPUT_GAMEPAD_A)))
PrintN("Button B: " + Str(Xinput_Button(*state\buttons, #XINPUT_GAMEPAD_B)))
PrintN("Button X: " + Str(Xinput_Button(*state\buttons, #XINPUT_GAMEPAD_X)))
PrintN("Button Y: " + Str(Xinput_Button(*state\buttons, #XINPUT_GAMEPAD_Y)))
PrintN("Button Left Shoulder: " + Str(Xinput_Button(*state\buttons, #XINPUT_GAMEPAD_LEFT_SHOULDER)))
PrintN("Button Right Shoulder: " + Str(Xinput_Button(*state\buttons, #XINPUT_GAMEPAD_RIGHT_SHOULDER)))
PrintN("Button Back: " + Str(Xinput_Button(*state\buttons, #XINPUT_GAMEPAD_BACK)))
PrintN("Button Start: " + Str(Xinput_Button(*state\buttons, #XINPUT_GAMEPAD_START)))
PrintN("Button Left Thumb: " + Str(Xinput_Button(*state\buttons, #XINPUT_GAMEPAD_LEFT_THUMB)))
PrintN("Button Right Thumb: " + Str(Xinput_Button(*state\buttons, #XINPUT_GAMEPAD_RIGHT_THUMB)))
PrintN("Button D-Pad Left: " + Str(Xinput_Button(*state\buttons, #XINPUT_GAMEPAD_DPAD_LEFT)))
PrintN("Button D-Pad Up: " + Str(Xinput_Button(*state\buttons, #XINPUT_GAMEPAD_DPAD_UP)))
PrintN("Button D-Pad Right: " + Str(Xinput_Button(*state\buttons, #XINPUT_GAMEPAD_DPAD_RIGHT)))
PrintN("Button D-Pad Down: " + Str(Xinput_Button(*state\buttons, #XINPUT_GAMEPAD_DPAD_DOWN)))
PrintN("Left Trigger: " + StrF(*state\triggerl, 7) )
PrintN("Right Trigger: " + StrF(*state\triggerr, 7))
PrintN("Left Stick X: " + StrF(*state\thumblx, 7))
PrintN("Left Stick Y: " + StrF(*state\thumbly, 7))
PrintN("Right Stick X: " + StrF(*state\thumbrx, 7))
PrintN("Right Stick Y: " + StrF(*state\thumbry, 7))
PrintN("")
PrintN("Press enter to stop.")
Else
PrintN("Error")
Break
EndIf
Delay(100)
Until InKey() <> ""
PrintN("")
PrintN("Press enter to quit.")
Input()
*pbinput = PBinput_Free(*pbinput)
CloseConsole()
End