feat(app): add demo mode with HUD effect plugin
- Add --demo flag that runs effect showcase with pygame display - Add HUD effect plugin (effects_plugins/hud.py) that displays: - FPS and frame time - Current effect name with intensity bar - Pipeline order - Demo mode cycles through noise, fade, glitch, firehose effects - Ramps intensity 0→1→0 over 5 seconds per effect
This commit is contained in:
61
effects_plugins/hud.py
Normal file
61
effects_plugins/hud.py
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
from engine.effects.performance import get_monitor
|
||||||
|
from engine.effects.types import EffectConfig, EffectContext, EffectPlugin
|
||||||
|
|
||||||
|
|
||||||
|
class HudEffect(EffectPlugin):
|
||||||
|
name = "hud"
|
||||||
|
config = EffectConfig(enabled=True, intensity=1.0)
|
||||||
|
|
||||||
|
def process(self, buf: list[str], ctx: EffectContext) -> list[str]:
|
||||||
|
result = list(buf)
|
||||||
|
monitor = get_monitor()
|
||||||
|
|
||||||
|
fps = 0.0
|
||||||
|
frame_time = 0.0
|
||||||
|
if monitor:
|
||||||
|
stats = monitor.get_stats()
|
||||||
|
if stats:
|
||||||
|
fps = stats.fps
|
||||||
|
frame_time = stats.avg_frame_time_ms
|
||||||
|
|
||||||
|
w = ctx.terminal_width
|
||||||
|
h = ctx.terminal_height
|
||||||
|
|
||||||
|
effect_name = self.config.params.get("display_effect", "none")
|
||||||
|
effect_intensity = self.config.params.get("display_intensity", 0.0)
|
||||||
|
|
||||||
|
hud_lines = []
|
||||||
|
hud_lines.append(
|
||||||
|
f"\033[1;1H\033[38;5;46mMAINLINE DEMO\033[0m \033[38;5;245m|\033[0m \033[38;5;39mFPS: {fps:.1f}\033[0m \033[38;5;245m|\033[0m \033[38;5;208m{frame_time:.1f}ms\033[0m"
|
||||||
|
)
|
||||||
|
|
||||||
|
bar_width = 20
|
||||||
|
filled = int(bar_width * effect_intensity)
|
||||||
|
bar = (
|
||||||
|
"\033[38;5;82m"
|
||||||
|
+ "█" * filled
|
||||||
|
+ "\033[38;5;240m"
|
||||||
|
+ "░" * (bar_width - filled)
|
||||||
|
+ "\033[0m"
|
||||||
|
)
|
||||||
|
hud_lines.append(
|
||||||
|
f"\033[2;1H\033[38;5;45mEFFECT:\033[0m \033[1;38;5;227m{effect_name:12s}\033[0m \033[38;5;245m|\033[0m {bar} \033[38;5;245m|\033[0m \033[38;5;219m{effect_intensity * 100:.0f}%\033[0m"
|
||||||
|
)
|
||||||
|
|
||||||
|
from engine.effects import get_effect_chain
|
||||||
|
|
||||||
|
chain = get_effect_chain()
|
||||||
|
order = chain.get_order()
|
||||||
|
pipeline_str = ",".join(order) if order else "(none)"
|
||||||
|
hud_lines.append(f"\033[3;1H\033[38;5;44mPIPELINE:\033[0m {pipeline_str}")
|
||||||
|
|
||||||
|
for i, line in enumerate(hud_lines):
|
||||||
|
if i < len(result):
|
||||||
|
result[i] = line + result[i][len(line) :]
|
||||||
|
else:
|
||||||
|
result.append(line)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
def configure(self, config: EffectConfig) -> None:
|
||||||
|
self.config = config
|
||||||
121
engine/app.py
121
engine/app.py
@@ -351,7 +351,128 @@ def pick_effects_config():
|
|||||||
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
|
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
|
||||||
|
|
||||||
|
|
||||||
|
def run_demo_mode():
|
||||||
|
"""Run demo mode - showcases effects with pygame display."""
|
||||||
|
import random
|
||||||
|
|
||||||
|
from engine import config
|
||||||
|
from engine.display import DisplayRegistry
|
||||||
|
from engine.effects import (
|
||||||
|
EffectContext,
|
||||||
|
PerformanceMonitor,
|
||||||
|
get_effect_chain,
|
||||||
|
get_registry,
|
||||||
|
set_monitor,
|
||||||
|
)
|
||||||
|
|
||||||
|
print(" \033[1;38;5;46mMAINLINE DEMO MODE\033[0m")
|
||||||
|
print(" \033[38;5;245mInitializing pygame display...\033[0m")
|
||||||
|
|
||||||
|
import effects_plugins
|
||||||
|
|
||||||
|
effects_plugins.discover_plugins()
|
||||||
|
|
||||||
|
registry = get_registry()
|
||||||
|
chain = get_effect_chain()
|
||||||
|
chain.set_order(["hud"])
|
||||||
|
|
||||||
|
monitor = PerformanceMonitor()
|
||||||
|
set_monitor(monitor)
|
||||||
|
chain._monitor = monitor
|
||||||
|
|
||||||
|
display = DisplayRegistry.create("pygame")
|
||||||
|
if not display:
|
||||||
|
print(" \033[38;5;196mFailed to create pygame display\033[0m")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
display.init(80, 24)
|
||||||
|
display.clear()
|
||||||
|
|
||||||
|
effects_to_demo = ["noise", "fade", "glitch", "firehose"]
|
||||||
|
w, h = 80, 24
|
||||||
|
|
||||||
|
base_buffer = []
|
||||||
|
for row in range(h):
|
||||||
|
line = ""
|
||||||
|
for col in range(w):
|
||||||
|
char = random.choice("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 ")
|
||||||
|
line += char
|
||||||
|
base_buffer.append(line)
|
||||||
|
|
||||||
|
print(" \033[38;5;82mStarting effect demo...\033[0m")
|
||||||
|
print(" \033[38;5;245mPress Ctrl+C to exit\033[0m\n")
|
||||||
|
|
||||||
|
effect_idx = 0
|
||||||
|
effect_name = effects_to_demo[effect_idx]
|
||||||
|
effect_start_time = time.time()
|
||||||
|
current_intensity = 0.0
|
||||||
|
ramping_up = True
|
||||||
|
|
||||||
|
frame_count = 0
|
||||||
|
|
||||||
|
try:
|
||||||
|
while True:
|
||||||
|
elapsed = time.time() - effect_start_time
|
||||||
|
duration = config.DEMO_EFFECT_DURATION
|
||||||
|
|
||||||
|
if elapsed >= duration:
|
||||||
|
effect_idx = (effect_idx + 1) % len(effects_to_demo)
|
||||||
|
effect_name = effects_to_demo[effect_idx]
|
||||||
|
effect_start_time = time.time()
|
||||||
|
elapsed = 0
|
||||||
|
current_intensity = 0.0
|
||||||
|
ramping_up = True
|
||||||
|
|
||||||
|
progress = elapsed / duration
|
||||||
|
if ramping_up:
|
||||||
|
current_intensity = progress
|
||||||
|
if progress >= 1.0:
|
||||||
|
ramping_up = False
|
||||||
|
else:
|
||||||
|
current_intensity = 1.0 - progress
|
||||||
|
|
||||||
|
for effect in registry.list_all().values():
|
||||||
|
if effect.name == effect_name:
|
||||||
|
effect.config.enabled = True
|
||||||
|
effect.config.intensity = current_intensity
|
||||||
|
elif effect.name not in ("hud", effect_name):
|
||||||
|
effect.config.enabled = False
|
||||||
|
|
||||||
|
hud_effect = registry.get("hud")
|
||||||
|
if hud_effect:
|
||||||
|
hud_effect.config.params["display_effect"] = effect_name
|
||||||
|
hud_effect.config.params["display_intensity"] = current_intensity
|
||||||
|
|
||||||
|
ctx = EffectContext(
|
||||||
|
terminal_width=w,
|
||||||
|
terminal_height=h,
|
||||||
|
scroll_cam=0,
|
||||||
|
ticker_height=h,
|
||||||
|
mic_excess=0.0,
|
||||||
|
grad_offset=time.time() % 1.0,
|
||||||
|
frame_number=frame_count,
|
||||||
|
has_message=False,
|
||||||
|
items=[],
|
||||||
|
)
|
||||||
|
|
||||||
|
result = chain.process(base_buffer, ctx)
|
||||||
|
display.show(result)
|
||||||
|
|
||||||
|
frame_count += 1
|
||||||
|
time.sleep(1 / 60)
|
||||||
|
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
pass
|
||||||
|
finally:
|
||||||
|
display.cleanup()
|
||||||
|
print("\n \033[38;5;245mDemo ended\033[0m")
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
if config.DEMO:
|
||||||
|
run_demo_mode()
|
||||||
|
return
|
||||||
|
|
||||||
atexit.register(lambda: print(CURSOR_ON, end="", flush=True))
|
atexit.register(lambda: print(CURSOR_ON, end="", flush=True))
|
||||||
|
|
||||||
def handle_sigint(*_):
|
def handle_sigint(*_):
|
||||||
|
|||||||
@@ -241,6 +241,10 @@ DISPLAY = _arg_value("--display", sys.argv) or "terminal"
|
|||||||
WEBSOCKET = "--websocket" in sys.argv
|
WEBSOCKET = "--websocket" in sys.argv
|
||||||
WEBSOCKET_PORT = _arg_int("--websocket-port", 8765)
|
WEBSOCKET_PORT = _arg_int("--websocket-port", 8765)
|
||||||
|
|
||||||
|
# ─── DEMO MODE ────────────────────────────────────────────
|
||||||
|
DEMO = "--demo" in sys.argv
|
||||||
|
DEMO_EFFECT_DURATION = 5.0 # seconds per effect
|
||||||
|
|
||||||
|
|
||||||
def set_font_selection(font_path=None, font_index=None):
|
def set_font_selection(font_path=None, font_index=None):
|
||||||
"""Set runtime primary font selection."""
|
"""Set runtime primary font selection."""
|
||||||
|
|||||||
Reference in New Issue
Block a user