Skip to content

Commit d5f8663

Browse files
committed
Add basic joystick port of SDL2.
1 parent 8d9c72a commit d5f8663

File tree

6 files changed

+375
-1
lines changed

6 files changed

+375
-1
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ Changes relevant to the users of python-tcod are documented here.
44
This project adheres to [Semantic Versioning](https://semver.org/) since version `2.0.0`.
55

66
## [Unreleased]
7+
### Added
8+
- Ported SDL2 joystick handing as `tcod.sdl.joystick`.
9+
- New joystick related events.
710

811
## [13.7.0] - 2022-08-07
912
### Added

docs/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ Contents:
4040
tcod/tileset
4141
libtcodpy
4242
sdl/audio
43+
sdl/joystick
4344
sdl/render
4445
sdl/mouse
4546
sdl/video

docs/sdl/joystick.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
tcod.sdl.joystick - SDL Joystick Support
2+
========================================
3+
4+
.. automodule:: tcod.sdl.joystick
5+
:members:
6+
:member-order: bysource

examples/eventget.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
from typing import List
99

1010
import tcod
11+
import tcod.sdl.joystick
12+
import tcod.sdl.sys
1113

1214
WIDTH, HEIGHT = 720, 480
1315

@@ -17,6 +19,7 @@ def main() -> None:
1719

1820
event_log: List[str] = []
1921
motion_desc = ""
22+
joysticks = tcod.sdl.joystick.get_joysticks()
2023

2124
with tcod.context.new(width=WIDTH, height=HEIGHT) as context:
2225
console = context.new_console()

tcod/event.py

Lines changed: 243 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@
9393
from tcod.event_constants import * # noqa: F4
9494
from tcod.event_constants import KMOD_ALT, KMOD_CTRL, KMOD_GUI, KMOD_SHIFT
9595
from tcod.loader import ffi, lib
96+
from tcod.sdl.joystick import _HAT_DIRECTIONS
9697

9798
T = TypeVar("T")
9899

@@ -303,7 +304,7 @@ def from_sdl_event(cls, sdl_event: Any) -> Event:
303304
raise NotImplementedError()
304305

305306
def __str__(self) -> str:
306-
return "<type=%r>" % (self.type,)
307+
return f"<type={self.type!r}>"
307308

308309

309310
class Quit(Event):
@@ -779,6 +780,199 @@ def __str__(self) -> str:
779780
)
780781

781782

