forked from genewildish/Mainline
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)
|
||||
|
||||
|
||||
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():
|
||||
if config.DEMO:
|
||||
run_demo_mode()
|
||||
return
|
||||
|
||||
atexit.register(lambda: print(CURSOR_ON, end="", flush=True))
|
||||
|
||||
def handle_sigint(*_):
|
||||
|
||||
@@ -241,6 +241,10 @@ DISPLAY = _arg_value("--display", sys.argv) or "terminal"
|
||||
WEBSOCKET = "--websocket" in sys.argv
|
||||
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):
|
||||
"""Set runtime primary font selection."""
|
||||
|
||||
Reference in New Issue
Block a user