1import sys
2from ctypes import (Structure, Union, c_int, c_char_p, c_void_p, c_float,
3    POINTER, create_string_buffer)
4from .dll import _bind
5from .dll import version as sdl_version
6from .stdinc import SDL_bool, Sint16, Uint32, Uint16, Uint8
7from .joystick import (SDL_JoystickGUID, SDL_Joystick, SDL_JoystickID,
8    SDL_JoystickGetGUIDString)
9from .rwops import SDL_RWops, SDL_RWFromFile
10from .sensor import SDL_SensorType
11
12__all__ = [
13    # Structs & Opaque Types
14    "SDL_GameController", "SDL_GameControllerButtonBind",
15
16    # Enums
17    "SDL_GameControllerType",
18    "SDL_CONTROLLER_TYPE_UNKNOWN", "SDL_CONTROLLER_TYPE_XBOX360",
19    "SDL_CONTROLLER_TYPE_XBOXONE", "SDL_CONTROLLER_TYPE_PS3",
20    "SDL_CONTROLLER_TYPE_PS4", "SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO",
21    "SDL_CONTROLLER_TYPE_VIRTUAL", "SDL_CONTROLLER_TYPE_PS5",
22    "SDL_CONTROLLER_TYPE_AMAZON_LUNA", "SDL_CONTROLLER_TYPE_GOOGLE_STADIA",
23
24    "SDL_GameControllerBindType",
25    "SDL_CONTROLLER_BINDTYPE_NONE", "SDL_CONTROLLER_BINDTYPE_BUTTON",
26    "SDL_CONTROLLER_BINDTYPE_AXIS", "SDL_CONTROLLER_BINDTYPE_HAT",
27
28    "SDL_GameControllerAxis",
29    "SDL_CONTROLLER_AXIS_INVALID", "SDL_CONTROLLER_AXIS_LEFTX",
30    "SDL_CONTROLLER_AXIS_LEFTY", "SDL_CONTROLLER_AXIS_RIGHTX",
31    "SDL_CONTROLLER_AXIS_RIGHTY", "SDL_CONTROLLER_AXIS_TRIGGERLEFT",
32    "SDL_CONTROLLER_AXIS_TRIGGERRIGHT", "SDL_CONTROLLER_AXIS_MAX",
33
34    "SDL_GameControllerButton",
35    "SDL_CONTROLLER_BUTTON_INVALID", "SDL_CONTROLLER_BUTTON_A",
36    "SDL_CONTROLLER_BUTTON_B", "SDL_CONTROLLER_BUTTON_X",
37    "SDL_CONTROLLER_BUTTON_Y", "SDL_CONTROLLER_BUTTON_BACK",
38    "SDL_CONTROLLER_BUTTON_GUIDE", "SDL_CONTROLLER_BUTTON_START",
39    "SDL_CONTROLLER_BUTTON_LEFTSTICK", "SDL_CONTROLLER_BUTTON_RIGHTSTICK",
40    "SDL_CONTROLLER_BUTTON_LEFTSHOULDER",
41    "SDL_CONTROLLER_BUTTON_RIGHTSHOULDER",
42    "SDL_CONTROLLER_BUTTON_DPAD_UP", "SDL_CONTROLLER_BUTTON_DPAD_DOWN",
43    "SDL_CONTROLLER_BUTTON_DPAD_LEFT", "SDL_CONTROLLER_BUTTON_DPAD_RIGHT",
44    "SDL_CONTROLLER_BUTTON_MISC1", "SDL_CONTROLLER_BUTTON_PADDLE1",
45    "SDL_CONTROLLER_BUTTON_PADDLE2", "SDL_CONTROLLER_BUTTON_PADDLE3",
46    "SDL_CONTROLLER_BUTTON_PADDLE4", "SDL_CONTROLLER_BUTTON_TOUCHPAD",
47    "SDL_CONTROLLER_BUTTON_MAX",
48
49    # Macro Functions
50    "SDL_GameControllerAddMappingsFromFile",
51
52    # Functions
53    "SDL_GameControllerAddMappingsFromRW", "SDL_GameControllerAddMapping",
54    "SDL_GameControllerNumMappings", "SDL_GameControllerMappingForIndex",
55    "SDL_GameControllerMappingForGUID", "SDL_GameControllerMapping",
56    "SDL_IsGameController", "SDL_GameControllerNameForIndex",
57    "SDL_GameControllerTypeForIndex", "SDL_GameControllerMappingForDeviceIndex",
58    "SDL_GameControllerOpen", "SDL_GameControllerFromInstanceID",
59    "SDL_GameControllerFromPlayerIndex", "SDL_GameControllerName",
60    "SDL_GameControllerGetType",
61    "SDL_GameControllerGetPlayerIndex", "SDL_GameControllerSetPlayerIndex",
62    "SDL_GameControllerGetVendor", "SDL_GameControllerGetProduct",
63    "SDL_GameControllerGetProductVersion", "SDL_GameControllerGetSerial",
64    "SDL_GameControllerGetAttached", "SDL_GameControllerGetJoystick",
65    "SDL_GameControllerEventState", "SDL_GameControllerUpdate",
66    "SDL_GameControllerGetAxisFromString", "SDL_GameControllerGetStringForAxis",
67    "SDL_GameControllerGetBindForAxis", "SDL_GameControllerHasAxis",
68    "SDL_GameControllerGetAxis",
69    "SDL_GameControllerGetButtonFromString",
70    "SDL_GameControllerGetStringForButton",
71    "SDL_GameControllerGetBindForButton", "SDL_GameControllerHasButton",
72    "SDL_GameControllerGetButton",
73    "SDL_GameControllerGetNumTouchpads",
74    "SDL_GameControllerGetNumTouchpadFingers",
75    "SDL_GameControllerGetTouchpadFinger",
76    "SDL_GameControllerHasSensor", "SDL_GameControllerSetSensorEnabled",
77    "SDL_GameControllerIsSensorEnabled",
78    "SDL_GameControllerGetSensorDataRate", "SDL_GameControllerGetSensorData",
79    "SDL_GameControllerRumble", "SDL_GameControllerRumbleTriggers",
80    "SDL_GameControllerHasLED", "SDL_GameControllerSetLED",
81    "SDL_GameControllerSendEffect",
82    "SDL_GameControllerClose"
83]
84
85
86class SDL_GameController(c_void_p):
87    pass
88
89
90SDL_GameControllerBindType = c_int
91
92SDL_CONTROLLER_BINDTYPE_NONE = 0
93SDL_CONTROLLER_BINDTYPE_BUTTON = 1
94SDL_CONTROLLER_BINDTYPE_AXIS = 2
95SDL_CONTROLLER_BINDTYPE_HAT = 3
96
97
98SDL_GameControllerType = c_int
99
100SDL_CONTROLLER_TYPE_UNKNOWN = 0
101SDL_CONTROLLER_TYPE_XBOX360 = 1
102SDL_CONTROLLER_TYPE_XBOXONE = 2
103SDL_CONTROLLER_TYPE_PS3 = 3
104SDL_CONTROLLER_TYPE_PS4 = 4
105SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO = 5
106SDL_CONTROLLER_TYPE_VIRTUAL = 6
107SDL_CONTROLLER_TYPE_PS5 = 7
108SDL_CONTROLLER_TYPE_AMAZON_LUNA = 8
109SDL_CONTROLLER_TYPE_GOOGLE_STADIA = 9
110
111
112class _gchat(Structure):
113    _fields_ = [("hat", c_int), ("hat_mask", c_int)]
114
115class _gcvalue(Union):
116    _fields_ = [("button", c_int), ("axis", c_int), ("hat", _gchat)]
117
118class SDL_GameControllerButtonBind(Structure):
119    _fields_ = [("bindType", SDL_GameControllerBindType), ("value", _gcvalue)]
120
121
122SDL_GameControllerAddMapping = _bind("SDL_GameControllerAddMapping", [c_char_p], c_int)
123SDL_GameControllerMapping = _bind("SDL_GameControllerMapping", [POINTER(SDL_GameController)], c_char_p)
124SDL_IsGameController = _bind("SDL_IsGameController", [c_int], SDL_bool)
125SDL_GameControllerNameForIndex = _bind("SDL_GameControllerNameForIndex", [c_int], c_char_p)
126SDL_GameControllerTypeForIndex = _bind("SDL_GameControllerTypeForIndex", [c_int], SDL_GameControllerType, added='2.0.12')
127SDL_GameControllerOpen = _bind("SDL_GameControllerOpen", [c_int], POINTER(SDL_GameController))
128SDL_GameControllerName = _bind("SDL_GameControllerName", [POINTER(SDL_GameController)], c_char_p)
129SDL_GameControllerGetType = _bind("SDL_GameControllerGetType", [POINTER(SDL_GameController)], SDL_GameControllerType, added='2.0.12')
130SDL_GameControllerGetAttached = _bind("SDL_GameControllerGetAttached", [POINTER(SDL_GameController)], SDL_bool)
131SDL_GameControllerGetJoystick = _bind("SDL_GameControllerGetJoystick", [POINTER(SDL_GameController)], POINTER(SDL_Joystick))
132SDL_GameControllerEventState = _bind("SDL_GameControllerEventState", [c_int], c_int)
133SDL_GameControllerUpdate = _bind("SDL_GameControllerUpdate")
134
135
136SDL_GameControllerAxis = c_int
137
138SDL_CONTROLLER_AXIS_INVALID = -1
139SDL_CONTROLLER_AXIS_LEFTX = 0
140SDL_CONTROLLER_AXIS_LEFTY = 1
141SDL_CONTROLLER_AXIS_RIGHTX = 2
142SDL_CONTROLLER_AXIS_RIGHTY = 3
143SDL_CONTROLLER_AXIS_TRIGGERLEFT = 4
144SDL_CONTROLLER_AXIS_TRIGGERRIGHT = 5
145SDL_CONTROLLER_AXIS_MAX = 6
146
147
148SDL_GameControllerGetAxisFromString = _bind("SDL_GameControllerGetAxisFromString", [c_char_p], SDL_GameControllerAxis)
149SDL_GameControllerGetStringForAxis = _bind("SDL_GameControllerGetStringForAxis", [SDL_GameControllerAxis], c_char_p)
150SDL_GameControllerGetBindForAxis = _bind("SDL_GameControllerGetBindForAxis", [POINTER(SDL_GameController), SDL_GameControllerAxis], SDL_GameControllerButtonBind)
151SDL_GameControllerHasAxis = _bind("SDL_GameControllerHasAxis", [POINTER(SDL_GameController), SDL_GameControllerAxis], SDL_bool, added='2.0.14')
152SDL_GameControllerGetAxis = _bind("SDL_GameControllerGetAxis", [POINTER(SDL_GameController), SDL_GameControllerAxis], Sint16)
153
154
155SDL_GameControllerButton = c_int
156
157SDL_CONTROLLER_BUTTON_INVALID = -1
158SDL_CONTROLLER_BUTTON_A = 0
159SDL_CONTROLLER_BUTTON_B = 1
160SDL_CONTROLLER_BUTTON_X = 2
161SDL_CONTROLLER_BUTTON_Y = 3
162SDL_CONTROLLER_BUTTON_BACK = 4
163SDL_CONTROLLER_BUTTON_GUIDE = 5
164SDL_CONTROLLER_BUTTON_START = 6
165SDL_CONTROLLER_BUTTON_LEFTSTICK = 7
166SDL_CONTROLLER_BUTTON_RIGHTSTICK = 8
167SDL_CONTROLLER_BUTTON_LEFTSHOULDER = 9
168SDL_CONTROLLER_BUTTON_RIGHTSHOULDER = 10
169SDL_CONTROLLER_BUTTON_DPAD_UP = 11
170SDL_CONTROLLER_BUTTON_DPAD_DOWN = 12
171SDL_CONTROLLER_BUTTON_DPAD_LEFT = 13
172SDL_CONTROLLER_BUTTON_DPAD_RIGHT = 14
173SDL_CONTROLLER_BUTTON_MISC1 = 15
174SDL_CONTROLLER_BUTTON_PADDLE1 = 16
175SDL_CONTROLLER_BUTTON_PADDLE2 = 17
176SDL_CONTROLLER_BUTTON_PADDLE3 = 18
177SDL_CONTROLLER_BUTTON_PADDLE4 = 19
178SDL_CONTROLLER_BUTTON_TOUCHPAD = 20
179SDL_CONTROLLER_BUTTON_MAX = 21
180if sdl_version < 2014:
181    SDL_CONTROLLER_BUTTON_MAX = 15  # For backwards compatibility
182
183
184SDL_GameControllerGetButtonFromString = _bind("SDL_GameControllerGetButtonFromString", [c_char_p], SDL_GameControllerButton)
185SDL_GameControllerGetStringForButton = _bind("SDL_GameControllerGetStringForButton", [SDL_GameControllerButton], c_char_p)
186SDL_GameControllerGetBindForButton = _bind("SDL_GameControllerGetBindForButton", [POINTER(SDL_GameController), SDL_GameControllerButton], SDL_GameControllerButtonBind)
187SDL_GameControllerHasButton = _bind("SDL_GameControllerHasButton", [POINTER(SDL_GameController), SDL_GameControllerButton], SDL_bool, added='2.0.14')
188SDL_GameControllerGetButton = _bind("SDL_GameControllerGetButton", [POINTER(SDL_GameController), SDL_GameControllerButton], Uint8)
189SDL_GameControllerGetNumTouchpads = _bind("SDL_GameControllerGetNumTouchpads", [POINTER(SDL_GameController)], c_int, added='2.0.14')
190SDL_GameControllerGetNumTouchpadFingers = _bind("SDL_GameControllerGetNumTouchpadFingers", [POINTER(SDL_GameController), c_int], c_int, added='2.0.14')
191SDL_GameControllerGetTouchpadFinger = _bind("SDL_GameControllerGetTouchpadFinger", [POINTER(SDL_GameController), c_int, c_int, POINTER(Uint8), POINTER(c_float), POINTER(c_float), POINTER(c_float)], c_int, added='2.0.14')
192SDL_GameControllerHasSensor = _bind("SDL_GameControllerHasSensor", [POINTER(SDL_GameController), SDL_SensorType], SDL_bool, added='2.0.14')
193SDL_GameControllerSetSensorEnabled = _bind("SDL_GameControllerSetSensorEnabled", [POINTER(SDL_GameController), SDL_SensorType, SDL_bool], c_int, added='2.0.14')
194SDL_GameControllerIsSensorEnabled = _bind("SDL_GameControllerIsSensorEnabled", [POINTER(SDL_GameController), SDL_SensorType], SDL_bool, added='2.0.14')
195SDL_GameControllerGetSensorDataRate = _bind("SDL_GameControllerGetSensorDataRate", [POINTER(SDL_GameController), SDL_SensorType], c_float, added='2.0.16')
196# TODO: Read how GetSensorData is implemented to figure out how the # of floats is determined
197SDL_GameControllerGetSensorData = _bind("SDL_GameControllerGetSensorData", [POINTER(SDL_GameController), SDL_SensorType, POINTER(c_float), c_int], c_int, added='2.0.14')
198SDL_GameControllerClose = _bind("SDL_GameControllerClose", [POINTER(SDL_GameController)])
199SDL_GameControllerAddMappingsFromRW = _bind("SDL_GameControllerAddMappingsFromRW", [POINTER(SDL_RWops), c_int], c_int)
200SDL_GameControllerAddMappingsFromFile = lambda fname: SDL_GameControllerAddMappingsFromRW(SDL_RWFromFile(fname, b"rb"), 1)
201SDL_GameControllerFromInstanceID = _bind("SDL_GameControllerFromInstanceID", [SDL_JoystickID], POINTER(SDL_GameController))
202SDL_GameControllerFromPlayerIndex = _bind("SDL_GameControllerFromPlayerIndex", [c_int], POINTER(SDL_GameController), added='2.0.12')
203SDL_GameControllerGetPlayerIndex = _bind("SDL_GameControllerGetPlayerIndex", [POINTER(SDL_GameController)], c_int, added='2.0.9')
204SDL_GameControllerSetPlayerIndex = _bind("SDL_GameControllerSetPlayerIndex", [POINTER(SDL_GameController), c_int], added='2.0.12')
205SDL_GameControllerGetVendor = _bind("SDL_GameControllerGetVendor", [POINTER(SDL_GameController)], Uint16, added='2.0.6')
206SDL_GameControllerGetProduct = _bind("SDL_GameControllerGetProduct", [POINTER(SDL_GameController)], Uint16, added='2.0.6')
207SDL_GameControllerGetProductVersion = _bind("SDL_GameControllerGetProductVersion", [POINTER(SDL_GameController)], Uint16, added='2.0.6')
208SDL_GameControllerGetSerial = _bind("SDL_GameControllerGetSerial", [POINTER(SDL_GameController)], c_char_p, added='2.0.14')
209SDL_GameControllerNumMappings = _bind("SDL_GameControllerNumMappings", None, c_int, added='2.0.6')
210SDL_GameControllerMappingForIndex = _bind("SDL_GameControllerMappingForIndex", [c_int], c_char_p, added='2.0.6')
211SDL_GameControllerMappingForDeviceIndex = _bind("SDL_GameControllerMappingForDeviceIndex", [c_int], c_char_p, added='2.0.9')
212SDL_GameControllerRumble = _bind("SDL_GameControllerRumble", [POINTER(SDL_GameController), Uint16, Uint16, Uint32], c_int, added='2.0.9')
213SDL_GameControllerRumbleTriggers = _bind("SDL_GameControllerRumbleTriggers", [POINTER(SDL_GameController), Uint16, Uint16, Uint32], c_int, added='2.0.14')
214SDL_GameControllerHasLED = _bind("SDL_GameControllerHasLED", [POINTER(SDL_GameController)], SDL_bool, added='2.0.14')
215SDL_GameControllerSetLED = _bind("SDL_GameControllerSetLED", [POINTER(SDL_GameController), Uint8, Uint8, Uint8], c_int, added='2.0.14')
216SDL_GameControllerSendEffect = _bind("SDL_GameControllerSendEffect", [POINTER(SDL_GameController), c_void_p, c_int], c_int, added='2.0.16')
217
218# Reimplemented w/ other functions due to crash-causing ctypes bug (fixed in 3.8)
219if sys.version_info >= (3, 8, 0, 'final'):
220    SDL_GameControllerMappingForGUID = _bind("SDL_GameControllerMappingForGUID", [SDL_JoystickGUID], c_char_p)
221else:
222    def SDL_GameControllerMappingForGUID(guid):
223        buff = create_string_buffer(33)
224        SDL_JoystickGetGUIDString(guid, buff, 33) # Get GUID string
225        guid_str = buff.value
226        # Iterate over controller mappings and look for a GUID match
227        # Note: iterates in reverse, so user-defined mappings are checked first
228        num = SDL_GameControllerNumMappings()
229        for i in range(num - 1, -1, -1):
230            m = SDL_GameControllerMappingForIndex(i)
231            if m.split(b',')[0] == guid_str:
232                return m
233        return None
234