fix(display): end the scroll before the schedule-off blank and WiFi notice (#721)

The schedule-off blank and the WiFi notice are drawn by the display controller, not dispatched to a plugin, so #716's handover never reached them. Drawn while the last scroll's state was still set, the blank went out with the ticker's lagging rows on a scan-compensated panel and stayed up for its 60 s dwell, and the notice's redraws (which #712 now shows over a running scroller or Vegas) were timed as 0.5-1 s freezes and logged as a mid-scroll Render stall. The controller now calls set_scrolling_state(False) before drawing either; a scroller that resumes sets the state again on its next frame.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Chuck
2026-10-01 20:46:01 -04:00
committed by GitHub
co-authored by Claude Opus 5.5
parent 0e78e06eb9
commit 85be4bf25d
4 changed files with 100 additions and 17 deletions
+18
View File
@@ -2592,6 +2592,7 @@ class DisplayController:
"""
# Clear display when schedule makes it inactive to ensure blank screen
# (not showing initialization screen)
self._end_scroll_before_core_screen()
try:
self.display_manager.clear()
self.display_manager.update_display()
@@ -2692,6 +2693,7 @@ class DisplayController:
wifi_status_data = self._check_wifi_status_message()
if not wifi_status_data:
return False
self._end_scroll_before_core_screen()
if not self._display_wifi_status_message(wifi_status_data):
# Display failed, clear the status and continue normally
return False
@@ -2798,6 +2800,22 @@ class DisplayController:
end_scroll()
return static_screen
def _end_scroll_before_core_screen(self) -> None:
"""End any scroll before the controller draws a screen of its own.
The schedule-off blank and the WiFi notice are drawn by the
controller, not by a plugin, so they never pass through
_dispatch_first_frame and its handover. Drawn while the last scroll's state is still set, the blank
went out with the scroller's lagging rows (on a scan-compensated
panel) for its 60 s dwell, and the notice's redraws were timed as
freezes of the old scroll. Ending the state first sends them out as
drawn, at hold 1, as static frames. A scroller that resumes sets the
state again on its next frame.
"""
set_scrolling_state = getattr(self.display_manager, 'set_scrolling_state', None)
if set_scrolling_state is not None:
set_scrolling_state(False)
def _note_screen_handover(self) -> None:
"""Tag the frame the first dispatch is about to present.