"""A light-weight implementation of event handling built on calls to SDL.
Many event constants are derived directly from SDL.
For example: ``tcod.event.KeySym.UP`` and ``tcod.event.Scancode.A`` refer to
SDL's ``SDLK_UP`` and ``SDL_SCANCODE_A`` respectfully.
`See this table for all of SDL's keyboard constants.
<https://wiki.libsdl.org/SDL_Keycode>`_
Printing any event will tell you its attributes in a human readable format.
An events type attribute if omitted is just the classes name with all letters upper-case.
As a general guideline, you should use :any:`KeyboardEvent.sym` for command inputs,
and :any:`TextInput.text` for name entry fields.
Example::
import tcod
KEY_COMMANDS = {
tcod.event.KeySym.UP: "move N",
tcod.event.KeySym.DOWN: "move S",
tcod.event.KeySym.LEFT: "move W",
tcod.event.KeySym.RIGHT: "move E",
}
context = tcod.context.new()
while True:
console = context.new_console()
context.present(console, integer_scaling=True)
for event in tcod.event.wait():
context.convert_event(event) # Adds tile coordinates to mouse events.
if isinstance(event, tcod.event.Quit):
print(event)
raise SystemExit()
elif isinstance(event, tcod.event.KeyDown):
print(event) # Prints the Scancode and KeySym enums for this event.
if event.sym in KEY_COMMANDS:
print(f"Command: {KEY_COMMANDS[event.sym]}")
elif isinstance(event, tcod.event.MouseButtonDown):
print(event) # Prints the mouse button constant names for this event.
elif isinstance(event, tcod.event.MouseMotion):
print(event) # Prints the mouse button mask bits in a readable format.
else:
print(event) # Print any unhandled events.
Python 3.10 introduced `match statements <https://docs.python.org/3/tutorial/controlflow.html#match-statements>`_
which can be used to dispatch events more gracefully:
Example::
import tcod
KEY_COMMANDS = {
tcod.event.KeySym.UP: "move N",
tcod.event.KeySym.DOWN: "move S",
tcod.event.KeySym.LEFT: "move W",
tcod.event.KeySym.RIGHT: "move E",
}
context = tcod.context.new()
while True:
console = context.new_console()
context.present(console, integer_scaling=True)
for event in tcod.event.wait():
context.convert_event(event) # Adds tile coordinates to mouse events.
match event:
case tcod.event.Quit():
raise SystemExit()
case tcod.event.KeyDown(sym) if sym in KEY_COMMANDS:
print(f"Command: {KEY_COMMANDS[sym]}")
case tcod.event.KeyDown(sym=sym, scancode=scancode, mod=mod, repeat=repeat):
print(f"KeyDown: {sym=}, {scancode=}, {mod=}, {repeat=}")
case tcod.event.MouseButtonDown(button=button, pixel=pixel, tile=tile):
print(f"MouseButtonDown: {button=}, {pixel=}, {tile=}")
case tcod.event.MouseMotion(pixel=pixel, pixel_motion=pixel_motion, tile=tile, tile_motion=tile_motion):
print(f"MouseMotion: {pixel=}, {pixel_motion=}, {tile=}, {tile_motion=}")
case tcod.event.Event() as event:
print(event) # Show any unhandled events.
.. versionadded:: 8.4
"""
from __future__ import annotations
import enum
import warnings
from typing import Any, Callable, Final, Generic, Iterator, Mapping, NamedTuple, TypeVar
import numpy as np
from numpy.typing import NDArray
from typing_extensions import Literal
import tcod.event
import tcod.event_constants
import tcod.sdl.joystick
import tcod.sdl.sys
from tcod.cffi import ffi, lib
from tcod.event_constants import * # noqa: F403
from tcod.sdl.joystick import _HAT_DIRECTIONS
T = TypeVar("T")
class _ConstantsWithPrefix(Mapping[int, str]):
def __init__(self, constants: Mapping[int, str]) -> None:
self.constants = constants
def __getitem__(self, key: int) -> str:
return "tcod.event." + self.constants[key]
def __len__(self) -> int:
return len(self.constants)
def __iter__(self) -> Iterator[int]:
return iter(self.constants)
def _describe_bitmask(bits: int, table: Mapping[int, str], default: str = "0") -> str:
"""Return a bitmask in human readable form.
This is a private function, used internally.
`bits` is the bitmask to be represented.
`table` is a reverse lookup table.
`default` is returned when no other bits can be represented.
"""
result = []
for bit, name in table.items():
if bit & bits:
result.append(name)
if not result:
return default
return "|".join(result)
def _pixel_to_tile(x: float, y: float) -> tuple[float, float] | None:
"""Convert pixel coordinates to tile coordinates."""
if not lib.TCOD_ctx.engine:
return None
xy = ffi.new("double[2]", (x, y))
lib.TCOD_sys_pixel_to_tile(xy, xy + 1)
return xy[0], xy[1]
[docs]
class Point(NamedTuple):
"""A 2D position used for events with mouse coordinates.
.. seealso::
:any:`MouseMotion` :any:`MouseButtonDown` :any:`MouseButtonUp`
"""
x: int
"""A pixel or tile coordinate starting with zero as the left-most position."""
y: int
"""A pixel or tile coordinate starting with zero as the top-most position."""
def _verify_tile_coordinates(xy: Point | None) -> Point:
"""Check if an events tile coordinate is initialized and warn if not.
Always returns a valid Point object for backwards compatibility.
"""
if xy is not None:
return xy
warnings.warn(
"This events tile coordinates are uninitialized!"
"\nYou MUST pass this event to `Context.convert_event` before you can"
" read its tile attributes.",
RuntimeWarning,
stacklevel=3, # Called within other functions, never directly.
)
return Point(0, 0)
def _init_sdl_video() -> None:
"""Keyboard layout stuff needs SDL to be initialized first."""
if lib.SDL_WasInit(lib.SDL_INIT_VIDEO):
return
lib.SDL_InitSubSystem(lib.SDL_INIT_VIDEO)
[docs]
class Modifier(enum.IntFlag):
"""Keyboard modifier flags, a bit-field of held modifier keys.
Use `bitwise and` to check if a modifier key is held.
The following example shows some common ways of checking modifiers.
All non-zero return values are considered true.
Example::
>>> mod = tcod.event.Modifier(4098)
>>> mod & tcod.event.Modifier.SHIFT # Check if any shift key is held.
<Modifier.RSHIFT: 2>
>>> mod & tcod.event.Modifier.LSHIFT # Check if left shift key is held.
<Modifier.NONE: 0>
>>> not mod & tcod.event.Modifier.LSHIFT # Check if left shift key is NOT held.
True
>>> mod & tcod.event.Modifier.SHIFT and mod & tcod.event.Modifier.CTRL # Check if Shift+Control is held.
<Modifier.NONE: 0>
.. versionadded:: 12.3
"""
NONE = 0
LSHIFT = 1
"""Left shift."""
RSHIFT = 2
"""Right shift."""
SHIFT = LSHIFT | RSHIFT
"""LSHIFT | RSHIFT"""
LCTRL = 64
"""Left control."""
RCTRL = 128
"""Right control."""
CTRL = LCTRL | RCTRL
"""LCTRL | RCTRL"""
LALT = 256
"""Left alt."""
RALT = 512
"""Right alt."""
ALT = LALT | RALT
"""LALT | RALT"""
LGUI = 1024
"""Left meta key."""
RGUI = 2048
"""Right meta key."""
GUI = LGUI | RGUI
"""LGUI | RGUI"""
NUM = 4096
"""Numpad lock."""
CAPS = 8192
"""Caps lock."""
MODE = 16384
"""Alt graph."""
class MouseButton(enum.IntEnum):
"""An enum for mouse buttons.
.. versionadded:: 16.1
"""
LEFT = 1
"""Left mouse button."""
MIDDLE = 2
"""Middle mouse button."""
RIGHT = 3
"""Right mouse button."""
X1 = 4
"""Back mouse button."""
X2 = 5
"""Forward mouse button."""
def __repr__(self) -> str:
return f"{self.__class__.__name__}.{self.name}"
class MouseButtonMask(enum.IntFlag):
"""A mask enum for held mouse buttons.
.. versionadded:: 16.1
"""
LEFT = 0x1
"""Left mouse button is held."""
MIDDLE = 0x2
"""Middle mouse button is held."""
RIGHT = 0x4
"""Right mouse button is held."""
X1 = 0x8
"""Back mouse button is held."""
X2 = 0x10
"""Forward mouse button is held."""
def __repr__(self) -> str:
if self.value == 0:
return f"{self.__class__.__name__}(0)"
return "|".join(f"{self.__class__.__name__}.{self.__class__(bit).name}" for bit in self.__class__ if bit & self)
[docs]
class Event:
"""The base event class.
Attributes:
type (str): This events type.
sdl_event: When available, this holds a python-cffi 'SDL_Event*'
pointer. All sub-classes have this attribute.
"""
def __init__(self, type: str | None = None) -> None:
if type is None:
type = self.__class__.__name__.upper()
self.type: Final = type
self.sdl_event = None
[docs]
@classmethod
def from_sdl_event(cls, sdl_event: Any) -> Event:
"""Return a class instance from a python-cffi 'SDL_Event*' pointer."""
raise NotImplementedError()
def __str__(self) -> str:
return f"<type={self.type!r}>"
[docs]
class Quit(Event):
"""An application quit request event.
For more info on when this event is triggered see:
https://wiki.libsdl.org/SDL_EventType#SDL_QUIT
Attributes:
type (str): Always "QUIT".
"""
[docs]
@classmethod
def from_sdl_event(cls, sdl_event: Any) -> Quit:
self = cls()
self.sdl_event = sdl_event
return self
def __repr__(self) -> str:
return f"tcod.event.{self.__class__.__name__}()"
[docs]
class KeyboardEvent(Event):
"""Base keyboard event.
Attributes:
type (str): Will be "KEYDOWN" or "KEYUP", depending on the event.
scancode (Scancode): The keyboard scan-code, this is the physical location
of the key on the keyboard rather than the keys symbol.
sym (KeySym): The keyboard symbol.
mod (Modifier): A bitmask of the currently held modifier keys.
For example, if shift is held then
``event.mod & tcod.event.Modifier.SHIFT`` will evaluate to a true
value.
repeat (bool): True if this event exists because of key repeat.
.. versionchanged:: 12.5
`scancode`, `sym`, and `mod` now use their respective enums.
"""
def __init__(self, scancode: int, sym: int, mod: int, repeat: bool = False) -> None:
super().__init__()
self.scancode = Scancode(scancode)
self.sym = KeySym(sym)
self.mod = Modifier(mod)
self.repeat = repeat
[docs]
@classmethod
def from_sdl_event(cls, sdl_event: Any) -> Any:
keysym = sdl_event.key.keysym
self = cls(keysym.scancode, keysym.sym, keysym.mod, bool(sdl_event.key.repeat))
self.sdl_event = sdl_event
return self
def __repr__(self) -> str:
return "tcod.event.{}(scancode={!r}, sym={!r}, mod={!r}{})".format(
self.__class__.__name__,
self.scancode,
self.sym,
self.mod,
", repeat=True" if self.repeat else "",
)
def __str__(self) -> str:
return self.__repr__().replace("tcod.event.", "")
[docs]
class KeyDown(KeyboardEvent):
pass
[docs]
class KeyUp(KeyboardEvent):
pass
class MouseState(Event):
"""Mouse state.
Attributes:
type (str): Always "MOUSESTATE".
position (Point): The position coordinates of the mouse.
tile (Point): The integer tile coordinates of the mouse on the screen.
state (int): A bitmask of which mouse buttons are currently held.
Will be a combination of the following names:
* tcod.event.BUTTON_LMASK
* tcod.event.BUTTON_MMASK
* tcod.event.BUTTON_RMASK
* tcod.event.BUTTON_X1MASK
* tcod.event.BUTTON_X2MASK
.. versionadded:: 9.3
.. versionchanged:: 15.0
Renamed `pixel` attribute to `position`.
"""
def __init__(
self,
position: tuple[int, int] = (0, 0),
tile: tuple[int, int] | None = (0, 0),
state: int = 0,
) -> None:
super().__init__()
self.position = Point(*position)
self._tile = Point(*tile) if tile is not None else None
self.state = state
@property
def pixel(self) -> Point:
warnings.warn(
"The mouse.pixel attribute is deprecated. Use mouse.position instead.",
DeprecationWarning,
stacklevel=2,
)
return self.position
@pixel.setter
def pixel(self, value: Point) -> None:
self.position = value
@property
def tile(self) -> Point:
warnings.warn(
"The mouse.tile attribute is deprecated. Use mouse.position of the event returned by context.convert_event instead.",
DeprecationWarning,
stacklevel=2,
)
return _verify_tile_coordinates(self._tile)
@tile.setter
def tile(self, xy: tuple[int, int]) -> None:
self._tile = Point(*xy)
def __repr__(self) -> str:
return f"tcod.event.{self.__class__.__name__}(position={tuple(self.position)!r}, tile={tuple(self.tile)!r}, state={MouseButtonMask(self.state)})"
def __str__(self) -> str:
return ("<%s, position=(x=%i, y=%i), tile=(x=%i, y=%i), state=%s>") % (
super().__str__().strip("<>"),
*self.position,
*self.tile,
MouseButtonMask(self.state),
)
[docs]
class MouseMotion(MouseState):
"""Mouse motion event.
Attributes:
type (str): Always "MOUSEMOTION".
position (Point): The pixel coordinates of the mouse.
motion (Point): The pixel delta.
tile (Point): The integer tile coordinates of the mouse on the screen.
tile_motion (Point): The integer tile delta.
state (int): A bitmask of which mouse buttons are currently held.
Will be a combination of the following names:
* tcod.event.BUTTON_LMASK
* tcod.event.BUTTON_MMASK
* tcod.event.BUTTON_RMASK
* tcod.event.BUTTON_X1MASK
* tcod.event.BUTTON_X2MASK
.. versionchanged:: 15.0
Renamed `pixel` attribute to `position`.
Renamed `pixel_motion` attribute to `motion`.
"""
def __init__(
self,
position: tuple[int, int] = (0, 0),
motion: tuple[int, int] = (0, 0),
tile: tuple[int, int] | None = (0, 0),
tile_motion: tuple[int, int] | None = (0, 0),
state: int = 0,
) -> None:
super().__init__(position, tile, state)
self.motion = Point(*motion)
self._tile_motion = Point(*tile_motion) if tile_motion is not None else None
@property
def pixel_motion(self) -> Point:
warnings.warn(
"The mouse.pixel_motion attribute is deprecated. Use mouse.motion instead.",
DeprecationWarning,
stacklevel=2,
)
return self.motion
@pixel_motion.setter
def pixel_motion(self, value: Point) -> None:
warnings.warn(
"The mouse.pixel_motion attribute is deprecated. Use mouse.motion instead.",
DeprecationWarning,
stacklevel=2,
)
self.motion = value
@property
def tile_motion(self) -> Point:
warnings.warn(
"The mouse.tile_motion attribute is deprecated."
" Use mouse.motion of the event returned by context.convert_event instead.",
DeprecationWarning,
stacklevel=2,
)
return _verify_tile_coordinates(self._tile_motion)
@tile_motion.setter
def tile_motion(self, xy: tuple[int, int]) -> None:
warnings.warn(
"The mouse.tile_motion attribute is deprecated."
" Use mouse.motion of the event returned by context.convert_event instead.",
DeprecationWarning,
stacklevel=2,
)
self._tile_motion = Point(*xy)
[docs]
@classmethod
def from_sdl_event(cls, sdl_event: Any) -> MouseMotion:
motion = sdl_event.motion
pixel = motion.x, motion.y
pixel_motion = motion.xrel, motion.yrel
subtile = _pixel_to_tile(*pixel)
if subtile is None:
self = cls(pixel, pixel_motion, None, None, motion.state)
else:
tile = int(subtile[0]), int(subtile[1])
prev_pixel = pixel[0] - pixel_motion[0], pixel[1] - pixel_motion[1]
prev_subtile = _pixel_to_tile(*prev_pixel) or (0, 0)
prev_tile = int(prev_subtile[0]), int(prev_subtile[1])
tile_motion = tile[0] - prev_tile[0], tile[1] - prev_tile[1]
self = cls(pixel, pixel_motion, tile, tile_motion, motion.state)
self.sdl_event = sdl_event
return self
def __repr__(self) -> str:
return f"tcod.event.{self.__class__.__name__}(position={tuple(self.position)!r}, motion={tuple(self.motion)!r}, tile={tuple(self.tile)!r}, tile_motion={tuple(self.tile_motion)!r}, state={MouseButtonMask(self.state)!r})"
def __str__(self) -> str:
return ("<%s, motion=(x=%i, y=%i), tile_motion=(x=%i, y=%i)>") % (
super().__str__().strip("<>"),
*self.motion,
*self.tile_motion,
)
[docs]
class MouseWheel(Event):
"""Mouse wheel event.
Attributes:
type (str): Always "MOUSEWHEEL".
x (int): Horizontal scrolling. A positive value means scrolling right.
y (int): Vertical scrolling. A positive value means scrolling away from
the user.
flipped (bool): If True then the values of `x` and `y` are the opposite
of their usual values. This depends on the settings of
the Operating System.
"""
def __init__(self, x: int, y: int, flipped: bool = False) -> None:
super().__init__()
self.x = x
self.y = y
self.flipped = flipped
[docs]
@classmethod
def from_sdl_event(cls, sdl_event: Any) -> MouseWheel:
wheel = sdl_event.wheel
self = cls(wheel.x, wheel.y, bool(wheel.direction))
self.sdl_event = sdl_event
return self
def __repr__(self) -> str:
return "tcod.event.%s(x=%i, y=%i%s)" % (
self.__class__.__name__,
self.x,
self.y,
", flipped=True" if self.flipped else "",
)
def __str__(self) -> str:
return "<%s, x=%i, y=%i, flipped=%r)" % (
super().__str__().strip("<>"),
self.x,
self.y,
self.flipped,
)
[docs]
class TextInput(Event):
"""SDL text input event.
Attributes:
type (str): Always "TEXTINPUT".
text (str): A Unicode string with the input.
"""
def __init__(self, text: str) -> None:
super().__init__()
self.text = text
[docs]
@classmethod
def from_sdl_event(cls, sdl_event: Any) -> TextInput:
self = cls(ffi.string(sdl_event.text.text, 32).decode("utf8"))
self.sdl_event = sdl_event
return self
def __repr__(self) -> str:
return f"tcod.event.{self.__class__.__name__}(text={self.text!r})"
def __str__(self) -> str:
return "<{}, text={!r})".format(super().__str__().strip("<>"), self.text)
[docs]
class WindowEvent(Event):
"""A window event."""
type: Final[ # type: ignore[misc] # Narrowing final type.
Literal[
"WindowShown",
"WindowHidden",
"WindowExposed",
"WindowMoved",
"WindowResized",
"WindowSizeChanged",
"WindowMinimized",
"WindowMaximized",
"WindowRestored",
"WindowEnter",
"WindowLeave",
"WindowFocusGained",
"WindowFocusLost",
"WindowClose",
"WindowTakeFocus",
"WindowHitTest",
]
]
"""The current window event. This can be one of various options."""
[docs]
@classmethod
def from_sdl_event(cls, sdl_event: Any) -> WindowEvent | Undefined:
if sdl_event.window.event not in cls.__WINDOW_TYPES:
return Undefined.from_sdl_event(sdl_event)
event_type: Final = cls.__WINDOW_TYPES[sdl_event.window.event]
self: WindowEvent
if sdl_event.window.event == lib.SDL_WINDOWEVENT_MOVED:
self = WindowMoved(sdl_event.window.data1, sdl_event.window.data2)
elif sdl_event.window.event in (
lib.SDL_WINDOWEVENT_RESIZED,
lib.SDL_WINDOWEVENT_SIZE_CHANGED,
):
self = WindowResized(event_type, sdl_event.window.data1, sdl_event.window.data2)
else:
self = cls(event_type)
self.sdl_event = sdl_event
return self
def __repr__(self) -> str:
return f"tcod.event.{self.__class__.__name__}(type={self.type!r})"
__WINDOW_TYPES: Final = {
lib.SDL_WINDOWEVENT_SHOWN: "WindowShown",
lib.SDL_WINDOWEVENT_HIDDEN: "WindowHidden",
lib.SDL_WINDOWEVENT_EXPOSED: "WindowExposed",
lib.SDL_WINDOWEVENT_MOVED: "WindowMoved",
lib.SDL_WINDOWEVENT_RESIZED: "WindowResized",
lib.SDL_WINDOWEVENT_SIZE_CHANGED: "WindowSizeChanged",
lib.SDL_WINDOWEVENT_MINIMIZED: "WindowMinimized",
lib.SDL_WINDOWEVENT_MAXIMIZED: "WindowMaximized",
lib.SDL_WINDOWEVENT_RESTORED: "WindowRestored",
lib.SDL_WINDOWEVENT_ENTER: "WindowEnter",
lib.SDL_WINDOWEVENT_LEAVE: "WindowLeave",
lib.SDL_WINDOWEVENT_FOCUS_GAINED: "WindowFocusGained",
lib.SDL_WINDOWEVENT_FOCUS_LOST: "WindowFocusLost",
lib.SDL_WINDOWEVENT_CLOSE: "WindowClose",
lib.SDL_WINDOWEVENT_TAKE_FOCUS: "WindowTakeFocus",
lib.SDL_WINDOWEVENT_HIT_TEST: "WindowHitTest",
}
[docs]
class WindowMoved(WindowEvent):
"""Window moved event.
Attributes:
x (int): Movement on the x-axis.
y (int): Movement on the y-axis.
"""
type: Final[Literal["WINDOWMOVED"]] # type: ignore[assignment,misc]
"""Always "WINDOWMOVED"."""
def __init__(self, x: int, y: int) -> None:
super().__init__(None)
self.x = x
self.y = y
def __repr__(self) -> str:
return f"tcod.event.{self.__class__.__name__}(type={self.type!r}, x={self.x!r}, y={self.y!r})"
def __str__(self) -> str:
return "<{}, x={!r}, y={!r})".format(
super().__str__().strip("<>"),
self.x,
self.y,
)
[docs]
class WindowResized(WindowEvent):
"""Window resized event.
Attributes:
width (int): The current width of the window.
height (int): The current height of the window.
"""
type: Final[Literal["WindowResized", "WindowSizeChanged"]] # type: ignore[misc]
"""WindowResized" or "WindowSizeChanged"""
def __init__(self, type: str, width: int, height: int) -> None:
super().__init__(type)
self.width = width
self.height = height
def __repr__(self) -> str:
return f"tcod.event.{self.__class__.__name__}(type={self.type!r}, width={self.width!r}, height={self.height!r})"
def __str__(self) -> str:
return "<{}, width={!r}, height={!r})".format(
super().__str__().strip("<>"),
self.width,
self.height,
)
[docs]
class JoystickEvent(Event):
"""A base class for joystick events.
.. versionadded:: 13.8
"""
def __init__(self, type: str, which: int) -> None:
super().__init__(type)
self.which = which
"""The ID of the joystick this event is for."""
@property
def joystick(self) -> tcod.sdl.joystick.Joystick:
if self.type == "JOYDEVICEADDED":
return tcod.sdl.joystick.Joystick._open(self.which)
return tcod.sdl.joystick.Joystick._from_instance_id(self.which)
def __repr__(self) -> str:
return f"tcod.event.{self.__class__.__name__}" f"(type={self.type!r}, which={self.which})"
def __str__(self) -> str:
prefix = super().__str__().strip("<>")
return f"<{prefix}, which={self.which}>"
[docs]
class JoystickAxis(JoystickEvent):
"""When a joystick axis changes in value.
.. versionadded:: 13.8
.. seealso::
:any:`tcod.sdl.joystick`
"""
which: int
"""The ID of the joystick this event is for."""
def __init__(self, type: str, which: int, axis: int, value: int) -> None:
super().__init__(type, which)
self.axis = axis
"""The index of the changed axis."""
self.value = value
"""The raw value of the axis in the range -32768 to 32767."""
[docs]
@classmethod
def from_sdl_event(cls, sdl_event: Any) -> JoystickAxis:
return cls("JOYAXISMOTION", sdl_event.jaxis.which, sdl_event.jaxis.axis, sdl_event.jaxis.value)
def __repr__(self) -> str:
return (
f"tcod.event.{self.__class__.__name__}"
f"(type={self.type!r}, which={self.which}, axis={self.axis}, value={self.value})"
)
def __str__(self) -> str:
prefix = super().__str__().strip("<>")
return f"<{prefix}, axis={self.axis}, value={self.value}>"
[docs]
class JoystickBall(JoystickEvent):
"""When a joystick ball is moved.
.. versionadded:: 13.8
.. seealso::
:any:`tcod.sdl.joystick`
"""
which: int
"""The ID of the joystick this event is for."""
def __init__(self, type: str, which: int, ball: int, dx: int, dy: int) -> None:
super().__init__(type, which)
self.ball = ball
"""The index of the moved ball."""
self.dx = dx
"""The X motion of the ball."""
self.dy = dy
"""The Y motion of the ball."""
[docs]
@classmethod
def from_sdl_event(cls, sdl_event: Any) -> JoystickBall:
return cls(
"JOYBALLMOTION", sdl_event.jball.which, sdl_event.jball.ball, sdl_event.jball.xrel, sdl_event.jball.yrel
)
def __repr__(self) -> str:
return (
f"tcod.event.{self.__class__.__name__}"
f"(type={self.type!r}, which={self.which}, ball={self.ball}, dx={self.dx}, dy={self.dy})"
)
def __str__(self) -> str:
prefix = super().__str__().strip("<>")
return f"<{prefix}, ball={self.ball}, dx={self.dx}, dy={self.dy}>"
[docs]
class JoystickHat(JoystickEvent):
"""When a joystick hat changes direction.
.. versionadded:: 13.8
.. seealso::
:any:`tcod.sdl.joystick`
"""
which: int
"""The ID of the joystick this event is for."""
def __init__(self, type: str, which: int, x: Literal[-1, 0, 1], y: Literal[-1, 0, 1]) -> None:
super().__init__(type, which)
self.x = x
"""The new X direction of the hat."""
self.y = y
"""The new Y direction of the hat."""
[docs]
@classmethod
def from_sdl_event(cls, sdl_event: Any) -> JoystickHat:
return cls("JOYHATMOTION", sdl_event.jhat.which, *_HAT_DIRECTIONS[sdl_event.jhat.hat])
def __repr__(self) -> str:
return (
f"tcod.event.{self.__class__.__name__}" f"(type={self.type!r}, which={self.which}, x={self.x}, y={self.y})"
)
def __str__(self) -> str:
prefix = super().__str__().strip("<>")
return f"<{prefix}, x={self.x}, y={self.y}>"
[docs]
class JoystickDevice(JoystickEvent):
"""An event for when a joystick is added or removed.
.. versionadded:: 13.8
Example::
joysticks: set[tcod.sdl.joystick.Joystick] = {}
for event in tcod.event.get():
match event:
case tcod.event.JoystickDevice(type="JOYDEVICEADDED", joystick=new_joystick):
joysticks.add(new_joystick)
case tcod.event.JoystickDevice(type="JOYDEVICEREMOVED", joystick=joystick):
joysticks.remove(joystick)
"""
type: Final[Literal["JOYDEVICEADDED", "JOYDEVICEREMOVED"]] # type: ignore[misc]
which: int
"""When type="JOYDEVICEADDED" this is the device ID.
When type="JOYDEVICEREMOVED" this is the instance ID.
"""
[docs]
@classmethod
def from_sdl_event(cls, sdl_event: Any) -> JoystickDevice:
type = {lib.SDL_JOYDEVICEADDED: "JOYDEVICEADDED", lib.SDL_JOYDEVICEREMOVED: "JOYDEVICEREMOVED"}[sdl_event.type]
return cls(type, sdl_event.jdevice.which)
[docs]
class ControllerEvent(Event):
"""Base class for controller events.
.. versionadded:: 13.8
"""
def __init__(self, type: str, which: int) -> None:
super().__init__(type)
self.which = which
"""The ID of the joystick this event is for."""
@property
def controller(self) -> tcod.sdl.joystick.GameController:
"""The :any:`GameController` for this event."""
if self.type == "CONTROLLERDEVICEADDED":
return tcod.sdl.joystick.GameController._open(self.which)
return tcod.sdl.joystick.GameController._from_instance_id(self.which)
def __repr__(self) -> str:
return f"tcod.event.{self.__class__.__name__}" f"(type={self.type!r}, which={self.which})"
def __str__(self) -> str:
prefix = super().__str__().strip("<>")
return f"<{prefix}, which={self.which}>"
[docs]
class ControllerAxis(ControllerEvent):
"""When a controller axis is moved.
.. versionadded:: 13.8
"""
type: Final[Literal["CONTROLLERAXISMOTION"]] # type: ignore[misc]
def __init__(self, type: str, which: int, axis: tcod.sdl.joystick.ControllerAxis, value: int) -> None:
super().__init__(type, which)
self.axis = axis
"""Which axis is being moved. One of :any:`ControllerAxis`."""
self.value = value
"""The new value of this events axis.
This will be -32768 to 32767 for all axes except for triggers which are 0 to 32767 instead."""
[docs]
@classmethod
def from_sdl_event(cls, sdl_event: Any) -> ControllerAxis:
return cls(
"CONTROLLERAXISMOTION",
sdl_event.caxis.which,
tcod.sdl.joystick.ControllerAxis(sdl_event.caxis.axis),
sdl_event.caxis.value,
)
def __repr__(self) -> str:
return (
f"tcod.event.{self.__class__.__name__}"
f"(type={self.type!r}, which={self.which}, axis={self.axis}, value={self.value})"
)
def __str__(self) -> str:
prefix = super().__str__().strip("<>")
return f"<{prefix}, axis={self.axis}, value={self.value}>"
[docs]
class ControllerDevice(ControllerEvent):
"""When a controller is added, removed, or remapped.
.. versionadded:: 13.8
"""
type: Final[Literal["CONTROLLERDEVICEADDED", "CONTROLLERDEVICEREMOVED", "CONTROLLERDEVICEREMAPPED"]] # type: ignore[misc]
[docs]
@classmethod
def from_sdl_event(cls, sdl_event: Any) -> ControllerDevice:
type = {
lib.SDL_CONTROLLERDEVICEADDED: "CONTROLLERDEVICEADDED",
lib.SDL_CONTROLLERDEVICEREMOVED: "CONTROLLERDEVICEREMOVED",
lib.SDL_CONTROLLERDEVICEREMAPPED: "CONTROLLERDEVICEREMAPPED",
}[sdl_event.type]
return cls(type, sdl_event.cdevice.which)
[docs]
class Undefined(Event):
"""This class is a place holder for SDL events without their own tcod.event class."""
def __init__(self) -> None:
super().__init__("")
[docs]
@classmethod
def from_sdl_event(cls, sdl_event: Any) -> Undefined:
self = cls()
self.sdl_event = sdl_event
return self
def __str__(self) -> str:
if self.sdl_event:
return "<Undefined sdl_event.type=%i>" % self.sdl_event.type
return "<Undefined>"
_SDL_TO_CLASS_TABLE: dict[int, type[Event]] = {
lib.SDL_QUIT: Quit,
lib.SDL_KEYDOWN: KeyDown,
lib.SDL_KEYUP: KeyUp,
lib.SDL_MOUSEMOTION: MouseMotion,
lib.SDL_MOUSEBUTTONDOWN: MouseButtonDown,
lib.SDL_MOUSEBUTTONUP: MouseButtonUp,
lib.SDL_MOUSEWHEEL: MouseWheel,
lib.SDL_TEXTINPUT: TextInput,
lib.SDL_WINDOWEVENT: WindowEvent,
lib.SDL_JOYAXISMOTION: JoystickAxis,
lib.SDL_JOYBALLMOTION: JoystickBall,
lib.SDL_JOYHATMOTION: JoystickHat,
lib.SDL_JOYBUTTONDOWN: JoystickButton,
lib.SDL_JOYBUTTONUP: JoystickButton,
lib.SDL_JOYDEVICEADDED: JoystickDevice,
lib.SDL_JOYDEVICEREMOVED: JoystickDevice,
lib.SDL_CONTROLLERAXISMOTION: ControllerAxis,
lib.SDL_CONTROLLERBUTTONDOWN: ControllerButton,
lib.SDL_CONTROLLERBUTTONUP: ControllerButton,
lib.SDL_CONTROLLERDEVICEADDED: ControllerDevice,
lib.SDL_CONTROLLERDEVICEREMOVED: ControllerDevice,
lib.SDL_CONTROLLERDEVICEREMAPPED: ControllerDevice,
}
def _parse_event(sdl_event: Any) -> Event:
"""Convert a C SDL_Event* type into a tcod Event sub-class."""
if sdl_event.type not in _SDL_TO_CLASS_TABLE:
return Undefined.from_sdl_event(sdl_event)
return _SDL_TO_CLASS_TABLE[sdl_event.type].from_sdl_event(sdl_event)
[docs]
def get() -> Iterator[Any]:
"""Return an iterator for all pending events.
Events are processed as the iterator is consumed.
Breaking out of, or discarding the iterator will leave the remaining events on the event queue.
It is also safe to call this function inside of a loop that is already handling events
(the event iterator is reentrant.)
"""
if not lib.SDL_WasInit(tcod.sdl.sys.Subsystem.EVENTS):
warnings.warn(
"Events polled before SDL was initialized.",
RuntimeWarning,
stacklevel=1,
)
return
sdl_event = ffi.new("SDL_Event*")
while lib.SDL_PollEvent(sdl_event):
if sdl_event.type in _SDL_TO_CLASS_TABLE:
yield _SDL_TO_CLASS_TABLE[sdl_event.type].from_sdl_event(sdl_event)
else:
yield Undefined.from_sdl_event(sdl_event)
[docs]
def wait(timeout: float | None = None) -> Iterator[Any]:
"""Block until events exist, then return an event iterator.
`timeout` is the maximum number of seconds to wait as a floating point
number with millisecond precision, or it can be None to wait forever.
Returns the same iterator as a call to :any:`tcod.event.get`.
This function is useful for simple games with little to no animations.
The following example sleeps whenever no events are queued:
Example::
context: tcod.context.Context # Context object initialized earlier.
while True: # Main game-loop.
console: tcod.console.Console # Console used for rendering.
... # Render the frame to `console` and then:
context.present(console) # Show the console to the display.
# The ordering to draw first before waiting for events is important.
for event in tcod.event.wait(): # Sleeps until the next events exist.
... # All events are handled at once before the next frame.
See :any:`tcod.event.get` examples for how different events are handled.
"""
if timeout is not None:
lib.SDL_WaitEventTimeout(ffi.NULL, int(timeout * 1000))
else:
lib.SDL_WaitEvent(ffi.NULL)
return get()
[docs]
class EventDispatch(Generic[T]):
'''Dispatches events to methods depending on the events type attribute.
To use this class, make a sub-class and override the relevant `ev_*` methods.
Then send events to the dispatch method.
.. versionchanged:: 11.12
This is now a generic class.
The type hints at the return value of :any:`dispatch` and the `ev_*` methods.
Example::
import tcod
MOVE_KEYS = { # key_symbol: (x, y)
# Arrow keys.
tcod.event.KeySym.LEFT: (-1, 0),
tcod.event.KeySym.RIGHT: (1, 0),
tcod.event.KeySym.UP: (0, -1),
tcod.event.KeySym.DOWN: (0, 1),
tcod.event.KeySym.HOME: (-1, -1),
tcod.event.KeySym.END: (-1, 1),
tcod.event.KeySym.PAGEUP: (1, -1),
tcod.event.KeySym.PAGEDOWN: (1, 1),
tcod.event.KeySym.PERIOD: (0, 0),
# Numpad keys.
tcod.event.KeySym.KP_1: (-1, 1),
tcod.event.KeySym.KP_2: (0, 1),
tcod.event.KeySym.KP_3: (1, 1),
tcod.event.KeySym.KP_4: (-1, 0),
tcod.event.KeySym.KP_5: (0, 0),
tcod.event.KeySym.KP_6: (1, 0),
tcod.event.KeySym.KP_7: (-1, -1),
tcod.event.KeySym.KP_8: (0, -1),
tcod.event.KeySym.KP_9: (1, -1),
tcod.event.KeySym.CLEAR: (0, 0), # Numpad `clear` key.
# Vi Keys.
tcod.event.KeySym.h: (-1, 0),
tcod.event.KeySym.j: (0, 1),
tcod.event.KeySym.k: (0, -1),
tcod.event.KeySym.l: (1, 0),
tcod.event.KeySym.y: (-1, -1),
tcod.event.KeySym.u: (1, -1),
tcod.event.KeySym.b: (-1, 1),
tcod.event.KeySym.n: (1, 1),
}
class State(tcod.event.EventDispatch[None]):
"""A state-based superclass that converts `events` into `commands`.
The configuration used to convert events to commands are hard-coded
in this example, but could be modified to be user controlled.
Subclasses will override the `cmd_*` methods with their own
functionality. There could be a subclass for every individual state
of your game.
"""
def ev_quit(self, event: tcod.event.Quit) -> None:
"""The window close button was clicked or Alt+F$ was pressed."""
print(event)
self.cmd_quit()
def ev_keydown(self, event: tcod.event.KeyDown) -> None:
"""A key was pressed."""
print(event)
if event.sym in MOVE_KEYS:
# Send movement keys to the cmd_move method with parameters.
self.cmd_move(*MOVE_KEYS[event.sym])
elif event.sym == tcod.event.KeySym.ESCAPE:
self.cmd_escape()
def ev_mousebuttondown(self, event: tcod.event.MouseButtonDown) -> None:
"""The window was clicked."""
print(event)
def ev_mousemotion(self, event: tcod.event.MouseMotion) -> None:
"""The mouse has moved within the window."""
print(event)
def cmd_move(self, x: int, y: int) -> None:
"""Intent to move: `x` and `y` is the direction, both may be 0."""
print("Command move: " + str((x, y)))
def cmd_escape(self) -> None:
"""Intent to exit this state."""
print("Command escape.")
self.cmd_quit()
def cmd_quit(self) -> None:
"""Intent to exit the game."""
print("Command quit.")
raise SystemExit()
root_console = libtcodpy.console_init_root(80, 60)
state = State()
while True:
libtcodpy.console_flush()
for event in tcod.event.wait():
state.dispatch(event)
'''
__slots__ = ()
[docs]
def dispatch(self, event: Any) -> T | None:
"""Send an event to an `ev_*` method.
`*` will be the `event.type` attribute converted to lower-case.
Values returned by `ev_*` calls will be returned by this function.
This value always defaults to None for any non-overridden method.
.. versionchanged:: 11.12
Now returns the return value of `ev_*` methods.
`event.type` values of None are deprecated.
"""
if event.type is None:
warnings.warn(
"`event.type` attribute should not be None.",
DeprecationWarning,
stacklevel=2,
)
return None
func_name = f"ev_{event.type.lower()}"
func: Callable[[Any], T | None] | None = getattr(self, func_name, None)
if func is None:
warnings.warn(f"{func_name} is missing from this EventDispatch object.", RuntimeWarning, stacklevel=2)
return None
return func(event)
def event_get(self) -> None:
for event in get():
self.dispatch(event)
def event_wait(self, timeout: float | None) -> None:
wait(timeout)
self.event_get()
[docs]
def ev_quit(self, event: tcod.event.Quit, /) -> T | None:
"""Called when the termination of the program is requested."""
[docs]
def ev_keydown(self, event: tcod.event.KeyDown, /) -> T | None:
"""Called when a keyboard key is pressed or repeated."""
[docs]
def ev_keyup(self, event: tcod.event.KeyUp, /) -> T | None:
"""Called when a keyboard key is released."""
[docs]
def ev_mousemotion(self, event: tcod.event.MouseMotion, /) -> T | None:
"""Called when the mouse is moved."""
[docs]
def ev_mousewheel(self, event: tcod.event.MouseWheel, /) -> T | None:
"""Called when the mouse wheel is scrolled."""
[docs]
def ev_textinput(self, event: tcod.event.TextInput, /) -> T | None:
"""Called to handle Unicode input."""
[docs]
def ev_windowshown(self, event: tcod.event.WindowEvent, /) -> T | None:
"""Called when the window is shown."""
[docs]
def ev_windowhidden(self, event: tcod.event.WindowEvent, /) -> T | None:
"""Called when the window is hidden."""
[docs]
def ev_windowexposed(self, event: tcod.event.WindowEvent, /) -> T | None:
"""Called when a window is exposed, and needs to be refreshed.
This usually means a call to :any:`libtcodpy.console_flush` is necessary.
"""
[docs]
def ev_windowmoved(self, event: tcod.event.WindowMoved, /) -> T | None:
"""Called when the window is moved."""
[docs]
def ev_windowresized(self, event: tcod.event.WindowResized, /) -> T | None:
"""Called when the window is resized."""
[docs]
def ev_windowsizechanged(self, event: tcod.event.WindowResized, /) -> T | None:
"""Called when the system or user changes the size of the window."""
[docs]
def ev_windowminimized(self, event: tcod.event.WindowEvent, /) -> T | None:
"""Called when the window is minimized."""
[docs]
def ev_windowmaximized(self, event: tcod.event.WindowEvent, /) -> T | None:
"""Called when the window is maximized."""
[docs]
def ev_windowrestored(self, event: tcod.event.WindowEvent, /) -> T | None:
"""Called when the window is restored."""
[docs]
def ev_windowenter(self, event: tcod.event.WindowEvent, /) -> T | None:
"""Called when the window gains mouse focus."""
[docs]
def ev_windowleave(self, event: tcod.event.WindowEvent, /) -> T | None:
"""Called when the window loses mouse focus."""
[docs]
def ev_windowfocusgained(self, event: tcod.event.WindowEvent, /) -> T | None:
"""Called when the window gains keyboard focus."""
[docs]
def ev_windowfocuslost(self, event: tcod.event.WindowEvent, /) -> T | None:
"""Called when the window loses keyboard focus."""
[docs]
def ev_windowclose(self, event: tcod.event.WindowEvent, /) -> T | None:
"""Called when the window manager requests the window to be closed."""
def ev_windowtakefocus(self, event: tcod.event.WindowEvent, /) -> T | None:
pass
def ev_windowhittest(self, event: tcod.event.WindowEvent, /) -> T | None:
pass
[docs]
def ev_joyaxismotion(self, event: tcod.event.JoystickAxis, /) -> T | None:
"""Called when a joystick analog is moved.
.. versionadded:: 13.8
"""
[docs]
def ev_joyballmotion(self, event: tcod.event.JoystickBall, /) -> T | None:
"""Called when a joystick ball is moved.
.. versionadded:: 13.8
"""
[docs]
def ev_joyhatmotion(self, event: tcod.event.JoystickHat, /) -> T | None:
"""Called when a joystick hat is moved.
.. versionadded:: 13.8
"""
[docs]
def ev_joydeviceadded(self, event: tcod.event.JoystickDevice, /) -> T | None:
"""Called when a joystick is added.
.. versionadded:: 13.8
"""
[docs]
def ev_joydeviceremoved(self, event: tcod.event.JoystickDevice, /) -> T | None:
"""Called when a joystick is removed.
.. versionadded:: 13.8
"""
[docs]
def ev_controlleraxismotion(self, event: tcod.event.ControllerAxis, /) -> T | None:
"""Called when a controller analog is moved.
.. versionadded:: 13.8
"""
[docs]
def ev_controllerdeviceadded(self, event: tcod.event.ControllerDevice, /) -> T | None:
"""Called when a standard controller is added.
.. versionadded:: 13.8
"""
[docs]
def ev_controllerdeviceremoved(self, event: tcod.event.ControllerDevice, /) -> T | None:
"""Called when a standard controller is removed.
.. versionadded:: 13.8
"""
[docs]
def ev_controllerdeviceremapped(self, event: tcod.event.ControllerDevice, /) -> T | None:
"""Called when a standard controller is remapped.
.. versionadded:: 13.8
"""
def ev_(self, event: Any, /) -> T | None:
pass
[docs]
def get_mouse_state() -> MouseState:
"""Return the current state of the mouse.
.. versionadded:: 9.3
"""
xy = ffi.new("int[2]")
buttons = lib.SDL_GetMouseState(xy, xy + 1)
tile = _pixel_to_tile(*xy)
if tile is None:
return MouseState((xy[0], xy[1]), None, buttons)
return MouseState((xy[0], xy[1]), (int(tile[0]), int(tile[1])), buttons)
@ffi.def_extern() # type: ignore
def _sdl_event_watcher(userdata: Any, sdl_event: Any) -> int:
callback: Callable[[Event], None] = ffi.from_handle(userdata)
callback(_parse_event(sdl_event))
return 0
_EventCallback = TypeVar("_EventCallback", bound=Callable[[Event], None])
_event_watch_handles: dict[Callable[[Event], None], Any] = {} # Callbacks and their FFI handles.
[docs]
def add_watch(callback: _EventCallback) -> _EventCallback:
"""Add a callback for watching events.
This function can be called with the callback to register, or be used as a decorator.
Callbacks added as event watchers can later be removed with :any:`tcod.event.remove_watch`.
.. warning::
How uncaught exceptions in a callback are handled is not currently defined by tcod.
They will likely be handled by :any:`sys.unraisablehook`.
This may be later changed to pass the exception to a :any:`tcod.event.get` or :any:`tcod.event.wait` call.
Args:
callback (Callable[[Event], None]):
A function which accepts :any:`Event` parameters.
Example::
import tcod.event
@tcod.event.add_watch
def handle_events(event: tcod.event.Event) -> None:
if isinstance(event, tcod.event.KeyDown):
print(event)
.. versionadded:: 13.4
"""
if callback in _event_watch_handles:
warnings.warn(
f"{callback} is already an active event watcher, nothing was added.", RuntimeWarning, stacklevel=2
)
return callback
handle = _event_watch_handles[callback] = ffi.new_handle(callback)
lib.SDL_AddEventWatch(lib._sdl_event_watcher, handle)
return callback
[docs]
def remove_watch(callback: Callable[[Event], None]) -> None:
"""Remove a callback as an event watcher.
Args:
callback (Callable[[Event], None]):
A function which has been previously registered with :any:`tcod.event.add_watch`.
.. versionadded:: 13.4
"""
if callback not in _event_watch_handles:
warnings.warn(f"{callback} is not an active event watcher, nothing was removed.", RuntimeWarning, stacklevel=2)
return
handle = _event_watch_handles[callback]
lib.SDL_DelEventWatch(lib._sdl_event_watcher, handle)
del _event_watch_handles[callback]
[docs]
def get_keyboard_state() -> NDArray[np.bool_]:
"""Return a boolean array with the current keyboard state.
Index this array with a scancode. The value will be True if the key is
currently held.
Example::
state = tcod.event.get_keyboard_state()
# Get a WASD movement vector:
x = int(state[tcod.event.Scancode.D]) - int(state[tcod.event.Scancode.A])
y = int(state[tcod.event.Scancode.S]) - int(state[tcod.event.Scancode.W])
# Key with 'z' glyph is held:
is_z_held = state[tcod.event.KeySym.z.scancode]
.. versionadded:: 12.3
"""
num_keys = ffi.new("int[1]")
keyboard_state = lib.SDL_GetKeyboardState(num_keys)
out: NDArray[np.bool_] = np.frombuffer(ffi.buffer(keyboard_state[0 : num_keys[0]]), dtype=np.bool_)
out.flags["WRITEABLE"] = False # This buffer is supposed to be const.
return out
[docs]
def get_modifier_state() -> Modifier:
"""Return a bitmask of the active keyboard modifiers.
.. versionadded:: 12.3
"""
return Modifier(lib.SDL_GetModState())
[docs]
class Scancode(enum.IntEnum):
"""A Scancode represents the physical location of a key.
For example the scan codes for WASD remain in the same physical location
regardless of the actual keyboard layout.
These names are derived from SDL except for the numbers which are prefixed
with ``N`` (since raw numbers can not be a Python name.)
.. versionadded:: 12.3
================== ===
UNKNOWN 0
A 4
B 5
C 6
D 7
E 8
F 9
G 10
H 11
I 12
J 13
K 14
L 15
M 16
N 17
O 18
P 19
Q 20
R 21
S 22
T 23
U 24
V 25
W 26
X 27
Y 28
Z 29
N1 30
N2 31
N3 32
N4 33
N5 34
N6 35
N7 36
N8 37
N9 38
N0 39
RETURN 40
ESCAPE 41
BACKSPACE 42
TAB 43
SPACE 44
MINUS 45
EQUALS 46
LEFTBRACKET 47
RIGHTBRACKET 48
BACKSLASH 49
NONUSHASH 50
SEMICOLON 51
APOSTROPHE 52
GRAVE 53
COMMA 54
PERIOD 55
SLASH 56
CAPSLOCK 57
F1 58
F2 59
F3 60
F4 61
F5 62
F6 63
F7 64
F8 65
F9 66
F10 67
F11 68
F12 69
PRINTSCREEN 70
SCROLLLOCK 71
PAUSE 72
INSERT 73
HOME 74
PAGEUP 75
DELETE 76
END 77
PAGEDOWN 78
RIGHT 79
LEFT 80
DOWN 81
UP 82
NUMLOCKCLEAR 83
KP_DIVIDE 84
KP_MULTIPLY 85
KP_MINUS 86
KP_PLUS 87
KP_ENTER 88
KP_1 89
KP_2 90
KP_3 91
KP_4 92
KP_5 93
KP_6 94
KP_7 95
KP_8 96
KP_9 97
KP_0 98
KP_PERIOD 99
NONUSBACKSLASH 100
APPLICATION 101
POWER 102
KP_EQUALS 103
F13 104
F14 105
F15 106
F16 107
F17 108
F18 109
F19 110
F20 111
F21 112
F22 113
F23 114
F24 115
EXECUTE 116
HELP 117
MENU 118
SELECT 119
STOP 120
AGAIN 121
UNDO 122
CUT 123
COPY 124
PASTE 125
FIND 126
MUTE 127
VOLUMEUP 128
VOLUMEDOWN 129
KP_COMMA 133
KP_EQUALSAS400 134
INTERNATIONAL1 135
INTERNATIONAL2 136
INTERNATIONAL3 137
INTERNATIONAL4 138
INTERNATIONAL5 139
INTERNATIONAL6 140
INTERNATIONAL7 141
INTERNATIONAL8 142
INTERNATIONAL9 143
LANG1 144
LANG2 145
LANG3 146
LANG4 147
LANG5 148
LANG6 149
LANG7 150
LANG8 151
LANG9 152
ALTERASE 153
SYSREQ 154
CANCEL 155
CLEAR 156
PRIOR 157
RETURN2 158
SEPARATOR 159
OUT 160
OPER 161
CLEARAGAIN 162
CRSEL 163
EXSEL 164
KP_00 176
KP_000 177
THOUSANDSSEPARATOR 178
DECIMALSEPARATOR 179
CURRENCYUNIT 180
CURRENCYSUBUNIT 181
KP_LEFTPAREN 182
KP_RIGHTPAREN 183
KP_LEFTBRACE 184
KP_RIGHTBRACE 185
KP_TAB 186
KP_BACKSPACE 187
KP_A 188
KP_B 189
KP_C 190
KP_D 191
KP_E 192
KP_F 193
KP_XOR 194
KP_POWER 195
KP_PERCENT 196
KP_LESS 197
KP_GREATER 198
KP_AMPERSAND 199
KP_DBLAMPERSAND 200
KP_VERTICALBAR 201
KP_DBLVERTICALBAR 202
KP_COLON 203
KP_HASH 204
KP_SPACE 205
KP_AT 206
KP_EXCLAM 207
KP_MEMSTORE 208
KP_MEMRECALL 209
KP_MEMCLEAR 210
KP_MEMADD 211
KP_MEMSUBTRACT 212
KP_MEMMULTIPLY 213
KP_MEMDIVIDE 214
KP_PLUSMINUS 215
KP_CLEAR 216
KP_CLEARENTRY 217
KP_BINARY 218
KP_OCTAL 219
KP_DECIMAL 220
KP_HEXADECIMAL 221
LCTRL 224
LSHIFT 225
LALT 226
LGUI 227
RCTRL 228
RSHIFT 229
RALT 230
RGUI 231
MODE 257
AUDIONEXT 258
AUDIOPREV 259
AUDIOSTOP 260
AUDIOPLAY 261
AUDIOMUTE 262
MEDIASELECT 263
WWW 264
MAIL 265
CALCULATOR 266
COMPUTER 267
AC_SEARCH 268
AC_HOME 269
AC_BACK 270
AC_FORWARD 271
AC_STOP 272
AC_REFRESH 273
AC_BOOKMARKS 274
BRIGHTNESSDOWN 275
BRIGHTNESSUP 276
DISPLAYSWITCH 277
KBDILLUMTOGGLE 278
KBDILLUMDOWN 279
KBDILLUMUP 280
EJECT 281
SLEEP 282
APP1 283
APP2 284
================== ===
"""
# --- SDL scancodes ---
UNKNOWN = 0
A = 4
B = 5
C = 6
D = 7
E = 8
F = 9
G = 10
H = 11
I = 12 # noqa: E741
J = 13
K = 14
L = 15
M = 16
N = 17
O = 18 # noqa: E741
P = 19
Q = 20
R = 21
S = 22
T = 23
U = 24
V = 25
W = 26
X = 27
Y = 28
Z = 29
N1 = 30
N2 = 31
N3 = 32
N4 = 33
N5 = 34
N6 = 35
N7 = 36
N8 = 37
N9 = 38
N0 = 39
RETURN = 40
ESCAPE = 41
BACKSPACE = 42
TAB = 43
SPACE = 44
MINUS = 45
EQUALS = 46
LEFTBRACKET = 47
RIGHTBRACKET = 48
BACKSLASH = 49
NONUSHASH = 50
SEMICOLON = 51
APOSTROPHE = 52
GRAVE = 53
COMMA = 54
PERIOD = 55
SLASH = 56
CAPSLOCK = 57
F1 = 58
F2 = 59
F3 = 60
F4 = 61
F5 = 62
F6 = 63
F7 = 64
F8 = 65
F9 = 66
F10 = 67
F11 = 68
F12 = 69
PRINTSCREEN = 70
SCROLLLOCK = 71
PAUSE = 72
INSERT = 73
HOME = 74
PAGEUP = 75
DELETE = 76
END = 77
PAGEDOWN = 78
RIGHT = 79
LEFT = 80
DOWN = 81
UP = 82
NUMLOCKCLEAR = 83
KP_DIVIDE = 84
KP_MULTIPLY = 85
KP_MINUS = 86
KP_PLUS = 87
KP_ENTER = 88
KP_1 = 89
KP_2 = 90
KP_3 = 91
KP_4 = 92
KP_5 = 93
KP_6 = 94
KP_7 = 95
KP_8 = 96
KP_9 = 97
KP_0 = 98
KP_PERIOD = 99
NONUSBACKSLASH = 100
APPLICATION = 101
POWER = 102
KP_EQUALS = 103
F13 = 104
F14 = 105
F15 = 106
F16 = 107
F17 = 108
F18 = 109
F19 = 110
F20 = 111
F21 = 112
F22 = 113
F23 = 114
F24 = 115
EXECUTE = 116
HELP = 117
MENU = 118
SELECT = 119
STOP = 120
AGAIN = 121
UNDO = 122
CUT = 123
COPY = 124
PASTE = 125
FIND = 126
MUTE = 127
VOLUMEUP = 128
VOLUMEDOWN = 129
KP_COMMA = 133
KP_EQUALSAS400 = 134
INTERNATIONAL1 = 135
INTERNATIONAL2 = 136
INTERNATIONAL3 = 137
INTERNATIONAL4 = 138
INTERNATIONAL5 = 139
INTERNATIONAL6 = 140
INTERNATIONAL7 = 141
INTERNATIONAL8 = 142
INTERNATIONAL9 = 143
LANG1 = 144
LANG2 = 145
LANG3 = 146
LANG4 = 147
LANG5 = 148
LANG6 = 149
LANG7 = 150
LANG8 = 151
LANG9 = 152
ALTERASE = 153
SYSREQ = 154
CANCEL = 155
CLEAR = 156
PRIOR = 157
RETURN2 = 158
SEPARATOR = 159
OUT = 160
OPER = 161
CLEARAGAIN = 162
CRSEL = 163
EXSEL = 164
KP_00 = 176
KP_000 = 177
THOUSANDSSEPARATOR = 178
DECIMALSEPARATOR = 179
CURRENCYUNIT = 180
CURRENCYSUBUNIT = 181
KP_LEFTPAREN = 182
KP_RIGHTPAREN = 183
KP_LEFTBRACE = 184
KP_RIGHTBRACE = 185
KP_TAB = 186
KP_BACKSPACE = 187
KP_A = 188
KP_B = 189
KP_C = 190
KP_D = 191
KP_E = 192
KP_F = 193
KP_XOR = 194
KP_POWER = 195
KP_PERCENT = 196
KP_LESS = 197
KP_GREATER = 198
KP_AMPERSAND = 199
KP_DBLAMPERSAND = 200
KP_VERTICALBAR = 201
KP_DBLVERTICALBAR = 202
KP_COLON = 203
KP_HASH = 204
KP_SPACE = 205
KP_AT = 206
KP_EXCLAM = 207
KP_MEMSTORE = 208
KP_MEMRECALL = 209
KP_MEMCLEAR = 210
KP_MEMADD = 211
KP_MEMSUBTRACT = 212
KP_MEMMULTIPLY = 213
KP_MEMDIVIDE = 214
KP_PLUSMINUS = 215
KP_CLEAR = 216
KP_CLEARENTRY = 217
KP_BINARY = 218
KP_OCTAL = 219
KP_DECIMAL = 220
KP_HEXADECIMAL = 221
LCTRL = 224
LSHIFT = 225
LALT = 226
LGUI = 227
RCTRL = 228
RSHIFT = 229
RALT = 230
RGUI = 231
MODE = 257
AUDIONEXT = 258
AUDIOPREV = 259
AUDIOSTOP = 260
AUDIOPLAY = 261
AUDIOMUTE = 262
MEDIASELECT = 263
WWW = 264
MAIL = 265
CALCULATOR = 266
COMPUTER = 267
AC_SEARCH = 268
AC_HOME = 269
AC_BACK = 270
AC_FORWARD = 271
AC_STOP = 272
AC_REFRESH = 273
AC_BOOKMARKS = 274
BRIGHTNESSDOWN = 275
BRIGHTNESSUP = 276
DISPLAYSWITCH = 277
KBDILLUMTOGGLE = 278
KBDILLUMDOWN = 279
KBDILLUMUP = 280
EJECT = 281
SLEEP = 282
APP1 = 283
APP2 = 284
AUDIOREWIND = 285
AUDIOFASTFORWARD = 286
# --- end ---
@property
def label(self) -> str:
"""Return a human-readable name of a key based on its scancode.
Be sure not to confuse this with ``.name``, which will return the enum
name rather than the human-readable name.
.. seealso::
:any:`KeySym.label`
"""
return self.keysym.label
@property
def keysym(self) -> KeySym:
"""Return a :class:`KeySym` from a scancode.
Based on the current keyboard layout.
"""
_init_sdl_video()
return KeySym(lib.SDL_GetKeyFromScancode(self.value))
@property
def scancode(self) -> Scancode:
"""Return a scancode from a keycode.
Returns itself since it is already a :class:`Scancode`.
.. seealso::
:any:`KeySym.scancode`
"""
return self
@classmethod
def _missing_(cls, value: object) -> Scancode | None:
if not isinstance(value, int):
return None
result = cls(0)
result._value_ = value
return result
def __eq__(self, other: Any) -> bool:
if isinstance(other, KeySym):
msg = "Scancode and KeySym enums can not be compared directly. Convert one or the other to the same type."
raise TypeError(msg)
return super().__eq__(other)
def __hash__(self) -> int:
# __eq__ was defined, so __hash__ must be defined.
return super().__hash__()
[docs]
def __repr__(self) -> str:
"""Return the fully qualified name of this enum."""
return f"tcod.event.{self.__class__.__name__}.{self.name}"
[docs]
class KeySym(enum.IntEnum):
"""Keyboard constants based on their symbol.
These names are derived from SDL except for the numbers which are prefixed
with ``N`` (since raw numbers can not be a Python name.)
.. versionadded:: 12.3
================== ==========
UNKNOWN 0
BACKSPACE 8
TAB 9
RETURN 13
ESCAPE 27
SPACE 32
EXCLAIM 33
QUOTEDBL 34
HASH 35
DOLLAR 36
PERCENT 37
AMPERSAND 38
QUOTE 39
LEFTPAREN 40
RIGHTPAREN 41
ASTERISK 42
PLUS 43
COMMA 44
MINUS 45
PERIOD 46
SLASH 47
N0 48
N1 49
N2 50
N3 51
N4 52
N5 53
N6 54
N7 55
N8 56
N9 57
COLON 58
SEMICOLON 59
LESS 60
EQUALS 61
GREATER 62
QUESTION 63
AT 64
LEFTBRACKET 91
BACKSLASH 92
RIGHTBRACKET 93
CARET 94
UNDERSCORE 95
BACKQUOTE 96
a 97
b 98
c 99
d 100
e 101
f 102
g 103
h 104
i 105
j 106
k 107
l 108
m 109
n 110
o 111
p 112
q 113
r 114
s 115
t 116
u 117
v 118
w 119
x 120
y 121
z 122
DELETE 127
SCANCODE_MASK 1073741824
CAPSLOCK 1073741881
F1 1073741882
F2 1073741883
F3 1073741884
F4 1073741885
F5 1073741886
F6 1073741887
F7 1073741888
F8 1073741889
F9 1073741890
F10 1073741891
F11 1073741892
F12 1073741893
PRINTSCREEN 1073741894
SCROLLLOCK 1073741895
PAUSE 1073741896
INSERT 1073741897
HOME 1073741898
PAGEUP 1073741899
END 1073741901
PAGEDOWN 1073741902
RIGHT 1073741903
LEFT 1073741904
DOWN 1073741905
UP 1073741906
NUMLOCKCLEAR 1073741907
KP_DIVIDE 1073741908
KP_MULTIPLY 1073741909
KP_MINUS 1073741910
KP_PLUS 1073741911
KP_ENTER 1073741912
KP_1 1073741913
KP_2 1073741914
KP_3 1073741915
KP_4 1073741916
KP_5 1073741917
KP_6 1073741918
KP_7 1073741919
KP_8 1073741920
KP_9 1073741921
KP_0 1073741922
KP_PERIOD 1073741923
APPLICATION 1073741925
POWER 1073741926
KP_EQUALS 1073741927
F13 1073741928
F14 1073741929
F15 1073741930
F16 1073741931
F17 1073741932
F18 1073741933
F19 1073741934
F20 1073741935
F21 1073741936
F22 1073741937
F23 1073741938
F24 1073741939
EXECUTE 1073741940
HELP 1073741941
MENU 1073741942
SELECT 1073741943
STOP 1073741944
AGAIN 1073741945
UNDO 1073741946
CUT 1073741947
COPY 1073741948
PASTE 1073741949
FIND 1073741950
MUTE 1073741951
VOLUMEUP 1073741952
VOLUMEDOWN 1073741953
KP_COMMA 1073741957
KP_EQUALSAS400 1073741958
ALTERASE 1073741977
SYSREQ 1073741978
CANCEL 1073741979
CLEAR 1073741980
PRIOR 1073741981
RETURN2 1073741982
SEPARATOR 1073741983
OUT 1073741984
OPER 1073741985
CLEARAGAIN 1073741986
CRSEL 1073741987
EXSEL 1073741988
KP_00 1073742000
KP_000 1073742001
THOUSANDSSEPARATOR 1073742002
DECIMALSEPARATOR 1073742003
CURRENCYUNIT 1073742004
CURRENCYSUBUNIT 1073742005
KP_LEFTPAREN 1073742006
KP_RIGHTPAREN 1073742007
KP_LEFTBRACE 1073742008
KP_RIGHTBRACE 1073742009
KP_TAB 1073742010
KP_BACKSPACE 1073742011
KP_A 1073742012
KP_B 1073742013
KP_C 1073742014
KP_D 1073742015
KP_E 1073742016
KP_F 1073742017
KP_XOR 1073742018
KP_POWER 1073742019
KP_PERCENT 1073742020
KP_LESS 1073742021
KP_GREATER 1073742022
KP_AMPERSAND 1073742023
KP_DBLAMPERSAND 1073742024
KP_VERTICALBAR 1073742025
KP_DBLVERTICALBAR 1073742026
KP_COLON 1073742027
KP_HASH 1073742028
KP_SPACE 1073742029
KP_AT 1073742030
KP_EXCLAM 1073742031
KP_MEMSTORE 1073742032
KP_MEMRECALL 1073742033
KP_MEMCLEAR 1073742034
KP_MEMADD 1073742035
KP_MEMSUBTRACT 1073742036
KP_MEMMULTIPLY 1073742037
KP_MEMDIVIDE 1073742038
KP_PLUSMINUS 1073742039
KP_CLEAR 1073742040
KP_CLEARENTRY 1073742041
KP_BINARY 1073742042
KP_OCTAL 1073742043
KP_DECIMAL 1073742044
KP_HEXADECIMAL 1073742045
LCTRL 1073742048
LSHIFT 1073742049
LALT 1073742050
LGUI 1073742051
RCTRL 1073742052
RSHIFT 1073742053
RALT 1073742054
RGUI 1073742055
MODE 1073742081
AUDIONEXT 1073742082
AUDIOPREV 1073742083
AUDIOSTOP 1073742084
AUDIOPLAY 1073742085
AUDIOMUTE 1073742086
MEDIASELECT 1073742087
WWW 1073742088
MAIL 1073742089
CALCULATOR 1073742090
COMPUTER 1073742091
AC_SEARCH 1073742092
AC_HOME 1073742093
AC_BACK 1073742094
AC_FORWARD 1073742095
AC_STOP 1073742096
AC_REFRESH 1073742097
AC_BOOKMARKS 1073742098
BRIGHTNESSDOWN 1073742099
BRIGHTNESSUP 1073742100
DISPLAYSWITCH 1073742101
KBDILLUMTOGGLE 1073742102
KBDILLUMDOWN 1073742103
KBDILLUMUP 1073742104
EJECT 1073742105
SLEEP 1073742106
================== ==========
"""
# --- SDL keyboard symbols ---
UNKNOWN = 0
BACKSPACE = 8
TAB = 9
RETURN = 13
ESCAPE = 27
SPACE = 32
EXCLAIM = 33
QUOTEDBL = 34
HASH = 35
DOLLAR = 36
PERCENT = 37
AMPERSAND = 38
QUOTE = 39
LEFTPAREN = 40
RIGHTPAREN = 41
ASTERISK = 42
PLUS = 43
COMMA = 44
MINUS = 45
PERIOD = 46
SLASH = 47
N0 = 48
N1 = 49
N2 = 50
N3 = 51
N4 = 52
N5 = 53
N6 = 54
N7 = 55
N8 = 56
N9 = 57
COLON = 58
SEMICOLON = 59
LESS = 60
EQUALS = 61
GREATER = 62
QUESTION = 63
AT = 64
LEFTBRACKET = 91
BACKSLASH = 92
RIGHTBRACKET = 93
CARET = 94
UNDERSCORE = 95
BACKQUOTE = 96
a = 97
b = 98
c = 99
d = 100
e = 101
f = 102
g = 103
h = 104
i = 105
j = 106
k = 107
l = 108 # noqa: E741
m = 109
n = 110
o = 111
p = 112
q = 113
r = 114
s = 115
t = 116
u = 117
v = 118
w = 119
x = 120
y = 121
z = 122
DELETE = 127
SCANCODE_MASK = 1073741824
CAPSLOCK = 1073741881
F1 = 1073741882
F2 = 1073741883
F3 = 1073741884
F4 = 1073741885
F5 = 1073741886
F6 = 1073741887
F7 = 1073741888
F8 = 1073741889
F9 = 1073741890
F10 = 1073741891
F11 = 1073741892
F12 = 1073741893
PRINTSCREEN = 1073741894
SCROLLLOCK = 1073741895
PAUSE = 1073741896
INSERT = 1073741897
HOME = 1073741898
PAGEUP = 1073741899
END = 1073741901
PAGEDOWN = 1073741902
RIGHT = 1073741903
LEFT = 1073741904
DOWN = 1073741905
UP = 1073741906
NUMLOCKCLEAR = 1073741907
KP_DIVIDE = 1073741908
KP_MULTIPLY = 1073741909
KP_MINUS = 1073741910
KP_PLUS = 1073741911
KP_ENTER = 1073741912
KP_1 = 1073741913
KP_2 = 1073741914
KP_3 = 1073741915
KP_4 = 1073741916
KP_5 = 1073741917
KP_6 = 1073741918
KP_7 = 1073741919
KP_8 = 1073741920
KP_9 = 1073741921
KP_0 = 1073741922
KP_PERIOD = 1073741923
APPLICATION = 1073741925
POWER = 1073741926
KP_EQUALS = 1073741927
F13 = 1073741928
F14 = 1073741929
F15 = 1073741930
F16 = 1073741931
F17 = 1073741932
F18 = 1073741933
F19 = 1073741934
F20 = 1073741935
F21 = 1073741936
F22 = 1073741937
F23 = 1073741938
F24 = 1073741939
EXECUTE = 1073741940
HELP = 1073741941
MENU = 1073741942
SELECT = 1073741943
STOP = 1073741944
AGAIN = 1073741945
UNDO = 1073741946
CUT = 1073741947
COPY = 1073741948
PASTE = 1073741949
FIND = 1073741950
MUTE = 1073741951
VOLUMEUP = 1073741952
VOLUMEDOWN = 1073741953
KP_COMMA = 1073741957
KP_EQUALSAS400 = 1073741958
ALTERASE = 1073741977
SYSREQ = 1073741978
CANCEL = 1073741979
CLEAR = 1073741980
PRIOR = 1073741981
RETURN2 = 1073741982
SEPARATOR = 1073741983
OUT = 1073741984
OPER = 1073741985
CLEARAGAIN = 1073741986
CRSEL = 1073741987
EXSEL = 1073741988
KP_00 = 1073742000
KP_000 = 1073742001
THOUSANDSSEPARATOR = 1073742002
DECIMALSEPARATOR = 1073742003
CURRENCYUNIT = 1073742004
CURRENCYSUBUNIT = 1073742005
KP_LEFTPAREN = 1073742006
KP_RIGHTPAREN = 1073742007
KP_LEFTBRACE = 1073742008
KP_RIGHTBRACE = 1073742009
KP_TAB = 1073742010
KP_BACKSPACE = 1073742011
KP_A = 1073742012
KP_B = 1073742013
KP_C = 1073742014
KP_D = 1073742015
KP_E = 1073742016
KP_F = 1073742017
KP_XOR = 1073742018
KP_POWER = 1073742019
KP_PERCENT = 1073742020
KP_LESS = 1073742021
KP_GREATER = 1073742022
KP_AMPERSAND = 1073742023
KP_DBLAMPERSAND = 1073742024
KP_VERTICALBAR = 1073742025
KP_DBLVERTICALBAR = 1073742026
KP_COLON = 1073742027
KP_HASH = 1073742028
KP_SPACE = 1073742029
KP_AT = 1073742030
KP_EXCLAM = 1073742031
KP_MEMSTORE = 1073742032
KP_MEMRECALL = 1073742033
KP_MEMCLEAR = 1073742034
KP_MEMADD = 1073742035
KP_MEMSUBTRACT = 1073742036
KP_MEMMULTIPLY = 1073742037
KP_MEMDIVIDE = 1073742038
KP_PLUSMINUS = 1073742039
KP_CLEAR = 1073742040
KP_CLEARENTRY = 1073742041
KP_BINARY = 1073742042
KP_OCTAL = 1073742043
KP_DECIMAL = 1073742044
KP_HEXADECIMAL = 1073742045
LCTRL = 1073742048
LSHIFT = 1073742049
LALT = 1073742050
LGUI = 1073742051
RCTRL = 1073742052
RSHIFT = 1073742053
RALT = 1073742054
RGUI = 1073742055
MODE = 1073742081
AUDIONEXT = 1073742082
AUDIOPREV = 1073742083
AUDIOSTOP = 1073742084
AUDIOPLAY = 1073742085
AUDIOMUTE = 1073742086
MEDIASELECT = 1073742087
WWW = 1073742088
MAIL = 1073742089
CALCULATOR = 1073742090
COMPUTER = 1073742091
AC_SEARCH = 1073742092
AC_HOME = 1073742093
AC_BACK = 1073742094
AC_FORWARD = 1073742095
AC_STOP = 1073742096
AC_REFRESH = 1073742097
AC_BOOKMARKS = 1073742098
BRIGHTNESSDOWN = 1073742099
BRIGHTNESSUP = 1073742100
DISPLAYSWITCH = 1073742101
KBDILLUMTOGGLE = 1073742102
KBDILLUMDOWN = 1073742103
KBDILLUMUP = 1073742104
EJECT = 1073742105
SLEEP = 1073742106
APP1 = 1073742107
APP2 = 1073742108
AUDIOREWIND = 1073742109
AUDIOFASTFORWARD = 1073742110
# --- end ---
@property
def label(self) -> str:
"""A human-readable name of a keycode.
Returns "" if the keycode doesn't have a name.
Be sure not to confuse this with ``.name``, which will return the enum
name rather than the human-readable name.
Example::
>>> tcod.event.KeySym.F1.label
'F1'
>>> tcod.event.KeySym.BACKSPACE.label
'Backspace'
"""
return str(ffi.string(lib.SDL_GetKeyName(self.value)), encoding="utf-8")
@property
def keysym(self) -> KeySym:
"""Return a keycode from a scancode.
Returns itself since it is already a :class:`KeySym`.
.. seealso::
:any:`Scancode.keysym`
"""
return self
@property
def scancode(self) -> Scancode:
"""Return a scancode from a keycode.
Based on the current keyboard layout.
"""
_init_sdl_video()
return Scancode(lib.SDL_GetScancodeFromKey(self.value))
@classmethod
def _missing_(cls, value: object) -> KeySym | None:
if not isinstance(value, int):
return None
result = cls(0)
result._value_ = value
return result
def __eq__(self, other: Any) -> bool:
if isinstance(other, Scancode):
msg = "Scancode and KeySym enums can not be compared directly. Convert one or the other to the same type."
raise TypeError(msg)
return super().__eq__(other)
def __hash__(self) -> int:
# __eq__ was defined, so __hash__ must be defined.
return super().__hash__()
[docs]
def __repr__(self) -> str:
"""Return the fully qualified name of this enum."""
return f"tcod.event.{self.__class__.__name__}.{self.name}"
[docs]
def __getattr__(name: str) -> int:
"""Migrate deprecated access of event constants."""
if name.startswith("BUTTON_"):
replacement = {
"BUTTON_LEFT": MouseButton.LEFT,
"BUTTON_MIDDLE": MouseButton.MIDDLE,
"BUTTON_RIGHT": MouseButton.RIGHT,
"BUTTON_X1": MouseButton.X1,
"BUTTON_X2": MouseButton.X2,
"BUTTON_LMASK": MouseButtonMask.LEFT,
"BUTTON_MMASK": MouseButtonMask.MIDDLE,
"BUTTON_RMASK": MouseButtonMask.RIGHT,
"BUTTON_X1MASK": MouseButtonMask.X1,
"BUTTON_X2MASK": MouseButtonMask.X2,
}[name]
warnings.warn(
"Key constants have been replaced with enums.\n"
f"'tcod.event.{name}' should be replaced with 'tcod.event.{replacement!r}'",
FutureWarning,
stacklevel=2,
)
return replacement
value: int | None = getattr(tcod.event_constants, name, None)
if not value:
msg = f"module {__name__!r} has no attribute {name!r}"
raise AttributeError(msg)
if name.startswith("SCANCODE_"):
scancode = name[9:]
if scancode.isdigit():
scancode = f"N{scancode}"
warnings.warn(
"Key constants have been replaced with enums.\n"
f"`tcod.event.{name}` should be replaced with `tcod.event.Scancode.{scancode}`",
FutureWarning,
stacklevel=2,
)
elif name.startswith("K_"):
sym = name[2:]
if sym.isdigit():
sym = f"N{sym}"
warnings.warn(
"Key constants have been replaced with enums.\n"
f"`tcod.event.{name}` should be replaced with `tcod.event.KeySym.{sym}`",
FutureWarning,
stacklevel=2,
)
elif name.startswith("KMOD_"):
modifier = name[5:]
warnings.warn(
"Key modifiers have been replaced with the Modifier IntFlag.\n"
f"`tcod.event.{modifier}` should be replaced with `tcod.event.Modifier.{modifier}`",
FutureWarning,
stacklevel=2,
)
return value
__all__ = [ # noqa: F405 RUF022
"Modifier",
"Point",
"BUTTON_LEFT",
"BUTTON_MIDDLE",
"BUTTON_RIGHT",
"BUTTON_X1",
"BUTTON_X2",
"BUTTON_LMASK",
"BUTTON_MMASK",
"BUTTON_RMASK",
"BUTTON_X1MASK",
"BUTTON_X2MASK",
"Event",
"Quit",
"KeyboardEvent",
"KeyDown",
"KeyUp",
"MouseMotion",
"MouseButtonEvent",
"MouseButtonDown",
"MouseButtonUp",
"MouseWheel",
"TextInput",
"WindowEvent",
"WindowMoved",
"WindowResized",
"JoystickEvent",
"JoystickAxis",
"JoystickBall",
"JoystickHat",
"JoystickButton",
"JoystickDevice",
"ControllerEvent",
"ControllerAxis",
"ControllerButton",
"ControllerDevice",
"Undefined",
"get",
"wait",
"get_mouse_state",
"add_watch",
"remove_watch",
"EventDispatch",
"get_keyboard_state",
"get_modifier_state",
"Scancode",
"KeySym",
# --- From event_constants.py ---
"MOUSEWHEEL_NORMAL",
"MOUSEWHEEL_FLIPPED",
"MOUSEWHEEL",
]