Skip to content

feat(embedded-mpv): real macOS native fullscreen for the embedded player [4/7]#6

Closed
larsemig wants to merge 1 commit into
pr/03-embedded-mpv-immersivefrom
pr/04-embedded-mpv-native-fullscreen
Closed

feat(embedded-mpv): real macOS native fullscreen for the embedded player [4/7]#6
larsemig wants to merge 1 commit into
pr/03-embedded-mpv-immersivefrom
pr/04-embedded-mpv-native-fullscreen

Conversation

@larsemig

@larsemig larsemig commented Jul 7, 2026

Copy link
Copy Markdown
Owner

Note

Sidenote from the author: My review focus for this series was the architecture, not every implementation detail. If there are specific contribution guidelines I should follow (commit style, PR conventions, test expectations, etc.), please let me know and I will adapt the series accordingly.

Step 4 of the 7-PR series implementing the direction approved in 4gray#1105.

What

Real macOS native NSWindow fullscreen for the embedded-MPV player, replacing DOM fullscreen for it (web/PWA players keep DOM fullscreen unchanged):

  • Contract extension (backward-compatible): new optional PlayerFullscreenController delegate on app-player-controls (default null → built-in DOM behavior). The web players in steps 5–7 need zero changes.
  • Fill/freeze choreography (embedded_mpv.mm): the native surface enters autoresize "fill" mode with rendering frozen so the last frame scales cleanly through macOS's snapshot zoom; setFullScreen is deferred two rAF so the snapshot catches a clean full-bleed window.
  • OS-initiated exits reconcile: green button / Ctrl+Cmd+F / ESC flow back via the WINDOW:STATE_CHANGED broadcast (now enabled on macOS) without re-driving the window; teardown drops the window out of fullscreen so it is never stranded in a Space.
  • New IPC surface: WINDOW:SET_FULLSCREEN, EMBEDDED_MPV_SET_FILL — all bridge methods optional, graceful no-ops on older bridges.

Review notes

  • Renderer reconciliation path is spec-covered (reconcile-without-re-drive, teardown-exits, destroyed-window no-op); the native .mm fill logic is not unit-tested (consistent with the rest of the native layer).
  • Docs updated: embedded-mpv-native.md, player-controls-refactor.md.
  • ~+497/−52 lines. Lint + ui-playback/web/electron-backend suites green.

🗺️ Series roadmap (proposal 4gray#1105)

Step Branch Purpose
1 pr/01-player-controls-core Shared engine-agnostic controls layer (lib only, flag OFF)
2 pr/02-embedded-mpv-decompose Decompose embedded-MPV session controller (no behavior change)
3 pr/03-embedded-mpv-immersive Embedded MPV adopts shared controls + immersive overlay (macOS)
➡️ 4 pr/04-embedded-mpv-native-fullscreen Real macOS native fullscreen for embedded MPV
5 pr/05-html5-shared-controls HTML5 player on shared controls (flag OFF)
6 pr/06-vjs-shared-controls Video.js on shared controls (flag OFF)
7 pr/07-art-player-shared-controls ArtPlayer on shared controls (flag OFF)

Stacked series: each PR is based on the previous one, so every diff shows only its own step.

🤖 Generated with Claude Code

@larsemig
larsemig force-pushed the pr/03-embedded-mpv-immersive branch from d16774c to 6b7cb92 Compare July 7, 2026 19:00
@larsemig
larsemig force-pushed the pr/04-embedded-mpv-native-fullscreen branch from 297a620 to 2e8ba1d Compare July 7, 2026 19:00
Use native NSWindow fullscreen instead of DOM fullscreen for the
embedded player. Extend the player-controls contract with a fullscreen
delegate (PlayerFullscreenController + ControlsFullscreenBinding) and
add window-state IPC so OS-initiated exits (green button, Ctrl+Cmd+F,
ESC) reconcile with the in-app fullscreen state.

Review-driven hardening: the deferred window-fullscreen request is now
gated by a generation counter + destroyed flag so it becomes a no-op if
fullscreen is exited/re-toggled or the component is torn down before the
two requestAnimationFrame ticks run (pending rAF ids are cancelled in
ngOnDestroy); the fill snap is awaited (raced with a ~150ms timeout so a
hung IPC can not block fullscreen) before scheduling the window request;
and the fullscreen controller now requires BOTH the setMainWindowFullScreen
and setEmbeddedMpvFill bridges in canToggle (older bridges fall back to the
built-in DOM fullscreen path).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@larsemig
larsemig force-pushed the pr/03-embedded-mpv-immersive branch from 6b7cb92 to 6a2c1c0 Compare July 7, 2026 19:56
@larsemig
larsemig force-pushed the pr/04-embedded-mpv-native-fullscreen branch from 2e8ba1d to 2a85165 Compare July 7, 2026 19:56
@larsemig

larsemig commented Jul 7, 2026

Copy link
Copy Markdown
Owner Author

Used as template only

@larsemig larsemig closed this Jul 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant