2323
2424import enum
2525import warnings
26- from typing import Any , Callable , Dict , Generic , Iterator , Mapping , NamedTuple , Optional , Tuple , TypeVar , Union
26+ from typing import Any , Callable , Dict , Generic , Iterator , Mapping , NamedTuple , Optional , Tuple , Type , TypeVar , Union
2727
2828import numpy as np
2929from numpy .typing import NDArray
@@ -238,7 +238,7 @@ def __init__(self, type: Optional[str] = None):
238238 self .sdl_event = None
239239
240240 @classmethod
241- def from_sdl_event (cls , sdl_event : Any ) -> Any :
241+ def from_sdl_event (cls , sdl_event : Any ) -> Event :
242242 """Return a class instance from a python-cffi 'SDL_Event*' pointer."""
243243 raise NotImplementedError ()
244244
@@ -739,7 +739,7 @@ def __str__(self) -> str:
739739 return "<Undefined>"
740740
741741
742- _SDL_TO_CLASS_TABLE : Dict [int , Any ] = {
742+ _SDL_TO_CLASS_TABLE : Dict [int , Type [ Event ] ] = {
743743 lib .SDL_QUIT : Quit ,
744744 lib .SDL_KEYDOWN : KeyDown ,
745745 lib .SDL_KEYUP : KeyUp ,
@@ -752,6 +752,13 @@ def __str__(self) -> str:
752752}
753753
754754
755+ def _parse_event (sdl_event : Any ) -> Event :
756+ """Convert a C SDL_Event* type into a tcod Event sub-class."""
757+ if sdl_event .type not in _SDL_TO_CLASS_TABLE :
758+ return Undefined .from_sdl_event (sdl_event )
759+ return _SDL_TO_CLASS_TABLE [sdl_event .type ].from_sdl_event (sdl_event )
760+
761+
755762def get () -> Iterator [Any ]:
756763 """Return an iterator for all pending events.
757764
@@ -1065,10 +1072,63 @@ def get_mouse_state() -> MouseState:
10651072
10661073
10671074@ffi .def_extern () # type: ignore
1068- def _pycall_event_watch (userdata : Any , sdl_event : Any ) -> int :
1075+ def _sdl_event_watcher (userdata : Any , sdl_event : Any ) -> int :
1076+ callback : Callable [[Event ], None ] = ffi .from_handle (userdata )
1077+ callback (_parse_event (sdl_event ))
10691078 return 0
10701079
10711080
1081+ _EventCallback = TypeVar ("_EventCallback" , bound = Callable [[Event ], None ])
1082+ _event_watch_handles : Dict [Callable [[Event ], None ], Any ] = {} # Callbacks and their FFI handles.
1083+
1084+
1085+ def add_watch (callback : _EventCallback ) -> _EventCallback :
1086+ """Add a callback for watching events.
1087+
1088+ This function can be called with the callback to register, or be used as a decorator.
1089+
1090+ Callbacks added as event watchers can later be removed with :any:`tcod.event.remove_watch`.
1091+
1092+ Args:
1093+ callback (Callable[[Event], None]):
1094+ A function which accepts :any:`Event` parameters.
1095+
1096+ Example::
1097+
1098+ import tcod.event
1099+
1100+ @tcod.event.add_watch
1101+ def handle_events(event: tcod.event.Event) -> None:
1102+ if isinstance(event, tcod.event.KeyDown):
1103+ print(event)
1104+
1105+ .. versionadded:: 13.4
1106+ """
1107+ if callback in _event_watch_handles :
1108+ warnings .warn (f"{ callback } is already an active event watcher, nothing was added." , RuntimeWarning )
1109+ return callback
1110+ handle = _event_watch_handles [callback ] = ffi .new_handle (callback )
1111+ lib .SDL_AddEventWatch (lib ._sdl_event_watcher , handle )
1112+ return callback
1113+
1114+
1115+ def remove_watch (callback : Callable [[Event ], None ]) -> None :
1116+ """Remove a callback as an event wacher.
1117+
1118+ Args:
1119+ callback (Callable[[Event], None]):
1120+ A function which has been previously registered with :any:`tcod.event.add_watch`.
1121+
1122+ .. versionadded:: 13.4
1123+ """
1124+ if callback not in _event_watch_handles :
1125+ warnings .warn (f"{ callback } is not an active event watcher, nothing was removed." , RuntimeWarning )
1126+ return
1127+ handle = _event_watch_handles [callback ]
1128+ lib .SDL_DelEventWatch (lib ._sdl_event_watcher , handle )
1129+ del _event_watch_handles [callback ]
1130+
1131+
10721132def get_keyboard_state () -> NDArray [np .bool_ ]:
10731133 """Return a boolean array with the current keyboard state.
10741134
0 commit comments