forked from genewildish/Mainline
refactor: rename rendering components and variables for clarity, distinguishing between message, ticker, and scroll motion layers.
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
"""
|
"""
|
||||||
Scroll engine — the main frame loop with headline rendering and message display.
|
Render engine — ticker content, scroll motion, message panel, and firehose overlay.
|
||||||
Depends on: config, terminal, render, effects, ntfy, mic.
|
Depends on: config, terminal, render, effects, ntfy, mic.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -40,7 +40,7 @@ def _overlay_segments(ansi_line):
|
|||||||
|
|
||||||
|
|
||||||
def stream(items, ntfy_poller, mic_monitor):
|
def stream(items, ntfy_poller, mic_monitor):
|
||||||
"""Main rendering loop. Scrolls headlines, shows ntfy messages, applies effects."""
|
"""Main render loop with four layers: message, ticker, scroll motion, firehose."""
|
||||||
random.shuffle(items)
|
random.shuffle(items)
|
||||||
pool = list(items)
|
pool = list(items)
|
||||||
seen = set()
|
seen = set()
|
||||||
@@ -52,20 +52,25 @@ def stream(items, ntfy_poller, mic_monitor):
|
|||||||
|
|
||||||
w, h = tw(), th()
|
w, h = tw(), th()
|
||||||
fh = config.FIREHOSE_H if config.FIREHOSE else 0
|
fh = config.FIREHOSE_H if config.FIREHOSE else 0
|
||||||
sh = h # headline scroll uses full viewport
|
ticker_view_h = h # ticker uses full viewport; firehose overlays it
|
||||||
GAP = 3 # blank rows between headlines
|
GAP = 3 # blank rows between headlines
|
||||||
scroll_interval = config.SCROLL_DUR / (sh + 15) * 2
|
scroll_step_interval = config.SCROLL_DUR / (ticker_view_h + 15) * 2
|
||||||
|
|
||||||
# active blocks: (content_rows, color, canvas_y, meta_idx)
|
# Taxonomy:
|
||||||
|
# - message: ntfy interrupt panel at top
|
||||||
|
# - ticker: large headline text content
|
||||||
|
# - scroll: upward camera motion applied to ticker content
|
||||||
|
# - firehose: carriage-return style overlay drifting above ticker
|
||||||
|
# Active ticker blocks: (content_rows, color, canvas_y, meta_idx)
|
||||||
active = []
|
active = []
|
||||||
cam = 0 # viewport top in virtual canvas coords
|
scroll_cam = 0 # viewport top in virtual canvas coords
|
||||||
next_y = sh # canvas-y where next block starts (off-screen bottom)
|
ticker_next_y = ticker_view_h # canvas-y where next block starts (off-screen bottom)
|
||||||
noise_cache = {}
|
noise_cache = {}
|
||||||
scroll_accum = 0.0
|
scroll_motion_accum = 0.0
|
||||||
firehose_top = h - fh
|
firehose_top = h - fh
|
||||||
firehose_rows = [firehose_line(items, w) for _ in range(fh)] if fh > 0 else []
|
firehose_rows = [firehose_line(items, w) for _ in range(fh)] if fh > 0 else []
|
||||||
firehose_accum = 0.0
|
firehose_accum = 0.0
|
||||||
firehose_interval = max(config.FRAME_DT, scroll_interval * 1.3)
|
firehose_interval = max(config.FRAME_DT, scroll_step_interval * 1.3)
|
||||||
|
|
||||||
def _noise_at(cy):
|
def _noise_at(cy):
|
||||||
if cy not in noise_cache:
|
if cy not in noise_cache:
|
||||||
@@ -81,7 +86,7 @@ def stream(items, ntfy_poller, mic_monitor):
|
|||||||
t0 = time.monotonic()
|
t0 = time.monotonic()
|
||||||
w, h = tw(), th()
|
w, h = tw(), th()
|
||||||
fh = config.FIREHOSE_H if config.FIREHOSE else 0
|
fh = config.FIREHOSE_H if config.FIREHOSE else 0
|
||||||
sh = h
|
ticker_view_h = h
|
||||||
|
|
||||||
# ── Check for ntfy message ────────────────────────
|
# ── Check for ntfy message ────────────────────────
|
||||||
msg_h = 0 # rows consumed by message zone at top
|
msg_h = 0 # rows consumed by message zone at top
|
||||||
@@ -123,29 +128,28 @@ def stream(items, ntfy_poller, mic_monitor):
|
|||||||
row_idx += 1
|
row_idx += 1
|
||||||
msg_h = row_idx
|
msg_h = row_idx
|
||||||
|
|
||||||
# Effective ticker zone: below message
|
# Effective ticker draw height: below message
|
||||||
scroll_h = sh - msg_h
|
ticker_h = ticker_view_h - msg_h
|
||||||
|
|
||||||
# ── Scroll: headline rendering (always runs) ──────
|
# ── Ticker content + scroll motion (always runs) ──
|
||||||
# Advance scroll on schedule
|
scroll_motion_accum += config.FRAME_DT
|
||||||
scroll_accum += config.FRAME_DT
|
while scroll_motion_accum >= scroll_step_interval:
|
||||||
while scroll_accum >= scroll_interval:
|
scroll_motion_accum -= scroll_step_interval
|
||||||
scroll_accum -= scroll_interval
|
scroll_cam += 1
|
||||||
cam += 1
|
|
||||||
|
|
||||||
# Enqueue new headlines when room at the bottom
|
# Enqueue new headlines when room at the bottom
|
||||||
while next_y < cam + sh + 10 and queued < config.HEADLINE_LIMIT:
|
while ticker_next_y < scroll_cam + ticker_view_h + 10 and queued < config.HEADLINE_LIMIT:
|
||||||
t, src, ts = next_headline(pool, items, seen)
|
t, src, ts = next_headline(pool, items, seen)
|
||||||
content, hc, midx = make_block(t, src, ts, w)
|
ticker_content, hc, midx = make_block(t, src, ts, w)
|
||||||
active.append((content, hc, next_y, midx))
|
active.append((ticker_content, hc, ticker_next_y, midx))
|
||||||
next_y += len(content) + GAP
|
ticker_next_y += len(ticker_content) + GAP
|
||||||
queued += 1
|
queued += 1
|
||||||
|
|
||||||
# Prune off-screen blocks and stale noise
|
# Prune off-screen blocks and stale noise
|
||||||
active = [(c, hc, by, mi) for c, hc, by, mi in active
|
active = [(c, hc, by, mi) for c, hc, by, mi in active
|
||||||
if by + len(c) > cam]
|
if by + len(c) > scroll_cam]
|
||||||
for k in list(noise_cache):
|
for k in list(noise_cache):
|
||||||
if k < cam:
|
if k < scroll_cam:
|
||||||
del noise_cache[k]
|
del noise_cache[k]
|
||||||
|
|
||||||
# Firehose overlay drift (slower than main ticker)
|
# Firehose overlay drift (slower than main ticker)
|
||||||
@@ -164,15 +168,15 @@ def stream(items, ntfy_poller, mic_monitor):
|
|||||||
firehose_top = h - fh
|
firehose_top = h - fh
|
||||||
|
|
||||||
# Draw ticker zone (below message zone)
|
# Draw ticker zone (below message zone)
|
||||||
top_zone = max(1, int(scroll_h * 0.25))
|
top_zone = max(1, int(ticker_h * 0.25))
|
||||||
bot_zone = max(1, int(scroll_h * 0.10))
|
bot_zone = max(1, int(ticker_h * 0.10))
|
||||||
grad_offset = (time.monotonic() * config.GRAD_SPEED) % 1.0
|
grad_offset = (time.monotonic() * config.GRAD_SPEED) % 1.0
|
||||||
scroll_buf_start = len(buf) # track where scroll rows start in buf
|
ticker_buf_start = len(buf) # track where ticker rows start in buf
|
||||||
for r in range(scroll_h):
|
for r in range(ticker_h):
|
||||||
scr_row = msg_h + r + 1 # 1-indexed ANSI screen row
|
scr_row = msg_h + r + 1 # 1-indexed ANSI screen row
|
||||||
cy = cam + r
|
cy = scroll_cam + r
|
||||||
top_f = min(1.0, r / top_zone) if top_zone > 0 else 1.0
|
top_f = min(1.0, r / top_zone) if top_zone > 0 else 1.0
|
||||||
bot_f = min(1.0, (scroll_h - 1 - r) / bot_zone) if bot_zone > 0 else 1.0
|
bot_f = min(1.0, (ticker_h - 1 - r) / bot_zone) if bot_zone > 0 else 1.0
|
||||||
row_fade = min(top_f, bot_f)
|
row_fade = min(top_f, bot_f)
|
||||||
drawn = False
|
drawn = False
|
||||||
for content, hc, by, midx in active:
|
for content, hc, by, midx in active:
|
||||||
@@ -203,17 +207,16 @@ def stream(items, ntfy_poller, mic_monitor):
|
|||||||
else:
|
else:
|
||||||
buf.append(f"\033[{scr_row};1H\033[K")
|
buf.append(f"\033[{scr_row};1H\033[K")
|
||||||
|
|
||||||
|
# Glitch — base rate + mic-reactive spikes (ticker zone only)
|
||||||
# Glitch — base rate + mic-reactive spikes (scroll zone only)
|
|
||||||
mic_excess = mic_monitor.excess
|
mic_excess = mic_monitor.excess
|
||||||
glitch_prob = 0.32 + min(0.9, mic_excess * 0.16)
|
glitch_prob = 0.32 + min(0.9, mic_excess * 0.16)
|
||||||
n_hits = 4 + int(mic_excess / 2)
|
n_hits = 4 + int(mic_excess / 2)
|
||||||
scroll_buf_len = len(buf) - scroll_buf_start
|
ticker_buf_len = len(buf) - ticker_buf_start
|
||||||
if random.random() < glitch_prob and scroll_buf_len > 0:
|
if random.random() < glitch_prob and ticker_buf_len > 0:
|
||||||
for _ in range(min(n_hits, scroll_buf_len)):
|
for _ in range(min(n_hits, ticker_buf_len)):
|
||||||
gi = random.randint(0, scroll_buf_len - 1)
|
gi = random.randint(0, ticker_buf_len - 1)
|
||||||
scr_row = msg_h + gi + 1
|
scr_row = msg_h + gi + 1
|
||||||
buf[scroll_buf_start + gi] = f"\033[{scr_row};1H{glitch_bar(w)}"
|
buf[ticker_buf_start + gi] = f"\033[{scr_row};1H{glitch_bar(w)}"
|
||||||
|
|
||||||
if config.FIREHOSE and fh > 0:
|
if config.FIREHOSE and fh > 0:
|
||||||
for fr, fline in enumerate(firehose_rows):
|
for fr, fline in enumerate(firehose_rows):
|
||||||
|
|||||||
Reference in New Issue
Block a user