feat(display): add configurable multi-backend display system

This commit is contained in:
2026-03-15 21:17:16 -07:00
parent ac1306373d
commit d7b044ceae
6 changed files with 78 additions and 17 deletions

View File

@@ -33,13 +33,25 @@ from engine.websocket_display import WebSocketDisplay
def _get_display():
"""Get the appropriate display based on config."""
if config.WEBSOCKET:
"""Get the appropriate display(s) based on config."""
from engine.display import MultiDisplay, TerminalDisplay
displays = []
if config.DISPLAY in ("terminal", "both"):
displays.append(TerminalDisplay())
if config.DISPLAY in ("websocket", "both") or config.WEBSOCKET:
ws = WebSocketDisplay(host="0.0.0.0", port=config.WEBSOCKET_PORT)
ws.start_server()
ws.start_http_server()
return ws
return None
displays.append(ws)
if not displays:
return None
if len(displays) == 1:
return displays[0]
return MultiDisplay(displays)
TITLE = [

View File

@@ -127,6 +127,7 @@ class Config:
script_fonts: dict[str, str] = field(default_factory=_get_platform_font_paths)
display: str = "terminal"
websocket: bool = False
websocket_port: int = 8765
@@ -167,6 +168,7 @@ class Config:
glitch_glyphs="░▒▓█▌▐╌╍╎╏┃┆┇┊┋",
kata_glyphs="ハミヒーウシナモニサワツオリアホテマケメエカキムユラセネスタヌヘ",
script_fonts=_get_platform_font_paths(),
display=_arg_value("--display", argv) or "terminal",
websocket="--websocket" in argv,
websocket_port=_arg_int("--websocket-port", 8765, argv),
)
@@ -229,6 +231,7 @@ GLITCH = "░▒▓█▌▐╌╍╎╏┃┆┇┊┋"
KATA = "ハミヒーウシナモニサワツオリアホテマケメエカキムユラセネスタヌヘ"
# ─── WEBSOCKET ─────────────────────────────────────────────
DISPLAY = _arg_value("--display", sys.argv) or "terminal"
WEBSOCKET = "--websocket" in sys.argv
WEBSOCKET_PORT = _arg_int("--websocket-port", 8765)

View File

@@ -100,3 +100,30 @@ class NullDisplay:
def cleanup(self) -> None:
pass
class MultiDisplay:
"""Display that forwards to multiple displays."""
def __init__(self, displays: list[Display]):
self.displays = displays
self.width = 80
self.height = 24
def init(self, width: int, height: int) -> None:
self.width = width
self.height = height
for d in self.displays:
d.init(width, height)
def show(self, buffer: list[str]) -> None:
for d in self.displays:
d.show(buffer)
def clear(self) -> None:
for d in self.displays:
d.clear()
def cleanup(self) -> None:
for d in self.displays:
d.cleanup()

View File

@@ -193,11 +193,9 @@ class WebSocketDisplay:
def _run_async(self, coro):
"""Run coroutine in background."""
try:
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
loop.run_until_complete(coro)
except Exception:
pass
asyncio.run(coro)
except Exception as e:
print(f"WebSocket async error: {e}")
def start_server(self):
"""Start the WebSocket server in a background thread."""
@@ -224,6 +222,8 @@ class WebSocketDisplay:
if self._http_thread is not None:
return
self._http_running = True
self._http_running = True
self._http_thread = threading.Thread(
target=self._run_async, args=(self._run_http_server(),), daemon=True