It turns out that it is quite easy to use an XBOX 360 joypad in Panda3d on Windows using no extra modules by just making use of the ctypes modules which are part of the normal Python install. I am no expert, I found this technique at https://gist.github.com/dcoles/4378653 on github and just wanted to share it. The code sample below to the best of my knowledge runs on Panda 1.6 and later and can be saved as a .py file and run as a Panda3d program to demonstrate how you can read the joypad’s status and make it vibrate.
import direct.directbase.DirectStart
from direct.gui.OnscreenText import OnscreenText
from direct.task.Task import Task
from direct.showbase.DirectObject import DirectObject
import ctypes
from ctypes import *
from ctypes.wintypes import BYTE, WORD, SHORT, DWORD, SHORT
UBYTE = c_ubyte
class XInputGamepad(ctypes.Structure):
_fields_ = [
("buttons", WORD),
("leftTrigger", UBYTE),
("rightTrigger", UBYTE),
("thumbLX", SHORT),
("thumbLY", SHORT),
("thumbRX", SHORT),
("thumbRY", SHORT),
]
def __repr__(self):
return "XInputGamepad(thumbLX={0}, thumbRX={1}, ...)".format(self.thumbLX, self.thumbRX)
class XInputState(ctypes.Structure):
_fields_ = [
("packetNumber", DWORD),
("gamepad", XInputGamepad),
]
def __repr__(self):
return "XInputState(packetNumber={0})".format(self.packetNumber)
class XInputVibration(ctypes.Structure):
_fields_=[
("leftMotorSpeed",WORD),
("rightMotorSpeed",WORD),
]
def __init__(self,leftSpeed,rightSpeed):
self.leftMotorSpeed=leftSpeed
self.rightMotorSpeed=rightSpeed
class Main(DirectObject):
def __init__(self):
self.XINPUT_GAMEPAD_DPAD_UP =0x0001
self.XINPUT_GAMEPAD_DPAD_DOWN =0x0002
self.XINPUT_GAMEPAD_DPAD_LEFT =0x0004
self.XINPUT_GAMEPAD_DPAD_RIGHT =0x0008
self.XINPUT_GAMEPAD_START =0x0010
self.XINPUT_GAMEPAD_BACK =0x0020
self.XINPUT_GAMEPAD_LEFT_THUMB =0x0040
self.XINPUT_GAMEPAD_RIGHT_THUMB =0x0080
self.XINPUT_GAMEPAD_LEFT_SHOULDER =0x0100
self.XINPUT_GAMEPAD_RIGHT_SHOULDER =0x0200
self.XINPUT_GAMEPAD_A =0x1000
self.XINPUT_GAMEPAD_B =0x2000
self.XINPUT_GAMEPAD_X =0x4000
self.XINPUT_GAMEPAD_Y =0x8000
base.setBackgroundColor(0.6, 0.6, 0.8)
self.xinput = ctypes.windll.xinput9_1_0
self.onScreenTextA=OnscreenText(text = "", pos = (0,0.9), scale = 0.1, mayChange = 1)
self.onScreenText0=OnscreenText(text = "", pos = (0,0.6), scale = 0.1, mayChange = 1)
self.onScreenText1=OnscreenText(text = "", pos = (0,0.5), scale = 0.1, mayChange = 1)
self.onScreenText2=OnscreenText(text = "", pos = (0,0.4), scale = 0.1, mayChange = 1)
self.onScreenText3=OnscreenText(text = "", pos = (0,0.3), scale = 0.1, mayChange = 1)
self.onScreenText4=OnscreenText(text = "", pos = (0,0.2), scale = 0.1, mayChange = 1)
self.onScreenText5=OnscreenText(text = "", pos = (0,0.1), scale = 0.1, mayChange = 1)
self.onScreenText6=OnscreenText(text = "", pos = (0,0.0), scale = 0.1, mayChange = 1)
self.onScreenText7=OnscreenText(text = "", pos = (0,-0.1), scale = 0.1, mayChange = 1)
self.onScreenText8=OnscreenText(text = "", pos = (0,-0.2), scale = 0.1, mayChange = 1)
self.onScreenText9=OnscreenText(text = "", pos = (0,-0.3), scale = 0.1, mayChange = 1)
self.onScreenText10=OnscreenText(text = "", pos = (0,-0.4), scale = 0.1, mayChange = 1)
taskMgr.add(self.countPads,"countPads")
def howManyPadsConnected(self):
pads=0
for x in range(0,4):
state = XInputState(0)
res = self.xinput.XInputGetState(x, ctypes.byref(state))
if res==0:
pads+=1
return pads
def getState(self,index):
state = XInputState(0)
res = self.xinput.XInputGetState(index, ctypes.byref(state))
if res != 0:
raise ctypes.WinError(res)
return state.gamepad
def setState(self,index,state):
res = self.xinput.XInputSetState(index, ctypes.byref(state))
if res != 0:
raise ctypes.WinError(res)
def countPads(self,task):
if self.howManyPadsConnected()<1:
self.onScreenText0.setText("connect at least one pad")
else:
taskMgr.add(self.mainTask,"mainTask")
taskMgr.remove("countPads")
return Task.cont
def mainTask(self, task):
self.onScreenTextA.setText(str(self.howManyPadsConnected())+" pads are connected")
#get the state of the first joypad (the state holds information on the
#position of the joysticks, triggers and which buttons are pressed)
state=self.getState(0)
self.onScreenText0.setText("pad 0:")
self.onScreenText1.setText("buttons= "+str(state.buttons))
self.onScreenText2.setText("left trigger= "+str(state.leftTrigger))
self.onScreenText3.setText("right trigger= "+str(state.rightTrigger))
self.onScreenText4.setText("left thumb stick x-axis= "+str(state.thumbLX))
self.onScreenText5.setText("left thumb stick y-axis= "+str(state.thumbLY))
self.onScreenText6.setText("right thumb stick x-axis= "+str(state.thumbRX))
self.onScreenText7.setText("right thumb stick y-axis= "+str(state.thumbRY))
if state.buttons & self.XINPUT_GAMEPAD_A :
self.onScreenText8.setText("A is pressed")
self.setState(0, XInputVibration(0,65534))
else:
self.onScreenText8.setText("press A to make right motor vibrate")
if state.buttons & self.XINPUT_GAMEPAD_B :
self.onScreenText9.setText("B is pressed")
self.setState(0, XInputVibration(65534,0))
else:
self.onScreenText9.setText("press B to make left motor vibrate")
if state.buttons & self.XINPUT_GAMEPAD_X :
self.onScreenText10.setText("X is pressed")
self.setState(0, XInputVibration(0,0))
else:
self.onScreenText10.setText("press X to stop vibrations")
return Task.cont
main = Main()
run()