diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 32751700..608acafd 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -2191,11 +2191,13 @@ dependencies = [ "env_logger", "futures-util", "getrandom 0.2.16", + "gtk", "hostname", "jni 0.21.1", "keyring", "libc", "libmpv", + "libmpv-sys", "log", "ndk-context", "rand 0.8.7", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 553eeca7..7d7d1e5d 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -114,6 +114,25 @@ libc = "0.2" # than changing it. To take upstream fixes, bump this deliberately. libmpv = { git = "https://github.com/ParadoxSpiral/libmpv-rs.git", rev = "3e6c389b716f52a595cc5e8e3fa1f96cb76b3de7" } +# The raw FFI bindings behind `libmpv`, pinned to the *same* revision so the two +# can never describe different ABIs. +# +# Needed because the safe crate's `render` module is an empty stub at this +# revision — the render API (`mpv_render_context_create` and friends) exists only +# in the sys bindings, which do carry all of it. `Mpv::ctx` is public, so the +# render context can be built over the same handle the safe wrapper drives. This +# is what makes native video reachable *without* first completing the libmpv2 +# migration, which the spike's use of `libmpv2-sys` had implied was a +# prerequisite. +# +# TRACES: UR-080 | DR-230, IR-033 +libmpv-sys = { git = "https://github.com/ParadoxSpiral/libmpv-rs.git", rev = "3e6c389b716f52a595cc5e8e3fa1f96cb76b3de7" } + +# Same major as the one Tauri/wry already resolve, so `gtk_window()` and +# `default_vbox()` hand back types this crate can name rather than a second, +# incompatible GTK. +gtk = "0.18" + # JNI for Android ExoPlayer integration [target.'cfg(target_os = "android")'.dependencies] jni = "0.21" diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index a51fa8e0..5b8e76ce 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -1210,6 +1210,56 @@ pub fn run() { // listened for on the frontend via the generated bindings. builder.mount_events(app); + // Native video surface: put a GL area under Tauri's webview so mpv + // can draw beneath the controls (UR-080 / DR-230). + // + // 🔴 OFF BY DEFAULT — the naive reparent crashes the app on the + // first click. `tauri-runtime-wry`'s undecorated-resizing handler + // walks a hard-coded two-hop path on every button press in the + // webview: + // + // webview.parent() // "This one should be GtkBox" + // .parent() // ...and this one the GtkWindow + // .downcast::().unwrap() + // + // Wrapping the webview in a GtkOverlay makes that chain + // webview → GtkOverlay → GtkBox, the downcast fails, and because the + // panic is non-unwinding it aborts the process. The decoration check + // that would otherwise make this handler inert runs *after* the + // unwrap, so no window configuration avoids it. + // + // This is the "only place Tauri-specific behaviour could still bite" + // that the spike named as the untested half of G1. It bites. The + // surface attaches perfectly and then dies on interaction, so + // "attached successfully" in the log is not the gate — a click is. + // + // Kept behind an env var rather than deleted so the next attempt has + // something to iterate on: JELLYTAU_NATIVE_VIDEO=1 bun run tauri dev + // + // TRACES: UR-080 | DR-230 + #[cfg(target_os = "linux")] + if std::env::var("JELLYTAU_NATIVE_VIDEO").as_deref() == Ok("1") { + use tauri::Manager; + log::warn!( + "[INIT] JELLYTAU_NATIVE_VIDEO=1 — attaching the experimental \ + video surface; the app will abort on the first click until \ + the widget-tree shape is solved (DR-230)" + ); + if let Some(window) = app.get_webview_window("main") { + match window.default_vbox() { + Ok(vbox) => match crate::player::video_surface::attach(&vbox) { + Ok(_surface) => { + info!("[INIT] Native video surface attached"); + } + Err(e) => log::warn!("[INIT] Native video surface unavailable: {e}"), + }, + Err(e) => { + log::warn!("[INIT] No GTK vbox for the main window: {e}") + } + } + } + } + // In-app update, desktop only. // // Registered here rather than in the builder chain above because a diff --git a/src-tauri/src/player/mod.rs b/src-tauri/src/player/mod.rs index 1ae2e871..bd11bf57 100644 --- a/src-tauri/src/player/mod.rs +++ b/src-tauri/src/player/mod.rs @@ -24,6 +24,14 @@ pub mod android; #[cfg(target_os = "linux")] pub mod mpv_backend; +/// The native video surface mpv renders into (UR-080 / DR-230). +/// +/// Linux-gated for now because the surface is GTK. Everything *around* it — the +/// render context, its lifetime, frame pacing, the device profile — is +/// deliberately not, so Windows reuses it behind its own surface. +#[cfg(target_os = "linux")] +pub mod video_surface; + // Platforms with no native audio backend (e.g. Windows) render audio-only // playback through a webview