""" Display backend system with registry pattern. Allows swapping output backends via the Display protocol. Supports auto-discovery of display backends. """ from typing import Protocol from engine.display.backends.kitty import KittyDisplay from engine.display.backends.multi import MultiDisplay from engine.display.backends.null import NullDisplay from engine.display.backends.pygame import PygameDisplay from engine.display.backends.sixel import SixelDisplay from engine.display.backends.terminal import TerminalDisplay from engine.display.backends.websocket import WebSocketDisplay class Display(Protocol): """Protocol for display backends. All display backends must implement: - width, height: Terminal dimensions - init(width, height, reuse=False): Initialize the display - show(buffer): Render buffer to display - clear(): Clear the display - cleanup(): Shutdown the display Optional methods for keyboard input: - is_quit_requested(): Returns True if user pressed Ctrl+C/Q or Escape - clear_quit_request(): Clears the quit request flag The reuse flag allows attaching to an existing display instance rather than creating a new window/connection. Keyboard input support by backend: - terminal: No native input (relies on signal handler for Ctrl+C) - pygame: Supports Ctrl+C, Ctrl+Q, Escape for graceful shutdown - websocket: No native input (relies on signal handler for Ctrl+C) - sixel: No native input (relies on signal handler for Ctrl+C) - null: No native input - kitty: Supports Ctrl+C, Ctrl+Q, Escape (via pygame-like handling) """ width: int height: int def init(self, width: int, height: int, reuse: bool = False) -> None: """Initialize display with dimensions. Args: width: Terminal width in characters height: Terminal height in rows reuse: If True, attach to existing display instead of creating new """ ... def show(self, buffer: list[str]) -> None: """Show buffer on display.""" ... def clear(self) -> None: """Clear display.""" ... def cleanup(self) -> None: """Shutdown display.""" ... class DisplayRegistry: """Registry for display backends with auto-discovery.""" _backends: dict[str, type[Display]] = {} _initialized = False @classmethod def register(cls, name: str, backend_class: type[Display]) -> None: """Register a display backend.""" cls._backends[name.lower()] = backend_class @classmethod def get(cls, name: str) -> type[Display] | None: """Get a display backend class by name.""" return cls._backends.get(name.lower()) @classmethod def list_backends(cls) -> list[str]: """List all available display backend names.""" return list(cls._backends.keys()) @classmethod def create(cls, name: str, **kwargs) -> Display | None: """Create a display instance by name.""" cls.initialize() backend_class = cls.get(name) if backend_class: return backend_class(**kwargs) return None @classmethod def initialize(cls) -> None: """Initialize and register all built-in backends.""" if cls._initialized: return cls.register("terminal", TerminalDisplay) cls.register("null", NullDisplay) cls.register("websocket", WebSocketDisplay) cls.register("sixel", SixelDisplay) cls.register("kitty", KittyDisplay) cls.register("pygame", PygameDisplay) cls._initialized = True def get_monitor(): """Get the performance monitor.""" try: from engine.effects.performance import get_monitor as _get_monitor return _get_monitor() except Exception: return None __all__ = [ "Display", "DisplayRegistry", "get_monitor", "TerminalDisplay", "NullDisplay", "WebSocketDisplay", "SixelDisplay", "MultiDisplay", ]