783+
class JoystickEvent(Event):
784+
"""A base class for joystick events.
785+
786+
.. versionadded:: Unreleased
787+
"""
788+
789+
def __init__(self, type: str, which: int):
790+
super().__init__(type)
791+
self.which = which
792+
"""The ID of the joystick this event is for."""
793+
794+
def __repr__(self) -> str:
795+
return f"tcod.event.{self.__class__.__name__}" f"(type={self.type!r}, which={self.which})"
796+
797+
def __str__(self) -> str:
798+
prefix = super().__str__().strip("<>")
799+
return f"<{prefix}, which={self.which}>"
800+
801+
802+
class JoystickAxis(JoystickEvent):
803+
"""When a joystick axis changes in value.
804+
805+
.. versionadded:: Unreleased
806+
807+
.. seealso::
808+
:any:`tcod.sdl.joystick`
809+
"""
810+
811+
which: int
812+
"""The ID of the joystick this event is for."""
813+
814+
def __init__(self, type: str, which: int, axis: int, value: int):
815+
super().__init__(type, which)
816+
self.axis = axis
817+
"""The index of the changed axis."""
818+
self.value = value
819+
"""The raw value of the axis in the range -32768 to 32767."""
820+
821+
@classmethod
822+
def from_sdl_event(cls, sdl_event: Any) -> JoystickAxis:
823+
return cls("JOYAXISMOTION", sdl_event.jaxis.which, sdl_event.jaxis.axis, sdl_event.jaxis.value)
824+
825+
def __repr__(self) -> str:
826+
return (
827+
f"tcod.event.{self.__class__.__name__}"
828+
f"(type={self.type!r}, which={self.which}, axis={self.axis}, value={self.value})"
829+
)
830+
831+
def __str__(self) -> str:
832+
prefix = super().__str__().strip("<>")
833+
return f"<{prefix}, axis={self.axis}, value={self.value}>"
834+
835+
836+
class JoystickBall(JoystickEvent):
837+
"""When a joystick ball is moved.
838+
839+
.. versionadded:: Unreleased
840+
841+
.. seealso::
842+
:any:`tcod.sdl.joystick`
843+
"""
844+
845+
which: int
846+
"""The ID of the joystick this event is for."""
847+
848+
def __init__(self, type: str, which: int, ball: int, dx: int, dy: int):
849+
super().__init__(type, which)
850+
self.ball = ball
851+
"""The index of the moved ball."""
852+
self.dx = dx
853+
"""The X motion of the ball."""
854+
self.dy = dy
855+
"""The Y motion of the ball."""
856+
857+
@classmethod
858+
def from_sdl_event(cls, sdl_event: Any) -> JoystickBall:
859+
return cls(
860+
"JOYBALLMOTION", sdl_event.jball.which, sdl_event.jball.ball, sdl_event.jball.xrel, sdl_event.jball.yrel
861+
)
862+
863+
def __repr__(self) -> str:
864+
return (
865+
f"tcod.event.{self.__class__.__name__}"
866+
f"(type={self.type!r}, which={self.which}, ball={self.ball}, dx={self.dx}, dy={self.dy})"
867+
)
868+
869+
def __str__(self) -> str:
870+
prefix = super().__str__().strip("<>")
871+
return f"<{prefix}, ball={self.ball}, dx={self.dx}, dy={self.dy}>"
872+
873+
874+
class JoystickHat(JoystickEvent):
875+
"""When a joystick hat changes direction.
876+
877+
.. versionadded:: Unreleased
878+
879+
.. seealso::
880+
:any:`tcod.sdl.joystick`
881+
"""
882+
883+
which: int
884+
"""The ID of the joystick this event is for."""
885+
886+
def __init__(self, type: str, which: int, x: Literal[-1, 0, 1], y: Literal[-1, 0, 1]):
887+
super().__init__(type, which)
888+
self.x = x
889+
"""The new X direction of the hat."""
890+
self.y = y
891+
"""The new Y direction of the hat."""
892+
893+
@classmethod
894+
def from_sdl_event(cls, sdl_event: Any) -> JoystickHat:
895+
return cls("JOYHATMOTION", sdl_event.jhat.which, *_HAT_DIRECTIONS[sdl_event.jhat.hat])
896+
897+
def __repr__(self) -> str:
898+
return (
899+
f"tcod.event.{self.__class__.__name__}" f"(type={self.type!r}, which={self.which}, x={self.x}, y={self.y})"
900+
)
901+
902+
def __str__(self) -> str:
903+
prefix = super().__str__().strip("<>")
904+
return f"<{prefix}, x={self.x}, y={self.y}>"
905+
906+
907+
class JoystickButton(JoystickEvent):
908+
"""When a joystick button is pressed or released.
909+
910+
.. versionadded:: Unreleased
911+
912+
Example::
913+
914+
for event in tcod.event.get():
915+
match event:
916+
case JoystickButton(which=which, button=button, pressed=True):
917+
print(f"Pressed {button=} on controller {which}.")
918+
case JoystickButton(which=which, button=button, pressed=False):
919+
print(f"Released {button=} on controller {which}.")
920+
"""
921+
922+
which: int
923+
"""The ID of the joystick this event is for."""
924+
925+
def __init__(self, type: str, which: int, button: int):
926+
super().__init__(type, which)
927+
self.button = button
928+
"""The index of the button this event is for."""
929+
930+
@property
931+
def pressed(self) -> bool:
932+
"""True if the joystick button has been pressed, False when the button was released."""
933+
return self.type == "JOYBUTTONDOWN"
934+
935+
@classmethod
936+
def from_sdl_event(cls, sdl_event: Any) -> JoystickButton:
937+
type = {lib.SDL_JOYBUTTONDOWN: "JOYBUTTONDOWN", lib.SDL_JOYBUTTONUP: "JOYBUTTONUP"}[sdl_event.type]
938+
return cls(type, sdl_event.jbutton.which, sdl_event.jbutton.button)
939+
940+
def __repr__(self) -> str:
941+
return f"tcod.event.{self.__class__.__name__}" f"(type={self.type!r}, which={self.which}, button={self.button})"
942+
943+
def __str__(self) -> str:
944+
prefix = super().__str__().strip("<>")
945+
return f"<{prefix}, button={self.button}>"
946+
947+
948+
class JoystickDevice(JoystickEvent):
949+
"""An event for when a joystick is added or removed.
950+
951+
.. versionadded:: Unreleased
952+
953+
Example::
954+
955+
joysticks: dict[int, tcod.sdl.joystick.Joystick] = {}
956+
for event in tcod.event.get():
957+
match event:
958+
case tcod.event.JoystickDevice(type="JOYDEVICEADDED", which=device_id):
959+
new_joystick = tcod.sdl.joystick.Joystick(device_id)
960+
joysticks[new_joystick.id] = new_joystick
961+
case tcod.event.JoystickDevice(type="JOYDEVICEREMOVED", which=which):
962+
del joysticks[which]
963+
"""
964+
965+
which: int
966+
"""When type="JOYDEVICEADDED" this is the device ID.
967+
When type="JOYDEVICEREMOVED" this is the instance ID.
968+
"""
969+
970+
@classmethod
971+
def from_sdl_event(cls, sdl_event: Any) -> JoystickDevice:
972+
type = {lib.SDL_JOYDEVICEADDED: "JOYDEVICEADDED", lib.SDL_JOYDEVICEREMOVED: "JOYDEVICEREMOVED"}[sdl_event.type]
973+
return cls(type, sdl_event.jdevice.which)
974+
975+
782976
class Undefined(Event):
783977
"""This class is a place holder for SDL events without their own tcod.event
784978
class.
@@ -809,6 +1003,13 @@ def __str__(self) -> str:
8091003
lib.SDL_MOUSEWHEEL: MouseWheel,
8101004
lib.SDL_TEXTINPUT: TextInput,
8111005
lib.SDL_WINDOWEVENT: WindowEvent,
1006+
lib.SDL_JOYAXISMOTION: JoystickAxis,
1007+
lib.SDL_JOYBALLMOTION: JoystickBall,
1008+
lib.SDL_JOYHATMOTION: JoystickHat,
1009+
lib.SDL_JOYBUTTONDOWN: JoystickButton,
1010+
lib.SDL_JOYBUTTONUP: JoystickButton,
1011+
lib.SDL_JOYDEVICEADDED: JoystickDevice,
1012+
lib.SDL_JOYDEVICEREMOVED: JoystickDevice,
8121013
}
8131014

8141015

@@ -1076,6 +1277,41 @@ def ev_windowtakefocus(self, event: tcod.event.WindowEvent) -> Optional[T]:
10761277
def ev_windowhittest(self, event: tcod.event.WindowEvent) -> Optional[T]:
10771278
pass
10781279

1280+
def ev_joyaxismotion(self, event: tcod.event.JoystickAxis) -> Optional[T]:
1281+
"""
1282+
.. versionadded:: Unreleased
1283+
"""
1284+
1285+
def ev_joyballmotion(self, event: tcod.event.JoystickBall) -> Optional[T]:
1286+
"""
1287+
.. versionadded:: Unreleased
1288+
"""
1289+
1290+
def ev_joyhatmotion(self, event: tcod.event.JoystickHat) -> Optional[T]:
1291+
"""
1292+
.. versionadded:: Unreleased
1293+
"""
1294+
1295+
def ev_joybuttondown(self, event: tcod.event.JoystickButton) -> Optional[T]:
1296+
"""
1297+
.. versionadded:: Unreleased
1298+
"""
1299+
1300+
def ev_joybuttonup(self, event: tcod.event.JoystickButton) -> Optional[T]:
1301+
"""
1302+
.. versionadded:: Unreleased
1303+
"""
1304+
1305+
def ev_joydeviceadded(self, event: tcod.event.JoystickDevice) -> Optional[T]:
1306+
"""
1307+
.. versionadded:: Unreleased
1308+
"""
1309+
1310+
def ev_joydeviceremoved(self, event: tcod.event.JoystickDevice) -> Optional[T]:
1311+
"""
1312+
.. versionadded:: Unreleased
1313+
"""
1314+
10791315
def ev_(self, event: Any) -> Optional[T]:
10801316
pass
10811317

@@ -2326,6 +2562,12 @@ def __repr__(self) -> str:
23262562
"WindowEvent",
23272563
"WindowMoved",
23282564
"WindowResized",
2565+
"JoystickEvent",
2566+
"JoystickAxis",
2567+
"JoystickBall",
2568+
"JoystickHat",
2569+
"JoystickButton",
2570+
"JoystickDevice",
23292571
"Undefined",
23302572
"get",
23312573
"wait",

0 commit comments

Comments
 (0)