mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-10-06 07:15:09 +00:00
fix(vegas): pause for STATIC plugins where their turn falls in the strip (#651)
The static trigger peeked at the front of StreamManager's segment buffer, which continuous scrolling (the default) never advances -- it extends the strip with take_next_group() -- so the same first segment was examined on every frame. A STATIC plugin paused the scroll only if it was first, once, at startup; otherwise it scrolled past as ordinary content. Swap mode had the same problem for any STATIC plugin not first in its cycle. The render pipeline now records a marker (strip column, plugin id) for each STATIC plugin where the strip is built -- composition and every extension -- shifts the markers when the scrolled prefix is trimmed, and clears them on reset. The coordinator pauses when the scroll reaches the next marker: a tuple comparison per frame instead of a lock, a plugin lookup and a get_vegas_display_mode() call. take_next_group() no longer renders STATIC plugins' content. The pause calls display() under the plugin lock and is timed with the monotonic clock. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -667,6 +667,37 @@ class StreamManager:
|
||||
grouped.append((segment.plugin_id, list(segment.images)))
|
||||
return grouped
|
||||
|
||||
def get_static_layout(self) -> List[Tuple[str, bool]]:
|
||||
"""
|
||||
The buffer's composition order, with STATIC segments kept in place.
|
||||
|
||||
get_grouped_content_for_composition() drops STATIC segments because
|
||||
they contribute no columns; this says where they sat. Each entry is
|
||||
(plugin_id, is_static), and only segments that composition keeps or
|
||||
that are STATIC are listed, so the non-static entries line up one to
|
||||
one with get_grouped_content_for_composition()'s groups.
|
||||
"""
|
||||
layout: List[Tuple[str, bool]] = []
|
||||
with self._buffer_lock:
|
||||
for segment in self._active_buffer:
|
||||
if segment.display_mode == VegasDisplayMode.STATIC:
|
||||
layout.append((segment.plugin_id, True))
|
||||
elif segment.images:
|
||||
layout.append((segment.plugin_id, False))
|
||||
return layout
|
||||
|
||||
def is_static_plugin(self, plugin_id: str) -> bool:
|
||||
"""Whether a loaded plugin asks Vegas to pause for it (STATIC mode)."""
|
||||
plugin = getattr(self.plugin_manager, 'plugins', {}).get(plugin_id)
|
||||
if plugin is None:
|
||||
return False
|
||||
try:
|
||||
return plugin.get_vegas_display_mode() == VegasDisplayMode.STATIC
|
||||
except Exception:
|
||||
logger.debug("[%s] get_vegas_display_mode() failed; treating as not STATIC",
|
||||
plugin_id, exc_info=True)
|
||||
return False
|
||||
|
||||
def take_next_group(
|
||||
self, count: Optional[int] = None, offscreen_only: bool = False
|
||||
) -> List[Tuple[str, Optional[List[Image.Image]]]]:
|
||||
@@ -692,6 +723,8 @@ class StreamManager:
|
||||
content path now draws on a canvas of its own, so a background
|
||||
fetch that comes back empty had nothing to show, and ``images``
|
||||
is an empty list rather than a request for the render thread.
|
||||
A STATIC plugin is also returned with an empty list, unfetched: it
|
||||
pauses the scroll instead of adding to it (see is_static_plugin).
|
||||
"""
|
||||
if count is None:
|
||||
count = self.config.plugins_per_cycle
|
||||
@@ -717,6 +750,12 @@ class StreamManager:
|
||||
plugin = plugins.get(plugin_id)
|
||||
if not plugin:
|
||||
continue
|
||||
if self.is_static_plugin(plugin_id):
|
||||
# A STATIC plugin pauses the scroll rather than scrolling by,
|
||||
# so it contributes no columns. It keeps its place in the
|
||||
# group (empty) so the pipeline can mark where its turn falls.
|
||||
group.append((plugin_id, []))
|
||||
continue
|
||||
try:
|
||||
images = self.plugin_adapter.get_content(
|
||||
plugin, plugin_id, offscreen_only=offscreen_only)
|
||||
|
||||
Reference in New Issue
Block a user