Skip to content

Commit 8a678fd

Browse files
committed
Add event watch functions.
1 parent ba2de75 commit 8a678fd

File tree

3 files changed

+67
-6
lines changed

3 files changed

+67
-6
lines changed

build_sdl.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,10 @@ def on_directive_handle(
207207
extern "Python" {
208208
// SDL_AudioCallback callback.
209209
void _sdl_audio_callback(void* userdata, Uint8* stream, int len);
210+
// SDL to Python log function.
210211
void _sdl_log_output_function(void *userdata, int category, SDL_LogPriority priority, const char *message);
212+
// Generic event watcher callback.
213+
int _sdl_event_watcher(void* userdata, SDL_Event* event);
211214
}
212215
"""
213216

tcod/cdef.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,5 @@ float _pycall_path_dest_only(int x1, int y1, int x2, int y2, void* user_data);
1616

1717
void _pycall_sdl_hook(struct SDL_Surface*);
1818

19-
int _pycall_event_watch(void* userdata, union SDL_Event* event);
20-
2119
void _pycall_cli_output(void* userdata, const char* output);
2220
}

tcod/event.py

Lines changed: 64 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323

2424
import enum
2525
import 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

2828
import numpy as np
2929
from 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+
755762
def 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+
10721132
def get_keyboard_state() -> NDArray[np.bool_]:
10731133
"""Return a boolean array with the current keyboard state.
10741134

0 commit comments

Comments
 (0)