55from __future__ import annotations
66
77import enum
8- from typing import Dict , List , Optional , Tuple
8+ from typing import Any , Dict , List , Optional , Tuple , Union
99
1010from typing_extensions import Final , Literal
1111
2626}
2727
2828
29+ class ControllerAxis (enum .IntEnum ):
30+ """The standard axes for a game controller."""
31+
32+ INVALID = lib .SDL_CONTROLLER_AXIS_INVALID or - 1
33+ LEFTX = lib .SDL_CONTROLLER_AXIS_LEFTX or 0
34+ """"""
35+ LEFTY = lib .SDL_CONTROLLER_AXIS_LEFTY or 1
36+ """"""
37+ RIGHTX = lib .SDL_CONTROLLER_AXIS_RIGHTX or 2
38+ """"""
39+ RIGHTY = lib .SDL_CONTROLLER_AXIS_RIGHTY or 3
40+ """"""
41+ TRIGGERLEFT = lib .SDL_CONTROLLER_AXIS_TRIGGERLEFT or 4
42+ """"""
43+ TRIGGERRIGHT = lib .SDL_CONTROLLER_AXIS_TRIGGERRIGHT or 5
44+ """"""
45+
46+
47+ class ControllerButton (enum .IntEnum ):
48+ """The standard buttons for a game controller."""
49+
50+ INVALID = lib .SDL_CONTROLLER_BUTTON_INVALID or - 1
51+ A = lib .SDL_CONTROLLER_BUTTON_A or 0
52+ """"""
53+ B = lib .SDL_CONTROLLER_BUTTON_B or 1
54+ """"""
55+ X = lib .SDL_CONTROLLER_BUTTON_X or 2
56+ """"""
57+ Y = lib .SDL_CONTROLLER_BUTTON_Y or 3
58+ """"""
59+ BACK = lib .SDL_CONTROLLER_BUTTON_BACK or 4
60+ """"""
61+ GUIDE = lib .SDL_CONTROLLER_BUTTON_GUIDE or 5
62+ """"""
63+ START = lib .SDL_CONTROLLER_BUTTON_START or 6
64+ """"""
65+ LEFTSTICK = lib .SDL_CONTROLLER_BUTTON_LEFTSTICK or 7
66+ """"""
67+ RIGHTSTICK = lib .SDL_CONTROLLER_BUTTON_RIGHTSTICK or 8
68+ """"""
69+ LEFTSHOULDER = lib .SDL_CONTROLLER_BUTTON_LEFTSHOULDER or 9
70+ """"""
71+ RIGHTSHOULDER = lib .SDL_CONTROLLER_BUTTON_RIGHTSHOULDER or 10
72+ """"""
73+ DPAD_UP = lib .SDL_CONTROLLER_BUTTON_DPAD_UP or 11
74+ """"""
75+ DPAD_DOWN = lib .SDL_CONTROLLER_BUTTON_DPAD_DOWN or 12
76+ """"""
77+ DPAD_LEFT = lib .SDL_CONTROLLER_BUTTON_DPAD_LEFT or 13
78+ """"""
79+ DPAD_RIGHT = lib .SDL_CONTROLLER_BUTTON_DPAD_RIGHT or 14
80+ """"""
81+ MISC1 = 15
82+ """"""
83+ PADDLE1 = 16
84+ """"""
85+ PADDLE2 = 17
86+ """"""
87+ PADDLE3 = 18
88+ """"""
89+ PADDLE4 = 19
90+ """"""
91+ TOUCHPAD = 20
92+ """"""
93+
94+
2995class Power (enum .IntEnum ):
3096 """The possible power states of a controller.
3197
@@ -50,15 +116,14 @@ class Power(enum.IntEnum):
50116
51117
52118class Joystick :
53- """An SDL joystick.
119+ """A low-level SDL joystick.
54120
55121 .. seealso::
56122 https://wiki.libsdl.org/CategoryJoystick
57123 """
58124
59- def __init__ (self , device_index : int ):
60- tcod .sdl .sys .init (tcod .sdl .sys .Subsystem .JOYSTICK )
61- self .sdl_joystick_p : Final = _check_p (ffi .gc (lib .SDL_JoystickOpen (device_index ), lib .SDL_JoystickClose ))
125+ def __init__ (self , sdl_joystick_p : Any ):
126+ self .sdl_joystick_p : Final = sdl_joystick_p
62127 """The CFFI pointer to an SDL_Joystick struct."""
63128 self .axes : Final [int ] = _check (lib .SDL_JoystickNumAxes (self .sdl_joystick_p ))
64129 """The total number of axes."""
@@ -74,6 +139,24 @@ def __init__(self, device_index: int):
74139 """The GUID of this joystick."""
75140 self .id : Final [int ] = _check (lib .SDL_JoystickInstanceID (self .sdl_joystick_p ))
76141 """The instance ID of this joystick. This is not the same as the device ID."""
142+ self ._keep_alive : Any = None
143+ """The owner of this objects memory if this object does not own itself."""
144+
145+ @classmethod
146+ def _open (cls , device_index : int ) -> Joystick :
147+ tcod .sdl .sys .init (tcod .sdl .sys .Subsystem .JOYSTICK )
148+ p = _check_p (ffi .gc (lib .SDL_JoystickOpen (device_index ), lib .SDL_JoystickClose ))
149+ return cls (p )
150+
151+ def __eq__ (self , other : object ) -> bool :
152+ if isinstance (other , GameController ):
153+ return self == other .joystick .id
154+ if isinstance (other , Joystick ):
155+ return self .id == other .id
156+ return NotImplemented
157+
158+ def __hash__ (self ) -> int :
159+ return hash (self .id )
77160
78161 def _get_guid (self ) -> str :
79162 guid_str = ffi .new ("char[33]" )
@@ -95,30 +178,218 @@ def get_ball(self, ball: int) -> Tuple[int, int]:
95178 return int (xy [0 ]), int (xy [1 ])
96179
97180 def get_button (self , button : int ) -> bool :
98- """Return True if `button` is pressed ."""
181+ """Return True if `button` is currently held ."""
99182 return bool (lib .SDL_JoystickGetButton (self .sdl_joystick_p , button ))
100183
101184 def get_hat (self , hat : int ) -> Tuple [Literal [- 1 , 0 , 1 ], Literal [- 1 , 0 , 1 ]]:
102185 """Return the direction of `hat` as (x, y). With (-1, -1) being in the upper-left."""
103186 return _HAT_DIRECTIONS [lib .SDL_JoystickGetHat (self .sdl_joystick_p , hat )]
104187
105188
106- def get_number () -> int :
189+ class GameController :
190+ """A standard interface for an Xbox 360 style game controller."""
191+
192+ def __init__ (self , sdl_controller_p : Any ):
193+ self .sdl_controller_p : Final = sdl_controller_p
194+ self .joystick : Final = Joystick (lib .SDL_GameControllerGetJoystick (self .sdl_controller_p ))
195+ """The :any:`Joystick` associated with this controller."""
196+ self .joystick ._keep_alive = self .sdl_controller_p # This objects real owner needs to be kept alive.
197+
198+ @classmethod
199+ def _open (cls , joystick_index : int ) -> GameController :
200+ return cls (_check_p (ffi .gc (lib .SDL_GameControllerOpen (joystick_index ), lib .SDL_GameControllerClose )))
201+
202+ def get_button (self , button : ControllerButton ) -> bool :
203+ """Return True if `button` is currently held."""
204+ return bool (lib .SDL_GameControllerGetButton (self .sdl_controller_p , button ))
205+
206+ def get_axis (self , axis : ControllerAxis ) -> int :
207+ """Return the state of the given `axis`.
208+
209+ The state is usually a value from -32768 to 32767, with positive values towards the lower-right direction.
210+ Triggers have the range of 0 to 32767 instead.
211+ """
212+ return int (lib .SDL_GameControllerGetAxis (self .sdl_controller_p , axis ))
213+
214+ def __eq__ (self , other : object ) -> bool :
215+ if isinstance (other , GameController ):
216+ return self .joystick .id == other .joystick .id
217+ if isinstance (other , Joystick ):
218+ return self .joystick .id == other .id
219+ return NotImplemented
220+
221+ def __hash__ (self ) -> int :
222+ return hash (self .joystick .id )
223+
224+ # These could exist as convenience functions, but the get_X functions are probably better.
225+ @property
226+ def _left_x (self ) -> int :
227+ "Return the position of this axis. (-32768 to 32767)"
228+ return int (lib .SDL_GameControllerGetAxis (self .sdl_controller_p , lib .SDL_CONTROLLER_AXIS_LEFTX ))
229+
230+ @property
231+ def _left_y (self ) -> int :
232+ "Return the position of this axis. (-32768 to 32767)"
233+ return int (lib .SDL_GameControllerGetAxis (self .sdl_controller_p , lib .SDL_CONTROLLER_AXIS_LEFTY ))
234+
235+ @property
236+ def _right_x (self ) -> int :
237+ "Return the position of this axis. (-32768 to 32767)"
238+ return int (lib .SDL_GameControllerGetAxis (self .sdl_controller_p , lib .SDL_CONTROLLER_AXIS_RIGHTX ))
239+
240+ @property
241+ def _right_y (self ) -> int :
242+ "Return the position of this axis. (-32768 to 32767)"
243+ return int (lib .SDL_GameControllerGetAxis (self .sdl_controller_p , lib .SDL_CONTROLLER_AXIS_RIGHTY ))
244+
245+ @property
246+ def _trigger_left (self ) -> int :
247+ "Return the position of this trigger. (0 to 32767)"
248+ return int (lib .SDL_GameControllerGetAxis (self .sdl_controller_p , lib .SDL_CONTROLLER_AXIS_TRIGGERLEFT ))
249+
250+ @property
251+ def _trigger_right (self ) -> int :
252+ "Return the position of this trigger. (0 to 32767)"
253+ return int (lib .SDL_GameControllerGetAxis (self .sdl_controller_p , lib .SDL_CONTROLLER_AXIS_TRIGGERRIGHT ))
254+
255+ @property
256+ def _a (self ) -> bool :
257+ """Return True if this button is held."""
258+ return bool (lib .SDL_GameControllerGetButton (self .sdl_controller_p , lib .SDL_CONTROLLER_BUTTON_A ))
259+
260+ @property
261+ def _b (self ) -> bool :
262+ """Return True if this button is held."""
263+ return bool (lib .SDL_GameControllerGetButton (self .sdl_controller_p , lib .SDL_CONTROLLER_BUTTON_B ))
264+
265+ @property
266+ def _x (self ) -> bool :
267+ """Return True if this button is held."""
268+ return bool (lib .SDL_GameControllerGetButton (self .sdl_controller_p , lib .SDL_CONTROLLER_BUTTON_X ))
269+
270+ @property
271+ def _y (self ) -> bool :
272+ """Return True if this button is held."""
273+ return bool (lib .SDL_GameControllerGetButton (self .sdl_controller_p , lib .SDL_CONTROLLER_BUTTON_Y ))
274+
275+ @property
276+ def _back (self ) -> bool :
277+ """Return True if this button is held."""
278+ return bool (lib .SDL_GameControllerGetButton (self .sdl_controller_p , lib .SDL_CONTROLLER_BUTTON_BACK ))
279+
280+ @property
281+ def _guide (self ) -> bool :
282+ """Return True if this button is held."""
283+ return bool (lib .SDL_GameControllerGetButton (self .sdl_controller_p , lib .SDL_CONTROLLER_BUTTON_GUIDE ))
284+
285+ @property
286+ def _start (self ) -> bool :
287+ """Return True if this button is held."""
288+ return bool (lib .SDL_GameControllerGetButton (self .sdl_controller_p , lib .SDL_CONTROLLER_BUTTON_START ))
289+
290+ @property
291+ def _left_stick (self ) -> bool :
292+ """Return True if this button is held."""
293+ return bool (lib .SDL_GameControllerGetButton (self .sdl_controller_p , lib .SDL_CONTROLLER_BUTTON_LEFTSTICK ))
294+
295+ @property
296+ def _right_stick (self ) -> bool :
297+ """Return True if this button is held."""
298+ return bool (lib .SDL_GameControllerGetButton (self .sdl_controller_p , lib .SDL_CONTROLLER_BUTTON_RIGHTSTICK ))
299+
300+ @property
301+ def _left_shoulder (self ) -> bool :
302+ """Return True if this button is held."""
303+ return bool (lib .SDL_GameControllerGetButton (self .sdl_controller_p , lib .SDL_CONTROLLER_BUTTON_LEFTSHOULDER ))
304+
305+ @property
306+ def _right_shoulder (self ) -> bool :
307+ """Return True if this button is held."""
308+ return bool (lib .SDL_GameControllerGetButton (self .sdl_controller_p , lib .SDL_CONTROLLER_BUTTON_RIGHTSHOULDER ))
309+
310+ @property
311+ def _dpad (self ) -> Tuple [Literal [- 1 , 0 , 1 ], Literal [- 1 , 0 , 1 ]]:
312+ return (
313+ lib .SDL_GameControllerGetButton (self .sdl_controller_p , lib .SDL_CONTROLLER_BUTTON_DPAD_RIGHT )
314+ - lib .SDL_GameControllerGetButton (self .sdl_controller_p , lib .SDL_CONTROLLER_BUTTON_DPAD_LEFT ),
315+ lib .SDL_GameControllerGetButton (self .sdl_controller_p , lib .SDL_CONTROLLER_BUTTON_DPAD_DOWN )
316+ - lib .SDL_GameControllerGetButton (self .sdl_controller_p , lib .SDL_CONTROLLER_BUTTON_DPAD_UP ),
317+ )
318+
319+ @property
320+ def _misc1 (self ) -> bool :
321+ """Return True if this button is held."""
322+ return bool (lib .SDL_GameControllerGetButton (self .sdl_controller_p , lib .SDL_CONTROLLER_BUTTON_MISC1 ))
323+
324+ @property
325+ def _paddle1 (self ) -> bool :
326+ """Return True if this button is held."""
327+ return bool (lib .SDL_GameControllerGetButton (self .sdl_controller_p , lib .SDL_CONTROLLER_BUTTON_PADDLE1 ))
328+
329+ @property
330+ def _paddle2 (self ) -> bool :
331+ """Return True if this button is held."""
332+ return bool (lib .SDL_GameControllerGetButton (self .sdl_controller_p , lib .SDL_CONTROLLER_BUTTON_PADDLE2 ))
333+
334+ @property
335+ def _paddle3 (self ) -> bool :
336+ """Return True if this button is held."""
337+ return bool (lib .SDL_GameControllerGetButton (self .sdl_controller_p , lib .SDL_CONTROLLER_BUTTON_PADDLE3 ))
338+
339+ @property
340+ def _paddle4 (self ) -> bool :
341+ """Return True if this button is held."""
342+ return bool (lib .SDL_GameControllerGetButton (self .sdl_controller_p , lib .SDL_CONTROLLER_BUTTON_PADDLE4 ))
343+
344+ @property
345+ def _touchpad (self ) -> bool :
346+ """Return True if this button is held."""
347+ return bool (lib .SDL_GameControllerGetButton (self .sdl_controller_p , lib .SDL_CONTROLLER_BUTTON_TOUCHPAD ))
348+
349+
350+ def _get_number () -> int :
107351 """Return the number of attached joysticks."""
108352 tcod .sdl .sys .init (tcod .sdl .sys .Subsystem .JOYSTICK )
109353 return _check (lib .SDL_NumJoysticks ())
110354
111355
112356def get_joysticks () -> List [Joystick ]:
113357 """Return a list of all connected joystick devices."""
114- return [Joystick (i ) for i in range (get_number ())]
358+ return [Joystick ._open (i ) for i in range (_get_number ())]
359+
360+
361+ def get_controllers () -> List [GameController ]:
362+ """Return a list of all connected game controllers.
363+
364+ This ignores joysticks without a game controller mapping.
365+ """
366+ return [GameController ._open (i ) for i in range (_get_number ()) if lib .SDL_IsGameController (i )]
115367
116368
117- def event_state (new_state : Optional [bool ] = None ) -> bool :
369+ def get_all () -> List [Union [Joystick , GameController ]]:
370+ """Return a list of all connected joystick or controller devices.
371+
372+ If the joystick has a controller mapping then it is returned as a :any:`GameController`.
373+ Otherwise it is returned as a :any:`Joystick`.
374+ """
375+ return [GameController ._open (i ) if lib .SDL_IsGameController (i ) else Joystick ._open (i ) for i in range (_get_number ())]
376+
377+
378+ def joystick_event_state (new_state : Optional [bool ] = None ) -> bool :
118379 """Check or set joystick event polling.
119380
120381 .. seealso::
121382 https://wiki.libsdl.org/SDL_JoystickEventState
122383 """
123384 _OPTIONS = {None : lib .SDL_QUERY , False : lib .SDL_IGNORE , True : lib .SDL_ENABLE }
124385 return bool (_check (lib .SDL_JoystickEventState (_OPTIONS [new_state ])))
386+
387+
388+ def controller_event_state (new_state : Optional [bool ] = None ) -> bool :
389+ """Check or set game controller event polling.
390+
391+ .. seealso::
392+ https://wiki.libsdl.org/SDL_GameControllerEventState
393+ """
394+ _OPTIONS = {None : lib .SDL_QUERY , False : lib .SDL_IGNORE , True : lib .SDL_ENABLE }
395+ return bool (_check (lib .SDL_GameControllerEventState (_OPTIONS [new_state ])))
0 commit comments