From 837bc2823ab77e654f34608403d2ffd4e102abcf Mon Sep 17 00:00:00 2001 From: The_miro Date: Sun, 19 Jul 2026 16:21:28 +0200 Subject: [PATCH] fix(hyprdrive/station-bar): wall-clock safety net so the holo intro can't stall MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The materialise intro was driven only by the frame-clock tick, which stops advancing when the compositor withholds frame callbacks (e.g. the bar restarted into an idle compositor) — freezing it at t=0 with content stuck at opacity 0, i.e. permanent static. Arm a GLib.timeout alongside start_intro() that force-resolves the intro regardless, so content always appears. Co-Authored-By: Claude Opus 4.8 --- .../hyprdrive/station-bar/lib/hologram.py | 39 +++++++++++++++++-- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/desktopenvs/hyprdrive/station-bar/lib/hologram.py b/desktopenvs/hyprdrive/station-bar/lib/hologram.py index 19a2921..8b62084 100644 --- a/desktopenvs/hyprdrive/station-bar/lib/hologram.py +++ b/desktopenvs/hyprdrive/station-bar/lib/hologram.py @@ -19,7 +19,7 @@ import cairo import gi gi.require_version("Gtk", "4.0") -from gi.repository import Gtk # noqa: E402 +from gi.repository import GLib, Gtk # noqa: E402 # Same CyberQueer violet/magenta/red combo as the rest of the suite's hologram. _VIOLET = (0x50 / 255, 0x18 / 255, 0xDD / 255) @@ -58,6 +58,13 @@ class HologramOverlay: self._intro_t: float | None = None # >=0 while the materialise intro plays self._outro_t: float | None = None # >=0 while the closing dissolve plays self._outro_done = None # callback fired when the dissolve finishes + # Wall-clock safety net for the intro: the frame-clock tick that normally + # advances the intro only fires while the compositor sends frame callbacks. + # When the bar is (re)started into an otherwise-idle compositor those can + # stall, freezing the intro at t=0 — i.e. the bar sits in permanent static + # with content stuck at opacity 0. This timeout force-resolves the intro on + # real time regardless, so content always appears. + self._intro_deadline_id: int | None = None self._mask_cache: tuple | None = None # (w, h, pattern) edge-fade mask self.widget = Gtk.DrawingArea() @@ -76,9 +83,7 @@ class HologramOverlay: if self._intro_t is not None: self._intro_t += dt if self._intro_t >= self.INTRO_DURATION: - self._intro_t = None - if self._fade_widget is not None: - self._fade_widget.set_opacity(1.0) + self._finish_intro() elif self._fade_widget is not None: p = self._intro_t / self.INTRO_DURATION self._fade_widget.set_opacity(p * p * (3 - 2 * p)) # smooth ramp 0->1 @@ -95,6 +100,17 @@ class HologramOverlay: done() self.widget.queue_draw() + def _finish_intro(self) -> None: + """End the intro: clear its state, cancel the safety timeout, and make the + content fully opaque. Safe to call from either the tick or the timeout.""" + self._intro_t = None + if self._intro_deadline_id is not None: + GLib.source_remove(self._intro_deadline_id) + self._intro_deadline_id = None + if self._fade_widget is not None: + self._fade_widget.set_opacity(1.0) + self.widget.queue_draw() # in case the frame clock has stalled + def start_intro(self) -> None: """Kick off the 'hologram materialising out of static' opening effect.""" if self.enabled: @@ -103,6 +119,18 @@ class HologramOverlay: self._intro_t = 0.0 if self._fade_widget is not None: self._fade_widget.set_opacity(0.0) # start hidden; tick() ramps it up + # Wall-clock backstop: if frame callbacks stall, force the intro done a + # little past its nominal length so content can never get stuck hidden. + if self._intro_deadline_id is not None: + GLib.source_remove(self._intro_deadline_id) + self._intro_deadline_id = GLib.timeout_add( + int(self.INTRO_DURATION * 1000) + 150, self._on_intro_deadline) + + def _on_intro_deadline(self) -> bool: + self._intro_deadline_id = None + if self._intro_t is not None: + self._finish_intro() + return False # one-shot def start_outro(self, on_done) -> None: """Play a quick reverse of the intro (content dissolving back into static), @@ -111,6 +139,9 @@ class HologramOverlay: on_done() return self._intro_t = None # cancel any in-flight opening intro + if self._intro_deadline_id is not None: + GLib.source_remove(self._intro_deadline_id) + self._intro_deadline_id = None self._outro_t = 0.0 self._outro_done = on_done