tauri_plugin_libmpv/
lib.rs

1use tauri::{
2    plugin::{Builder, TauriPlugin},
3    Manager, RunEvent, Runtime, WindowEvent,
4};
5
6pub use models::*;
7
8#[cfg(desktop)]
9mod desktop;
10#[cfg(mobile)]
11mod mobile;
12#[cfg(desktop)]
13mod wrapper;
14
15mod commands;
16mod error;
17mod models;
18mod utils;
19
20pub use error::{Error, Result};
21
22#[cfg(desktop)]
23use desktop::Mpv;
24#[cfg(mobile)]
25use mobile::Mpv;
26
27pub trait MpvExt<R: Runtime> {
28    fn mpv(&self) -> &Mpv<R>;
29}
30
31impl<R: Runtime, T: Manager<R>> crate::MpvExt<R> for T {
32    fn mpv(&self) -> &Mpv<R> {
33        self.state::<Mpv<R>>().inner()
34    }
35}
36
37pub fn init<R: Runtime>() -> TauriPlugin<R> {
38    Builder::new("libmpv")
39        .invoke_handler(tauri::generate_handler![
40            commands::init,
41            commands::destroy,
42            commands::command,
43            commands::set_property,
44            commands::get_property,
45            commands::set_video_margin_ratio,
46        ])
47        .setup(|app, api| {
48            unsafe {
49                let locale = std::ffi::CString::new("C").unwrap();
50                libc::setlocale(libc::LC_NUMERIC, locale.as_ptr());
51            }
52
53            #[cfg(mobile)]
54            let mpv = mobile::init(app, api)?;
55            #[cfg(desktop)]
56            let mpv = desktop::init(app, api)?;
57            app.manage(mpv);
58            Ok(())
59        })
60        .on_event(|app_handle, run_event| {
61            if let RunEvent::WindowEvent {
62                label,
63                event: WindowEvent::CloseRequested { api, .. },
64                ..
65            } = run_event
66            {
67                let mpv_state = app_handle.state::<Mpv<R>>();
68
69                let instance_exists = {
70                    let instances_lock = match mpv_state.instances.lock() {
71                        Ok(guard) => guard,
72                        Err(poisoned) => {
73                            log::warn!("Mutex for mpv instances was poisoned. Recovering.");
74                            poisoned.into_inner()
75                        }
76                    };
77                    instances_lock.contains_key(label)
78                };
79
80                if instance_exists {
81                    api.prevent_close();
82
83                    let app_handle_clone = app_handle.clone();
84                    let window_label = label.to_string();
85
86                    tauri::async_runtime::spawn(async move {
87                        log::info!(
88                            "Close requested for '{}', destroying mpv instance first...",
89                            &window_label
90                        );
91
92                        if let Err(e) = app_handle_clone.mpv().destroy(&window_label) {
93                            log::error!(
94                                "Failed to destroy mpv for '{}': {}. Still closing.",
95                                &window_label,
96                                e
97                            );
98                        }
99
100                        if let Some(window) = app_handle_clone.get_webview_window(&window_label) {
101                            if let Err(e) = window.close() {
102                                log::error!("Failed to close window '{}': {}", &window_label, e);
103                            }
104                        }
105                    });
106                }
107            }
108        })
109        .build()
110}