Source code for mini_arcade_core.runtime.input_frame
"""
Input frame data structure for capturing input state per frame.
"""
from __future__ import annotations
from dataclasses import asdict, dataclass, field
from typing import Dict, FrozenSet, Tuple
from mini_arcade_core.backend.keys import Key
[docs]
@dataclass(frozen=True)
class ButtonState:
"""
State of a single action button.
:ivar down (bool): Whether the button is currently held down.
:ivar pressed (bool): Whether the button was pressed this frame.
:ivar released (bool): Whether the button was released this frame.
"""
down: bool
pressed: bool
released: bool
[docs]
def to_dict(self) -> Dict[str, bool]:
"""
Convert the ButtonState to a dictionary.
:return: Dictionary representation of the ButtonState.
:rtype: Dict[str, bool]
"""
return asdict(self)
[docs]
@classmethod
def from_dict(cls, data: Dict[str, bool]) -> ButtonState:
"""
Create a ButtonState from a dictionary.
:param data: Dictionary containing button state data.
:type data: Dict[str, bool]
:return: ButtonState instance.
:rtype: ButtonState
"""
return cls(
down=data.get("down", False),
pressed=data.get("pressed", False),
released=data.get("released", False),
)
# TODO: Solve too-many-instance-attributes warning later
# Justification: This data class needs multiple attributes to capture input state.
# pylint: disable=too-many-instance-attributes
[docs]
@dataclass(frozen=True)
class InputFrame:
"""
Snapshot of input state for a single frame.
:ivar frame_index (int): Sequential index of the frame.
:ivar dt (float): Delta time since the last frame in seconds.
:ivar keys_down (FrozenSet[Key]): Set of currently held down keys.
:ivar keys_pressed (FrozenSet[Key]): Set of keys pressed this frame.
:ivar keys_released (FrozenSet[Key]): Set of keys released this frame.
:ivar buttons (Dict[str, ButtonState]): Mapping of action button names to their states.
:ivar axes (Dict[str, float]): Mapping of axis names to their float values.
:ivar mouse_pos (Tuple[int, int]): Current mouse position (x, y).
:ivar mouse_delta (Tuple[int, int]): Mouse movement delta (dx, dy)
:ivar text_input (str): Text input entered this frame.
:ivar quit (bool): Whether a quit request was made this frame.
"""
frame_index: int
dt: float
# Physical keys (device-level snapshot) – supports cheats & replay
keys_down: FrozenSet[Key] = frozenset()
keys_pressed: FrozenSet[Key] = frozenset()
keys_released: FrozenSet[Key] = frozenset()
# action buttons (jump, confirm, pause, etc.)
buttons: Dict[str, ButtonState] = field(default_factory=dict)
# axes (move_y, aim_x, etc.)
axes: Dict[str, float] = field(default_factory=dict)
# optional: pass through for UI needs
mouse_pos: Tuple[int, int] = (0, 0)
mouse_delta: Tuple[int, int] = (0, 0)
text_input: str = ""
# Window/OS quit request
quit: bool = False
[docs]
def to_dict(self) -> Dict[str, object]:
"""
Convert the InputFrame to a dictionary.
:return: Dictionary representation of the InputFrame.
:rtype: Dict[str, object]
"""
data = asdict(self)
# Convert ButtonState objects to dicts
data["buttons"] = {
name: state.to_dict() for name, state in self.buttons.items()
}
# Convert FrozenSet to list for serialization
data["keys_down"] = [k.value for k in self.keys_down]
data["keys_pressed"] = [k.value for k in self.keys_pressed]
data["keys_released"] = [k.value for k in self.keys_released]
return data
[docs]
@classmethod
def from_dict(cls, data: Dict[str, object]) -> InputFrame:
"""
Create an InputFrame from a dictionary.
:param data: Dictionary containing input frame data.
:type data: Dict[str, object]
:return: InputFrame instance.
:rtype: InputFrame
"""
return cls(
frame_index=data.get("frame_index", 0),
dt=data.get("dt", 0.0),
keys_down=frozenset(Key(v) for v in data.get("keys_down", [])),
keys_pressed=frozenset(
Key(v) for v in data.get("keys_pressed", [])
),
keys_released=frozenset(
Key(v) for v in data.get("keys_released", [])
),
buttons={
name: ButtonState.from_dict(state)
for name, state in data.get("buttons", {}).items()
},
axes=data.get("axes", {}),
mouse_pos=tuple(data.get("mouse_pos", (0, 0))),
mouse_delta=tuple(data.get("mouse_delta", (0, 0))),
text_input=data.get("text_input", ""),
quit=data.get("quit", False),
)
# pylint: enable=too-many-instance-attributes