diff --git a/effects_plugins/hud.py b/effects_plugins/hud.py index 7ab91be..e284728 100644 --- a/effects_plugins/hud.py +++ b/effects_plugins/hud.py @@ -14,9 +14,11 @@ class HudEffect(EffectPlugin): frame_time = 0.0 if monitor: stats = monitor.get_stats() - if stats: - fps = stats.fps - frame_time = stats.avg_frame_time_ms + if stats and "pipeline" in stats: + frame_time = stats["pipeline"].get("avg_ms", 0.0) + frame_count = stats.get("frame_count", 0) + if frame_count > 0 and frame_time > 0: + fps = 1000.0 / frame_time w = ctx.terminal_width h = ctx.terminal_height diff --git a/engine/app.py b/engine/app.py index a23b749..8bc6089 100644 --- a/engine/app.py +++ b/engine/app.py @@ -376,7 +376,7 @@ def run_demo_mode(): registry = get_registry() chain = get_effect_chain() - chain.set_order(["hud", "noise", "fade", "glitch", "firehose"]) + chain.set_order(["noise", "fade", "glitch", "firehose", "hud"]) monitor = PerformanceMonitor() set_monitor(monitor) @@ -512,6 +512,13 @@ def run_demo_mode(): result = chain.process(buf, ctx) display.show(result) + new_w, new_h = display.get_dimensions() + if new_w != w or new_h != h: + w, h = new_w, new_h + scroll_step_interval = calculate_scroll_step(config.SCROLL_DUR, h) + active = [] + noise_cache = {} + frame_number += 1 time.sleep(1 / 60) diff --git a/engine/display/backends/pygame.py b/engine/display/backends/pygame.py index 591656e..e2548e8 100644 --- a/engine/display/backends/pygame.py +++ b/engine/display/backends/pygame.py @@ -36,6 +36,7 @@ class PygameDisplay: self._pygame = None self._screen = None self._font = None + self._resized = False def _get_font_path(self) -> str | None: """Get font path for rendering.""" @@ -110,7 +111,10 @@ class PygameDisplay: pygame.init() pygame.display.set_caption("Mainline") - self._screen = pygame.display.set_mode((self.window_width, self.window_height)) + self._screen = pygame.display.set_mode( + (self.window_width, self.window_height), + pygame.RESIZABLE, + ) self._pygame = pygame PygameDisplay._pygame_initialized = True @@ -136,6 +140,12 @@ class PygameDisplay: for event in self._pygame.event.get(): if event.type == self._pygame.QUIT: sys.exit(0) + elif event.type == self._pygame.VIDEORESIZE: + self.window_width = event.w + self.window_height = event.h + self.width = max(1, self.window_width // self.cell_width) + self.height = max(1, self.window_height // self.cell_height) + self._resized = True self._screen.fill((0, 0, 0)) @@ -175,6 +185,16 @@ class PygameDisplay: self._screen.fill((0, 0, 0)) self._pygame.display.flip() + def get_dimensions(self) -> tuple[int, int]: + """Get current terminal dimensions based on window size. + + Returns: + (width, height) in character cells + """ + if self._resized: + self._resized = False + return self.width, self.height + def cleanup(self, quit_pygame: bool = True) -> None: """Cleanup display resources. diff --git a/tests/test_hud.py b/tests/test_hud.py new file mode 100644 index 0000000..195815c --- /dev/null +++ b/tests/test_hud.py @@ -0,0 +1,107 @@ + +from engine.effects.performance import PerformanceMonitor, set_monitor +from engine.effects.types import EffectContext + + +def test_hud_effect_adds_hud_lines(): + """Test that HUD effect adds HUD lines to the buffer.""" + from effects_plugins.hud import HudEffect + + set_monitor(PerformanceMonitor()) + + hud = HudEffect() + hud.config.params["display_effect"] = "noise" + hud.config.params["display_intensity"] = 0.5 + + ctx = EffectContext( + terminal_width=80, + terminal_height=24, + scroll_cam=0, + ticker_height=24, + mic_excess=0.0, + grad_offset=0.0, + frame_number=0, + has_message=False, + items=[], + ) + + buf = [ + "A" * 80, + "B" * 80, + "C" * 80, + ] + + result = hud.process(buf, ctx) + + assert len(result) >= 3, f"Expected at least 3 lines, got {len(result)}" + + first_line = result[0] + assert "MAINLINE DEMO" in first_line, ( + f"HUD not found in first line: {first_line[:50]}" + ) + + second_line = result[1] + assert "EFFECT:" in second_line, f"Effect line not found: {second_line[:50]}" + + print("First line:", result[0]) + print("Second line:", result[1]) + if len(result) > 2: + print("Third line:", result[2]) + + +def test_hud_effect_shows_current_effect(): + """Test that HUD displays the correct effect name.""" + from effects_plugins.hud import HudEffect + + set_monitor(PerformanceMonitor()) + + hud = HudEffect() + hud.config.params["display_effect"] = "fade" + hud.config.params["display_intensity"] = 0.75 + + ctx = EffectContext( + terminal_width=80, + terminal_height=24, + scroll_cam=0, + ticker_height=24, + mic_excess=0.0, + grad_offset=0.0, + frame_number=0, + has_message=False, + items=[], + ) + + buf = ["X" * 80] + result = hud.process(buf, ctx) + + second_line = result[1] + assert "fade" in second_line, f"Effect name 'fade' not found in: {second_line}" + + +def test_hud_effect_shows_intensity(): + """Test that HUD displays intensity percentage.""" + from effects_plugins.hud import HudEffect + + set_monitor(PerformanceMonitor()) + + hud = HudEffect() + hud.config.params["display_effect"] = "glitch" + hud.config.params["display_intensity"] = 0.8 + + ctx = EffectContext( + terminal_width=80, + terminal_height=24, + scroll_cam=0, + ticker_height=24, + mic_excess=0.0, + grad_offset=0.0, + frame_number=0, + has_message=False, + items=[], + ) + + buf = ["Y" * 80] + result = hud.process(buf, ctx) + + second_line = result[1] + assert "80%" in second_line, f"Intensity 80% not found in: {second_line}"