Skip to content

Commit f78d1c3

Browse files
committed
Initial commit
0 parents  commit f78d1c3

File tree

3 files changed

+218
-0
lines changed

3 files changed

+218
-0
lines changed

.gitattributes

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Auto detect text files and perform LF normalization
2+
* text=auto

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# XInput

XInput.py

Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
import ctypes, ctypes.util
2+
3+
from ctypes import Structure, POINTER
4+
5+
from math import sqrt
6+
7+
import glm
8+
9+
XINPUT_DLL_NAMES = (
10+
"XInput1_4.dll",
11+
"XInput9_1_0.dll",
12+
"XInput1_3.dll",
13+
"XInput1_2.dll",
14+
"XInput1_1.dll"
15+
)
16+
17+
libXInput = None
18+
19+
for name in XINPUT_DLL_NAMES:
20+
found = ctypes.util.find_library(name)
21+
if found:
22+
libXInput = ctypes.CDLL(found)
23+
break
24+
25+
if not libXInput:
26+
raise IOError("XInput library was not found.")
27+
28+
WORD = ctypes.c_ushort
29+
BYTE = ctypes.c_ubyte
30+
SHORT = ctypes.c_short
31+
DWORD = ctypes.c_ulong
32+
33+
ERROR_SUCCESS = 0
34+
ERROR_DEVICE_NOT_CONNECTED = 1167
35+
36+
XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE = 7849
37+
XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE = 8689
38+
XINPUT_GAMEPAD_TRIGGER_THRESHOLD = 30
39+
40+
BATTERY_DEVTYPE_GAMEPAD = 0x00
41+
BATTERY_TYPE_DISCONNECTED = 0x00
42+
BATTERY_TYPE_WIRED = 0x01
43+
BATTERY_TYPE_ALKALINE = 0x02
44+
BATTERY_TYPE_NIMH = 0x03
45+
BATTERY_TYPE_UNKNOWN = 0xFF
46+
BATTERY_LEVEL_EMPTY = 0x00
47+
BATTERY_LEVEL_LOW = 0x01
48+
BATTERY_LEVEL_MEDIUM = 0x02
49+
BATTERY_LEVEL_FULL = 0x03
50+
51+
_battery_type_dict = {BATTERY_TYPE_DISCONNECTED : "DISCONNECTED",
52+
BATTERY_TYPE_WIRED : "WIRED",
53+
BATTERY_TYPE_ALKALINE : "ALKALINE",
54+
BATTERY_TYPE_NIMH : "NIMH",
55+
BATTERY_TYPE_UNKNOWN : "UNKNOWN"}
56+
57+
_battery_level_dict = {BATTERY_LEVEL_EMPTY : "EMPTY",
58+
BATTERY_LEVEL_LOW : "LOW",
59+
BATTERY_LEVEL_MEDIUM : "MEDIUM",
60+
BATTERY_LEVEL_FULL : "FULL"}
61+
62+
class XINPUT_GAMEPAD(Structure):
63+
_fields_ = [("wButtons", WORD),
64+
("bLeftTrigger", BYTE),
65+
("bRightTrigger", BYTE),
66+
("sThumbLX", SHORT),
67+
("sThumbLY", SHORT),
68+
("sThumbRX", SHORT),
69+
("sThumbRY", SHORT),
70+
]
71+
72+
class XINPUT_STATE(Structure):
73+
_fields_ = [("dwPacketNumber", DWORD),
74+
("Gamepad", XINPUT_GAMEPAD),
75+
]
76+
77+
State = XINPUT_STATE
78+
79+
class XINPUT_VIBRATION(Structure):
80+
_fields_ = [("wLeftMotorSpeed", WORD),
81+
("wRightMotorSpeed", WORD),
82+
]
83+
84+
class XINPUT_BATTERY_INFORMATION(Structure):
85+
_fields_ = [("BatteryType", BYTE),
86+
("BatteryLevel", BYTE),
87+
]
88+
89+
libXInput.XInputGetState.argtypes = [DWORD, POINTER(XINPUT_STATE)]
90+
libXInput.XInputGetState.restype = DWORD
91+
92+
def XInputGetState(dwUserIndex, state):
93+
return libXInput.XInputGetState(dwUserIndex, ctypes.byref(state))
94+
95+
libXInput.XInputSetState.argtypes = [DWORD, POINTER(XINPUT_VIBRATION)]
96+
libXInput.XInputSetState.restype = DWORD
97+
98+
def XInputSetState(dwUserIndex, vibration):
99+
return libXInput.XInputSetState(dwUserIndex, ctypes.byref(vibration))
100+
101+
libXInput.XInputGetBatteryInformation.argtypes = [DWORD, BYTE, POINTER(XINPUT_BATTERY_INFORMATION)]
102+
libXInput.XInputGetBatteryInformation.restype = DWORD
103+
104+
def XInputGetBatteryInformation(dwUserIndex, devType, batteryInformation):
105+
return libXInput.XInputGetBatteryInformation(dwUserIndex, devType, ctypes.byref(batteryInformation))
106+
107+
108+
class XInputNotConnectedError(Exception):
109+
pass
110+
111+
def get_state(user_index):
112+
state = XINPUT_STATE()
113+
res = XInputGetState(user_index, state)
114+
if res == ERROR_DEVICE_NOT_CONNECTED:
115+
raise XInputNotConnectedError("Controller [{}] appears to be disconnected.".format(user_index))
116+
117+
assert res == 0, "Couldn't get the state of controller [{}]. Is it disconnected?".format(user_index)
118+
119+
return state
120+
121+
def get_battery_information(user_index):
122+
battery_information = XINPUT_BATTERY_INFORMATION()
123+
XInputGetBatteryInformation(user_index, BATTERY_DEVTYPE_GAMEPAD, battery_information)
124+
return (_battery_type_dict[battery_information.BatteryType], _battery_level_dict[battery_information.BatteryLevel])
125+
126+
def set_vibration(user_index, left_speed, right_speed):
127+
if type(left_speed) == float and left_speed <= 1.0:
128+
left_speed = (round(65535 * left_speed, 0))
129+
130+
if type(right_speed) == float and right_speed <= 1.0:
131+
right_speed = (round(65535 * right_speed, 0))
132+
133+
vibration = XINPUT_VIBRATION()
134+
135+
vibration.wLeftMotorSpeed = int(left_speed)
136+
vibration.wRightMotorSpeed = int(right_speed)
137+
138+
return XInputSetState(user_index, vibration)
139+
140+
def get_button_values(state):
141+
wButtons = state.Gamepad.wButtons
142+
return {"DPAD_UP" : bool(wButtons & 0x0001),
143+
"DPAD_DOWN" : bool(wButtons & 0x0002),
144+
"DPAD_LEFT" : bool(wButtons & 0x0004),
145+
"DPAD_RIGHT" : bool(wButtons & 0x0008),
146+
"START" : bool(wButtons & 0x0010),
147+
"BACK" : bool(wButtons & 0x0020),
148+
"LEFT_THUMB" : bool(wButtons & 0x0040),
149+
"RIGHT_THUMB" : bool(wButtons & 0x0080),
150+
"LEFT_SHOULDER" : bool(wButtons & 0x0100),
151+
"RIGHT_SHOULDER" : bool(wButtons & 0x0200),
152+
"A" : bool(wButtons & 0x1000),
153+
"B" : bool(wButtons & 0x2000),
154+
"X" : bool(wButtons & 0x4000),
155+
"Y" : bool(wButtons & 0x8000),
156+
}
157+
158+
def get_trigger_values(state):
159+
LT = state.Gamepad.bLeftTrigger
160+
RT = state.Gamepad.bRightTrigger
161+
162+
normLT = 0
163+
normRT = 0
164+
165+
if LT > XINPUT_GAMEPAD_TRIGGER_THRESHOLD:
166+
LT -= XINPUT_GAMEPAD_TRIGGER_THRESHOLD
167+
normLT = LT / (255 - XINPUT_GAMEPAD_TRIGGER_THRESHOLD)
168+
else:
169+
LT = 0
170+
171+
if RT > XINPUT_GAMEPAD_TRIGGER_THRESHOLD:
172+
RT -= XINPUT_GAMEPAD_TRIGGER_THRESHOLD
173+
normRT = RT / (255 - XINPUT_GAMEPAD_TRIGGER_THRESHOLD)
174+
else:
175+
RT = 0
176+
177+
return (normLT, normRT)
178+
179+
def get_thumb_values(state):
180+
LX = state.Gamepad.sThumbLX
181+
LY = state.Gamepad.sThumbLY
182+
RX = state.Gamepad.sThumbRX
183+
RY = state.Gamepad.sThumbRY
184+
185+
magL = sqrt(LX*LX + LY*LY)
186+
magR = sqrt(RX*RX + RY*RY)
187+
188+
normLX = LX / magL
189+
normLY = LY / magL
190+
normRX = RX / magR
191+
normRY = RY / magR
192+
193+
normMagL = 0
194+
normMagR = 0
195+
196+
if (magL > XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE):
197+
magL = min(32767, magL)
198+
199+
magL -= XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE
200+
201+
normMagL = magL / (32767 - XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE)
202+
else:
203+
magL = 0
204+
205+
if (magR > XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE):
206+
magR = min(32767, magR)
207+
208+
magR -= XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE
209+
210+
normMagR = magR / (32767 - XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE)
211+
else:
212+
magR = 0
213+
214+
return (glm.vec2(normLX, normLY) * normMagL, glm.vec2(normRX, normRY) * normMagR)
215+

0 commit comments

Comments
 (0)