|
1 | | -from __future__ import annotations |
2 | | - |
3 | | -import logging |
4 | | -import sys as _sys |
5 | | -from dataclasses import dataclass |
| 1 | +"""tcod.sdl package.""" |
6 | 2 | from pkgutil import extend_path |
7 | | -from types import TracebackType |
8 | | -from typing import Any, Callable, TypeVar |
9 | | - |
10 | | -from tcod.loader import ffi, lib |
11 | 3 |
|
12 | 4 | __path__ = extend_path(__path__, __name__) |
13 | | - |
14 | | -T = TypeVar("T") |
15 | | - |
16 | | -logger = logging.getLogger("tcod.sdl") |
17 | | - |
18 | | -_LOG_PRIORITY = { |
19 | | - 1: logging.DEBUG, # SDL_LOG_PRIORITY_VERBOSE |
20 | | - 2: logging.DEBUG, # SDL_LOG_PRIORITY_DEBUG |
21 | | - 3: logging.INFO, # SDL_LOG_PRIORITY_INFO |
22 | | - 4: logging.WARNING, # SDL_LOG_PRIORITY_WARN |
23 | | - 5: logging.ERROR, # SDL_LOG_PRIORITY_ERROR |
24 | | - 6: logging.CRITICAL, # SDL_LOG_PRIORITY_CRITICAL |
25 | | -} |
26 | | - |
27 | | -_LOG_CATEGORY = { |
28 | | - int(lib.SDL_LOG_CATEGORY_APPLICATION): "APPLICATION", |
29 | | - int(lib.SDL_LOG_CATEGORY_ERROR): "ERROR", |
30 | | - int(lib.SDL_LOG_CATEGORY_ASSERT): "ASSERT", |
31 | | - int(lib.SDL_LOG_CATEGORY_SYSTEM): "SYSTEM", |
32 | | - int(lib.SDL_LOG_CATEGORY_AUDIO): "AUDIO", |
33 | | - int(lib.SDL_LOG_CATEGORY_VIDEO): "VIDEO", |
34 | | - int(lib.SDL_LOG_CATEGORY_RENDER): "RENDER", |
35 | | - int(lib.SDL_LOG_CATEGORY_INPUT): "INPUT", |
36 | | - int(lib.SDL_LOG_CATEGORY_TEST): "TEST", |
37 | | - int(lib.SDL_LOG_CATEGORY_CUSTOM): "", |
38 | | -} |
39 | | - |
40 | | - |
41 | | -@dataclass |
42 | | -class _UnraisableHookArgs: |
43 | | - exc_type: type[BaseException] |
44 | | - exc_value: BaseException | None |
45 | | - exc_traceback: TracebackType | None |
46 | | - err_msg: str | None |
47 | | - object: object |
48 | | - |
49 | | - |
50 | | -class _ProtectedContext: |
51 | | - def __init__(self, obj: object = None) -> None: |
52 | | - self.obj = obj |
53 | | - |
54 | | - def __enter__(self) -> None: |
55 | | - pass |
56 | | - |
57 | | - def __exit__( |
58 | | - self, exc_type: type[BaseException] | None, value: BaseException | None, traceback: TracebackType | None |
59 | | - ) -> bool: |
60 | | - if exc_type is None: |
61 | | - return False |
62 | | - if _sys.version_info < (3, 8): |
63 | | - return False |
64 | | - _sys.unraisablehook(_UnraisableHookArgs(exc_type, value, traceback, None, self.obj)) # type: ignore[arg-type] |
65 | | - return True |
66 | | - |
67 | | - |
68 | | -@ffi.def_extern() # type: ignore |
69 | | -def _sdl_log_output_function(_userdata: None, category: int, priority: int, message_p: Any) -> None: # noqa: ANN401 |
70 | | - """Pass logs sent by SDL to Python's logging system.""" |
71 | | - message = str(ffi.string(message_p), encoding="utf-8") |
72 | | - logger.log(_LOG_PRIORITY.get(priority, 0), "%s:%s", _LOG_CATEGORY.get(category, ""), message) |
73 | | - |
74 | | - |
75 | | -def _get_error() -> str: |
76 | | - """Return a message from SDL_GetError as a Unicode string.""" |
77 | | - return str(ffi.string(lib.SDL_GetError()), encoding="utf-8") |
78 | | - |
79 | | - |
80 | | -def _check(result: int) -> int: |
81 | | - """Check if an SDL function returned without errors, and raise an exception if it did.""" |
82 | | - if result < 0: |
83 | | - raise RuntimeError(_get_error()) |
84 | | - return result |
85 | | - |
86 | | - |
87 | | -def _check_p(result: Any) -> Any: |
88 | | - """Check if an SDL function returned NULL, and raise an exception if it did.""" |
89 | | - if not result: |
90 | | - raise RuntimeError(_get_error()) |
91 | | - return result |
92 | | - |
93 | | - |
94 | | -if lib._sdl_log_output_function: |
95 | | - lib.SDL_LogSetOutputFunction(lib._sdl_log_output_function, ffi.NULL) |
96 | | - if __debug__: |
97 | | - lib.SDL_LogSetAllPriority(lib.SDL_LOG_PRIORITY_VERBOSE) |
98 | | - |
99 | | - |
100 | | -def _compiled_version() -> tuple[int, int, int]: |
101 | | - return int(lib.SDL_MAJOR_VERSION), int(lib.SDL_MINOR_VERSION), int(lib.SDL_PATCHLEVEL) |
102 | | - |
103 | | - |
104 | | -def _linked_version() -> tuple[int, int, int]: |
105 | | - sdl_version = ffi.new("SDL_version*") |
106 | | - lib.SDL_GetVersion(sdl_version) |
107 | | - return int(sdl_version.major), int(sdl_version.minor), int(sdl_version.patch) |
108 | | - |
109 | | - |
110 | | -def _version_at_least(required: tuple[int, int, int]) -> None: |
111 | | - """Raise an error if the compiled version is less than required. Used to guard recently defined SDL functions.""" |
112 | | - if required <= _compiled_version(): |
113 | | - return |
114 | | - msg = f"This feature requires SDL version {required}, but tcod was compiled with version {_compiled_version()}" |
115 | | - raise RuntimeError(msg) |
116 | | - |
117 | | - |
118 | | -def _required_version(required: tuple[int, int, int]) -> Callable[[T], T]: |
119 | | - if not lib: # Read the docs mock object. |
120 | | - return lambda x: x |
121 | | - if required <= _compiled_version(): |
122 | | - return lambda x: x |
123 | | - |
124 | | - def replacement(*_args: Any, **_kwargs: Any) -> Any: |
125 | | - msg = f"This feature requires SDL version {required}, but tcod was compiled with version {_compiled_version()}" |
126 | | - raise RuntimeError(msg) |
127 | | - |
128 | | - return lambda x: replacement # type: ignore[return-value] |
0 commit comments