From 3e782aff31b7dd0078ccf5ea8341c38a5c8f2870 Mon Sep 17 00:00:00 2001 From: hl-valdemar Date: Sun, 18 Oct 2026 02:05:25 +0200 Subject: [PATCH] fix: observe playback state to unhide native video surface --- Checks/app-startup.py | 7 +++++++ Checks/app-startup.swift | 6 ++++++ README.md | 2 ++ Sources/App/TerebiApp.swift | 2 +- 4 files changed, 16 insertions(+), 1 deletion(-) diff --git a/Checks/app-startup.py b/Checks/app-startup.py index 57af21a..3df602d 100644 --- a/Checks/app-startup.py +++ b/Checks/app-startup.py @@ -285,8 +285,15 @@ def check_cli_instances(root, app, fixtures): and state["position"] > 0 and state["hasVideo"] for state in states.values()), "cold default CLI opens one actual player window") first_pid = next(iter(first)) + renderer = first[first_pid]["window"] + if (not renderer.get("rendererAttached") or renderer.get("rendererHidden") + or abs(renderer["rendererWidth"] - renderer["videoWidth"]) > 1 + or abs(renderer["rendererHeight"] - renderer["videoHeight"]) > 1): + raise RuntimeError(f"playing renderer must fill its visible native surface: {renderer}") opened = input_request(first_pid, 1, lambda state: state["showsQueue"] and state["window"].get("videoOwnsMouse") is True + and state["window"].get("rendererAttached") is True + and state["window"].get("rendererHidden") is False and abs(state["window"]["videoWidth"] - first[first_pid]["window"]["videoWidth"]) < 1, "queue preserves video size and mouse target", action="queue", visible=True) window = opened[first_pid]["window"] diff --git a/Checks/app-startup.swift b/Checks/app-startup.swift index d79da8a..7a1c799 100644 --- a/Checks/app-startup.swift +++ b/Checks/app-startup.swift @@ -122,6 +122,12 @@ private enum CLIInstanceCheck { result["sidebarOwnsMouse"] = hit(NSPoint(x: surface.bounds.maxX + 130, y: surface.bounds.midY)) !== surface result["videoWidth"] = surface.bounds.width result["videoHeight"] = surface.bounds.height + if let video = PlayerModel.shared.videoView { + result["rendererWidth"] = video.bounds.width + result["rendererHeight"] = video.bounds.height + result["rendererAttached"] = video.superview === surface + result["rendererHidden"] = video.isHidden + } result["surfaceMovesOnMouseDown"] = surface.mouseDownCanMoveWindow let edges = [NSPoint(x: 1, y: content.bounds.midY), NSPoint(x: content.bounds.maxX - 1, y: content.bounds.midY), diff --git a/README.md b/README.md index e30c52c..fbf8e6c 100644 --- a/README.md +++ b/README.md @@ -155,6 +155,8 @@ make check The full suite requires a graphical macOS session and a working audio device; it launches isolated app instances and plays short test audio. Targeted `check-*` targets are listed in the [Makefile](Makefile). Visual appearance, physical media-key routing, and receiver behavior still need manual checks. +The startup checks verify that playing video remains attached, unhidden and sized to its native drag surface, including with the queue open; playback-state checks alone do not establish renderer visibility. + `make benchmark-playback` measures cold source startup, cached/uncached seeks, and sustained native/Cast playback with repeatable loopback fixtures. It requires the same macOS session/audio device and fixture-generation FFmpeg; results exclude live extraction, WAN/TLS, and physical-receiver latency. The [benchmark runner](Checks/playback-benchmark.py) accepts `--compare-native ` for alternating native-only comparisons; add `--seek-work` to isolate cache-miss seeks when both binaries support that mode. Preserve the baseline executable before rebuilding. diff --git a/Sources/App/TerebiApp.swift b/Sources/App/TerebiApp.swift index 77cd99e..2cb7ef4 100644 --- a/Sources/App/TerebiApp.swift +++ b/Sources/App/TerebiApp.swift @@ -1881,7 +1881,7 @@ private struct CaptionSelector: NSViewRepresentable { @MainActor struct PlaybackSurface: NSViewRepresentable { - let model: PlayerModel + @ObservedObject var model: PlayerModel func makeNSView(context: Context) -> DragView { DragView(model: model) } func updateNSView(_ view: DragView, context: Context) { view.updateVideo() }