libmpv_client/
handle.rs

1//! Definition and implementation of [`Handle`], this crate's primary interface to mpv.
2
3use std::ffi::{CStr, CString};
4use std::mem::MaybeUninit;
5use std::ops::Deref;
6use std::ptr::null;
7
8use libmpv_client_sys as mpv;
9use libmpv_client_sys::mpv_node;
10use crate::*;
11use crate::error::{error_to_result, error_to_result_code};
12use crate::event::LogLevel;
13use crate::types::traits::{MpvRecv, MpvSend, MpvSendInternal};
14
15/// The primary interface to mpv.
16///
17/// This [`Handle`] must be created by mpv; usually it is passed in from mpv to a cplugin via `mpv_open_cplugin(*mpv_handle)`.
18/// See [`Handle::from_ptr()`] for an example.
19pub struct Handle {
20    handle: *mut mpv_handle
21}
22
23impl Handle {
24    /// Creates a [`Handle`] from the provided pointer to a [`mpv_handle`].
25    ///
26    /// This [`mpv_handle`] must be created by mpv, usually passed in via mpv's call into `mpv_open_cplugin(*mpv_handle)`.
27    ///
28    /// # Example
29    /// ```
30    ///# use libmpv_client::*;
31    ///#
32    /// #[unsafe(no_mangle)]
33    /// extern "C" fn mpv_open_cplugin(ptr: *mut mpv_handle) -> std::os::raw::c_int {
34    ///     let handle = Handle::from_ptr(ptr);
35    ///     // ...
36    ///#     0
37    /// }
38    /// ```
39    #[must_use]
40    pub fn from_ptr(handle: *mut mpv_handle) -> Self {
41        Handle {
42            handle
43        }
44    }
45
46    /// Return the [`MPV_CLIENT_API_VERSION`](libmpv_client_sys::MPV_CLIENT_API_VERSION) the mpv source has been compiled with.
47    pub fn client_api_version() -> u64 {
48        unsafe { mpv::client_api_version() as u64 }
49    }
50
51    /// Return the name of this client [`Handle`].
52    ///
53    /// Every client has its own unique name, which is mostly used for user interface purposes.
54    pub fn client_name(&self) -> Result<String> {
55        let c_str = unsafe { CStr::from_ptr(mpv::client_name(self.handle)) };
56        Ok(c_str.to_str()?.to_string())
57    }
58
59    /// Return the ID of this client [`Handle`].
60    ///
61    /// Every client has its own unique ID. This ID is never reused by the core, even if the [`Handle`] at hand gets destroyed and new handles get allocated.
62    ///
63    /// Some mpv APIs (not necessarily all) accept a name in the form `@id` in addition to the proper [`Handle::client_name()`], where `id` is the ID in decimal form (e.g. `@123`).
64    /// For example, the [`script-message-to`](https://mpv.io/manual/stable/#command-interface-script-message-to[-]]])
65    /// command takes the client name as the first argument but also accepts the client ID formatted in this manner.
66    pub fn client_id(&self) -> i64 {
67        unsafe { mpv::client_id(self.handle) }
68    }
69
70    /// Create a new mpv instance and an associated client API [`Client`] to control the mpv instance.
71    ///
72    /// This instance is in a pre-initialized state and needs to be initialized to be actually used with most other API functions.
73    ///
74    /// Some API functions will return [`Error::Uninitialized`] in the uninitialized state.
75    /// You can call [`Handle::set_property()`] to set initial options.
76    /// After this, call [`Client::initialize()`] to start the player, and then use e.g. [`Handle::command()`] to start playback of a file.
77    ///
78    /// The point of separating [`Client`] creation and actual initialization is that you can configure things which can't be changed during runtime.
79    ///
80    /// Unlike the command line player, this will have initial settings suitable for embedding in applications. The following settings are different:
81    /// - `stdin`/`stdout`/`stderr` and the terminal will never be accessed.
82    ///   This is equivalent to setting the [`--terminal=no`](https://mpv.io/manual/stable/#options-terminal) option. (Technically, this also suppresses C signal handling.)
83    /// - No config files will be loaded. This is roughly equivalent to using [`--no-config`](https://mpv.io/manual/stable/#options-no-config).
84    ///   Since libmpv 1.15, you can actually re-enable this option, which will make libmpv load config files during [`Client::initialize()`].
85    ///   If you do this, you are strongly encouraged to set the [`config-dir`](https://mpv.io/manual/stable/#options-config-dir) option too.
86    ///   (Otherwise it will load the mpv command line player's config.)
87    /// - Idle mode is enabled, which means the playback core will enter idle mode if there are no more files to play on the internal playlist, instead of exiting.
88    ///   This is equivalent to the [`--idle`](https://mpv.io/manual/stable/#options-idle) option.
89    /// - Disable parts of input handling.
90    /// - Most of the different settings can be viewed with the command line player by running `mpv --show-profile=libmpv`.
91    ///
92    /// All this assumes that API users want an mpv instance that is strictly isolated from the command line player's configuration, user settings, and so on.
93    /// You can re-enable disabled features by setting the appropriate options.
94    ///
95    /// The mpv command line parser is not available through this API, but you can set individual options with [`Handle::set_property()`].
96    /// Files for playback must be loaded with [`Handle::command()`] or others.
97    ///
98    /// # Concurrency
99    /// Note that you should avoid doing concurrent accesses on the uninitialized client handle.
100    /// (Whether concurrent access is definitely allowed or not has yet to be decided by mpv.)
101    pub fn create() -> Client {
102        let handle = unsafe { mpv::create() };
103        Client(Handle::from_ptr(handle))
104    }
105
106    /// Create a new [`Client`] connected to the same player core as `self`.
107    /// This context has its own event queue, its own [`Handle::request_event()`] state, its own [`Handle::request_log_messages()`] state,
108    /// its own set of observed properties, and its own state for asynchronous operations. Otherwise, everything is shared.
109    ///
110    /// This client should be destroyed with [`Client::destroy()`] if no longer needed.
111    /// The core will live as long as there is at least 1 handle referencing it.
112    /// Any handle can make the core quit, which will result in every handle receiving [`Event::Shutdown`].
113    pub fn create_client(&self, name: &str) -> Result<Client> {
114        let name_str = CString::new(name)?;
115
116        let handle = unsafe { mpv::create_client(self.handle, name_str.as_ptr()) };
117        Ok(Client(Handle::from_ptr(handle)))
118    }
119
120    /// This is the same as [`Handle::create_client()`], but the created [`mpv_handle`] is treated as a weak reference.
121    /// If all handles referencing a core are weak references, the core is automatically destroyed. (This still goes through normal shutdown, of course.
122    /// Effectively, if the last non-weak handle is destroyed, then the weak handles receive [`Event::Shutdown`] and are asked to terminate as well.)
123    ///
124    /// Note if you want to use this like refcounting: you have to be aware that [`Client::terminate_destroy()`] _and_ [`Client::destroy()`]
125    /// for the last non-weak [`mpv_handle`] will block until all weak handles are destroyed.
126    pub fn create_weak_client(&self, name: &str) -> Result<Client> {
127        let name_str = CString::new(name)?;
128
129        let handle = unsafe { mpv::create_weak_client(self.handle, name_str.as_ptr()) };
130        Ok(Client(Handle::from_ptr(handle)))
131    }
132
133    /// Load a config file. This parses the file and sets every entry in the config file's default section as if [`Handle::set_option()`] is called.
134    ///
135    /// The filename should be an absolute path. If it isn't, the actual path used is unspecified. (Note: an absolute path starts with '`/`' on UNIX.)
136    /// If the file wasn't found, [`Error::InvalidParameter`] is returned.
137    ///
138    /// If a fatal error happens when parsing a config file, [`Error::OptionError`] is returned.
139    /// Errors when setting options as well as other types or errors are ignored (even if options do not exist).
140    /// You can still try to capture the resulting error messages with [`Handle::request_log_messages()`].
141    /// Note that it's possible that some options were successfully set even if any of these errors happen.
142    pub fn load_config_file(&self, filename: &str) -> Result<()> {
143        let filename_str = CString::new(filename)?;
144
145        let err = unsafe { mpv::load_config_file(self.handle, filename_str.as_ptr()) };
146        error_to_result(err)
147    }
148
149    /// Return the internal time in nanoseconds. This has an arbitrary start offset but will never wrap or go backwards.
150    ///
151    /// Note that this is always the real time and doesn't necessarily have to do with playback time.
152    /// For example, playback could go faster or slower due to playback speed, or due to playback being paused.
153    /// Use the `time-pos` property instead to get the playback status.
154    ///
155    /// Unlike other libmpv APIs, this can be called at absolutely any time (even within wakeup callbacks), as long as the [`Handle`] is valid.
156    ///
157    /// Safe to be called from mpv render API threads.
158    pub fn get_time_ns(&self) -> i64 {
159        unsafe { mpv::get_time_ns(self.handle) }
160    }
161
162    /// Same as [`Handle::get_time_ns`] but in microseconds.
163    pub fn get_time_us(&self) -> i64 {
164        unsafe { mpv::get_time_us(self.handle) }
165    }
166
167    /// Set an option. Note that you can't normally set options during runtime. It works in an uninitialized state (see [`Handle::create()`]), and in some cases in at runtime.
168    ///
169    /// Using a format other than [`Node`] is equivalent to constructing a [`Node`] with the given format and data and passing it to this function.
170    ///
171    /// # Example
172    /// ```
173    ///# #![allow(deprecated)]
174    ///# use libmpv_client::*;
175    ///#
176    ///# fn example_func(ptr: *mut mpv_handle) -> Result<()> {
177    ///#     let handle = Handle::from_ptr(ptr);
178    /// handle.set_option("idle", "yes")?;
179    ///#     Ok(())
180    ///# }
181    /// ```
182    #[deprecated = "For most purposes, this is not needed anymore.\
183    \
184    Starting with mpv version 0.21.0 (version 1.23) most options can be set with `Handle::set_property()` (and related functions), and even before `Handle::initialize()`.\
185    In some obscure corner cases, using this function to set options might still be required (see \"Inconsistencies between options and properties\" in the manpage).\
186    Once these are resolved, the option setting functions might be fully deprecated."]
187    pub fn set_option<T: MpvSend>(&self, name: &str, data: T) -> Result<()> {
188        let name_str = CString::new(name)?;
189
190        data.to_mpv(|x| {
191            let err = unsafe { mpv::set_option(self.handle, name_str.as_ptr(), T::MPV_FORMAT.0, x) };
192            error_to_result_code(err)
193        }).map(|_| ())
194    }
195
196    /// Send a command to the player. Commands are the same as those used in `input.conf`, except that this function takes parameters in a pre-split form.
197    ///
198    /// The commands and their parameters are documented in input.rst.
199    ///
200    /// Does not use OSD and string expansion by default (unlike [`Handle::command_string()`] and input.conf).
201    ///
202    /// # Params
203    /// - `command` - Usually, the first item is the command, and the following items are arguments.
204    ///
205    /// # Example
206    /// ```
207    ///# use libmpv_client::*;
208    ///#
209    ///# fn example_func(ptr: *mut mpv_handle) -> Result<()> {
210    ///#     let handle = Handle::from_ptr(ptr);
211    /// handle.command(&["script-message-to", "commands", "type", "seek absolute-percent", "6"])?;
212    ///#     Ok(())
213    ///# }
214    /// ```
215    pub fn command(&self, command: &[impl AsRef<str>]) -> Result<()> {
216        let mut owned_strings = Vec::with_capacity(command.len());
217        for s in command {
218            owned_strings.push(CString::new(s.as_ref())?);
219        }
220
221        let mut cstrs: Vec<_> = owned_strings.iter().map(|s| s.as_ptr()).collect();
222        cstrs.push(null());
223
224        let err = unsafe { mpv::command(self.handle, cstrs.as_mut_ptr()) };
225        error_to_result(err)
226    }
227
228    /// Same as [`Handle::command_ret()`], but allows passing structured data in any format.
229    ///
230    /// In particular, calling [`Handle::command()`] is exactly like calling [`Handle::command_node()`] with the format set to [`NodeArray`],
231    /// and every arg passed in order as [`String`].
232    ///
233    /// Does not use OSD and string expansion by default.
234    ///
235    /// # Params
236    /// The `command` [`Node`] can be one of the following formats:
237    /// - [`Node::Array`]: Positional arguments. Each entry is an argument using an arbitrary format (the format must be compatible with the used command).
238    ///   Usually, the first item is the command name (as a [`Node::String`]). The order of arguments is as documented in each command description.
239    /// - [`Node::Map`]: Named arguments. This requires at least an entry with the key "name" to be present, which must be a string and contain the command name.
240    ///   The special entry "_flags" is optional, and if present, must be an array of strings, each being a command prefix to apply.
241    ///   All other entries are interpreted as arguments.
242    ///   They must use the argument names as documented in each command description. Some commands do not support named arguments at all and must use [`Node::Array`].
243    ///
244    /// # Return
245    /// If the function succeeds, [`Result<Node>`] is command-specific return data. Few commands actually use this.
246    ///
247    /// # Example
248    /// ```
249    ///# use libmpv_client::*;
250    ///#
251    ///# fn example_func(ptr: *mut mpv_handle) -> Result<()> {
252    ///#     let handle = Handle::from_ptr(ptr);
253    /// // For convenience, you use node_array!(), which accepts any arbitrary types
254    /// // implementing Into<Node> and produces a Node::Array...
255    /// handle.command_node(node_array!("frame-step", 20, "mute"))?;
256    ///
257    /// // ...or node_map!(), which is similar but takes (Into<String>, Into<Node>) tuples
258    /// // and produces a Node::Map.
259    /// handle.command_node(node_map! {
260    ///     ("name", "show-text"),
261    ///     ("text", "peekaboo!"),
262    ///     ("duration", 500),
263    /// })?;
264    ///#     Ok(())
265    ///# }
266    /// ```
267    pub fn command_node(&self, command: Node) -> Result<Node> {
268        let mut return_mpv_node = MaybeUninit::uninit();
269        
270        command.to_mpv(|x| {
271            let err = unsafe { mpv::command_node(self.handle, x as *mut mpv_node, return_mpv_node.as_ptr() as *mut mpv_node) };
272            error_to_result_code(err)
273        }).and_then(|_| {
274            let ret = unsafe { Node::from_node_ptr(return_mpv_node.as_ptr()) };
275            unsafe { mpv_free_node_contents(return_mpv_node.as_mut_ptr()) }
276            ret
277        })
278    }
279
280    /// This is essentially identical to [`Handle::command()`], but it also returns a result.
281    ///
282    /// Does not use OSD and string expansion by default.
283    ///
284    /// # Params
285    /// - `command` - Usually, the first item is the command, and the following items are arguments.
286    ///
287    /// # Return
288    /// If the function succeeds, [`Result<Node>`] is command-specific return data. Few commands actually use this.
289    pub fn command_ret(&self, command: &[impl AsRef<str>]) -> Result<Node> {
290        let mut owned_strings = Vec::with_capacity(command.len());
291        for s in command {
292            owned_strings.push(CString::new(s.as_ref())?);
293        }
294
295        let mut cstrs: Vec<_> = owned_strings.iter().map(|s| s.as_ptr()).collect();
296        cstrs.push(null());
297        
298        let mut return_mpv_node = MaybeUninit::uninit();
299
300        let err = unsafe { mpv::command_ret(self.handle, cstrs.as_mut_ptr(), return_mpv_node.as_mut_ptr()) };
301        error_to_result_code(err).and_then(|_| {
302            let ret = unsafe { Node::from_node_ptr(return_mpv_node.as_ptr()) };
303            unsafe { mpv_free_node_contents(return_mpv_node.as_mut_ptr()) }
304            ret
305        })
306    }
307
308    /// Same as [`Handle::command()`], but use input.conf parsing for splitting arguments.
309    ///
310    /// This is slightly simpler, but also more error-prone, since arguments may need quoting/escaping.
311    ///
312    /// This also has OSD and string expansion enabled by default.
313    pub fn command_string(&self, command: &str) -> Result<()> {
314        let owned_string = CString::new(command)?;
315
316        let err = unsafe { mpv::command_string(self.handle, owned_string.as_ptr()) };
317        error_to_result(err)
318    }
319
320    /// Set a property to a given value.
321    ///
322    /// Properties are essentially variables that can be queried or set at runtime. For example, writing to the pause property will actually pause or unpause playback.
323    ///
324    /// # Params
325    /// If the [`MpvFormat::MPV_FORMAT`] of `value` doesn't match with the internal [`mpv_format`](libmpv_client_sys::mpv_format) format of the property,
326    /// access usually will fail with [`Error::PropertyFormat`].
327    ///
328    /// In some cases, the data is automatically converted and access succeeds. For example, mpv converts [`i64`] to [`f64`],
329    /// and access using [`String`] usually invokes a string parser.
330    ///
331    /// The same happens when calling this function with [`Node`]: the underlying format may be converted to another type if possible.
332    ///
333    /// Using a format other than [`Node`] is equivalent to constructing a [`Node`] with the given format and data and passing it to this function.
334    ///
335    /// # Example
336    /// ```
337    ///# use libmpv_client::*;
338    ///#
339    ///# fn example_func(ptr: *mut mpv_handle) -> Result<()> {
340    ///#     let handle = Handle::from_ptr(ptr);
341    /// handle.set_property("chapter", 3)?;
342    ///#     Ok(())
343    ///# }
344    /// ```
345    pub fn set_property<T: MpvSend>(&self, name: &str, value: T) -> Result<()> {
346        let owned_name = CString::new(name)?;
347
348        value.to_mpv(|x| {
349            let err = unsafe { mpv::set_property(self.handle, owned_name.as_ptr(), T::MPV_FORMAT.0, x) };
350            error_to_result_code(err)
351        }).map(|_| ())
352    }
353
354    /// Convenience function to delete a property.
355    ///
356    /// This is equivalent to running the command `del [name]`.
357    pub fn del_property(&self, name: &str) -> Result<()> {
358        let owned_name = CString::new(name)?;
359
360        let err = unsafe { mpv::del_property(self.handle, owned_name.as_ptr()) };
361        error_to_result(err)
362    }
363
364    /// Read the value of the given property.
365    ///
366    /// If the [`MpvFormat::MPV_FORMAT`] of the requested type doesn't match with the internal [`mpv_format`](libmpv_client_sys::mpv_format) format of the property,
367    /// access usually will fail with [`Error::PropertyFormat`].
368    ///
369    /// In some cases, the data is automatically converted and access succeeds. For example, [`i64`] is always converted to [`f64`],
370    /// and access using [`String`] usually invokes a string formatter.
371    ///
372    /// # Example
373    /// ```
374    ///# use libmpv_client::*;
375    ///#
376    ///# fn example_func(ptr: *mut mpv_handle) -> Result<()> {
377    ///#     let handle = Handle::from_ptr(ptr);
378    /// // use turbofish...
379    /// let duration = handle.get_property::<f64>("duration")?;
380    /// // or explicitly type the assignment...
381    /// let node: Node = handle.get_property("metadata")?;
382    ///#     Ok(())
383    ///# }
384    /// ```
385    pub fn get_property<T: MpvRecv>(&self, name: &str) -> Result<T> {
386        let owned_name = CString::new(name)?;
387
388        unsafe {
389            T::from_mpv(|x| {
390                let err = mpv::get_property(self.handle, owned_name.as_ptr(), T::MPV_FORMAT.0, x);
391                error_to_result_code(err)
392            })
393        }
394    }
395
396    /// Get a notification whenever the given property changes.
397    ///
398    /// You will receive updates as [`Event::PropertyChange`]. Note that this is not very precise: for some properties, it may not send updates even if the property changed.
399    /// This depends on the property, and it's a valid feature request to ask for better update handling of a specific property.
400    /// (For some properties, like [`clock`](https://mpv.io/manual/stable/#command-interface-clock), which shows the wall clock, this mechanism doesn't make too much sense anyway.)
401    ///
402    /// Property changes are coalesced: the change events are returned only once the event queue becomes empty
403    /// (e.g., [`Handle::wait_event()`] would block or return [`Event::None`]), and then only one event per changed property is returned.
404    ///
405    /// You always get an initial change notification. This is meant to initialize the user's state to the current value of the property.
406    ///
407    /// Normally, change events are sent only if the property value changes within the requested format.
408    /// [`PropertyChange.value`](field@event::PropertyChange::value) will contain the [`PropertyValue`](event::PropertyValue).
409    ///
410    /// If the property is observed with the format parameter set to [`PropertyValue::None`](event::PropertyValue::None), you get low-level notifications whether the property _may_ have changed.
411    /// With this mode, you will have to determine yourself whether the property really changed. On the other hand, this mechanism can be faster and use fewer resources.
412    ///
413    /// Observing a property that doesn't exist is allowed. (Although it may still cause some sporadic change events.)
414    ///
415    /// Keep in mind that you will get [`Event::PropertyChange`] even if you change a property yourself.
416    /// Try to avoid endless feedback loops, which could happen if you react to the change notifications triggered by your own change.
417    ///
418    /// Only the [`Handle`] on which this was called will receive [`Event::PropertyChange`] events or can unobserve them.
419    ///
420    /// # Warning
421    /// If a property is unavailable or retrieving it caused an error, [`Event::PropertyChange`]'s [`PropertyChange.value`](field@event::PropertyChange::value) will be [`PropertyValue::None`](event::PropertyValue::None),
422    /// even if the format parameter was set to a different value.
423    ///
424    /// # Params
425    /// - `userdata`: This will be used for the [`PropertyChange.userdata`](field@event::PropertyChange::userdata) field for the received [`Event::PropertyChange`] events.
426    // (Also see the section about asynchronous calls, although this function is somewhat different from actual asynchronous calls.)
427    ///
428    ///   If you have no use for this, pass 0.
429    ///
430    ///
431    /// Also see [`Handle::unobserve_property()`].
432    ///
433    /// # Example
434    /// ```
435    ///# use libmpv_client::*;
436    ///#
437    ///# fn example_func(ptr: *mut mpv_handle) -> Result<()> {
438    ///#     let handle = Handle::from_ptr(ptr);
439    /// // you can set userdata = 0 if you don't plan un unobserving the value later
440    /// handle.observe_property("playtime-remaining", Format::DOUBLE, 0)?;
441    ///#     Ok(())
442    ///# }
443    /// ```
444    pub fn observe_property(&self, name: &str, format: Format, userdata: u64) -> Result<()> {
445        let owned_name = CString::new(name)?;
446
447        let err = unsafe { mpv::observe_property(self.handle, userdata, owned_name.as_ptr(), format.0) };
448        error_to_result(err)
449    }
450
451    /// Undo [`Handle::observe_property`].
452    ///
453    /// This will remove all observed properties for which the given number was passed as `userdata` to [`Handle::observe_property()`].
454    ///
455    /// # Params
456    /// - `userdata`: `userdata` that was passed to [`Handle::observe_property()`]
457    ///
458    /// # Returns
459    /// [`Result<i32>`] contains the number of properties removed on success.
460    ///
461    /// # Example
462    /// ```
463    ///# use libmpv_client::*;
464    ///#
465    ///# fn example_func(ptr: *mut mpv_handle) -> Result<()> {
466    ///#     let handle = Handle::from_ptr(ptr);
467    /// // if you want to later unobserve a property, you must provide a userdata
468    /// let media_title_userdata: u64 = 12345; // arbitrary, user-defined value
469    /// handle.observe_property("media-title", Format::STRING, media_title_userdata)?;
470    ///
471    /// // later...
472    /// handle.unobserve_property(media_title_userdata)?;
473    ///#     Ok(())
474    ///# }
475    /// ```
476    pub fn unobserve_property(&self, userdata: u64) -> Result<i32> {
477        let err = unsafe { mpv::unobserve_property(self.handle, userdata) };
478        error_to_result_code(err)
479    }
480
481    /// Enable or disable an [`Event`] given its [`EventId`].
482    ///
483    /// Some events are enabled by default. Some events can't be disabled.
484    ///
485    /// (Informational note: currently, all events are enabled by default, except [`Event::Tick`].)
486    pub fn request_event(&self, event_id: EventId, enable: bool) -> Result<()> {
487        let err = unsafe { mpv::request_event(self.handle, event_id.0, if enable { 1 } else { 0 }) };
488        error_to_result(err)
489    }
490
491    /// Enable or disable receiving of log messages.
492    ///
493    /// These are the messages the command line player prints to the terminal.
494    /// This call sets the maximum log level for a message to be received with [`Event::LogMessage`].
495    ///
496    /// # Params
497    /// - `max_level`: Maximum log level to subscribe to.
498    ///
499    /// The value [`LogLevel::None`] disables all messages. This is the default.
500    pub fn request_log_messages(&self, max_level: LogLevel) -> Result<()> {
501        let err = unsafe { mpv::request_log_messages(self.handle, max_level.to_cstr().as_ptr()) };
502        error_to_result(err)
503    }
504
505    /// Wait for the next event, or until the timeout expires, or if another thread makes a call to [`Handle::wakeup()`].
506    ///
507    /// See [`Event`] for the possible events.
508    ///
509    /// # Params
510    /// - `timeout`: Timeout in seconds, after which the function returns even if no event was received. An [`Event::None`] is returned on timeout.
511    ///   - A value of 0 will disable waiting and is suitable for polling.
512    ///   - Negative values will wait with an infinite timeout.
513    ///
514    /// # Warning
515    /// The internal event queue has a limited size (per client handle). If you don't empty the event queue quickly enough with [`Handle::wait_event()`],
516    /// it will overflow and silently discard further events. If this happens, making asynchronous requests will fail as well (with [`Error::EventQueueFull`]).
517    ///
518    /// # Concurrency
519    /// Only one thread is allowed to call this on the same [`Handle`] at a time. The API won't complain if more than one thread calls this,
520    /// but it will cause race conditions in the client when accessing the shared `mpv_event` struct.
521    ///
522    /// Note that most other API functions are not restricted by this, and no API function internally calls [`Handle::wait_event()`].
523    /// Additionally, concurrent calls to different [`Handle`]s are always safe.
524    ///
525    /// # Example
526    /// ```
527    ///# use libmpv_client::*;
528    ///#
529    ///# fn example_func(ptr: *mut mpv_handle) -> Result<()> {
530    ///#     let handle = Handle::from_ptr(ptr);
531    /// match handle.wait_event(0.0)? {
532    ///     Event::None => println!("No event was ready yet!"),
533    ///     Event::Shutdown => {
534    ///         println!("Shutting down!");
535    ///         // You must cleanly exit after receiving Event::Shutdown, or else you'll hang mpv.
536    ///         return Ok(());
537    ///     }
538    ///     Event::LogMessage(log_message) => println!("Got a log message: {log_message:?}"),
539    ///     event => println!("Got an other event: {event:?}"),
540    /// }
541    ///#     Ok(())
542    ///# }
543    /// ```
544    /// 
545    /// # Warning
546    /// cplugins **must** call [`Handle::wait_event()`] at least once after initialization;
547    /// mpv will block awaiting a sign of life:.
548    ///```
549    ///# use std::thread::sleep;
550    ///# use std::time::Duration;
551    ///# use libmpv_client::Handle;
552    ///# use libmpv_client_sys::mpv_handle;
553    ///# 
554    /// #[unsafe(no_mangle)]
555    /// extern "C" fn mpv_open_cplugin(ptr: *mut mpv_handle) -> std::os::raw::c_int {
556    ///     let handle = Handle::from_ptr(ptr);
557    /// 
558    ///     println!("Sleeping 5 seconds pre-wait_event...");
559    ///     // mpv will be completely hung during this sleep...
560    ///     sleep(Duration::from_secs(5));
561    /// 
562    ///     // Let mpv know we're alive!
563    ///     let _ = handle.wait_event(-1.0);
564    /// 
565    ///     println!("Sleeping 15 seconds post-wait_event...");
566    ///     // mpv will operate normally during this sleep.
567    ///     sleep(Duration::from_secs(15));
568    /// 
569    ///     return 0;
570    /// }
571    /// ```
572    pub fn wait_event(&self, timeout: f64) -> Result<Event> {
573        Event::from_ptr(unsafe { mpv::wait_event(self.handle, timeout) })
574    }
575
576    /// Interrupt the current [`Handle::wait_event()`] call.
577    ///
578    /// This will wake up the thread currently waiting in [`Handle::wait_event()`]. If no thread is waiting, the next [`Handle::wait_event()`]
579    /// call will return immediately (this is to avoid lost wakeups).
580    ///
581    /// [`Handle::wait_event()`] will receive an [`Event::None`] if it's woken up due to this call. But note that this dummy event might be
582    /// skipped if there are already other events queued. All that counts is that the waiting thread is woken up.
583    pub fn wakeup(&self) {
584        unsafe { mpv::wakeup(self.handle) }
585    }
586
587    /// A hook is like a synchronous event that blocks the player. You register a hook handler with this function. You will get an event,
588    /// which you need to handle, and once things are ready, you can let the player continue with [`Handle::hook_continue()`].
589    ///
590    /// Currently, hooks can't be removed explicitly. But they will be implicitly removed if the [`Handle`] it was registered with is destroyed.
591    /// This also continues the hook if it was being handled by the destroyed handle (but this should be avoided, as it might mess up the order of hook execution).
592    ///
593    /// See [the "Hooks" section in the manpage](https://mpv.io/manual/stable/#hooks) to see which hooks are currently defined.
594    ///
595    /// Some hooks might be reentrant (so you get multiple [`Event::Hook`] for the same hook). If this can happen for a specific hook type,
596    /// it will be explicitly documented in the manpage.
597    ///
598    /// Only the [`Handle`] on which this was called will receive the hook events or can "continue" them.
599    ///
600    /// # Priority
601    /// Hook handlers are ordered globally by priority and order of registration. Handlers for the same hook with the same priority are invoked
602    /// in order of registration (the handler registered first is run first). Handlers with lower priority are run first (which seems backward).
603    ///
604    /// # Params
605    /// - `userdata`: This will be used for the [`Event::Hook.userdata`](field@event::Hook::userdata) field for the received [`Event::Hook`] events.
606    ///   If you have no use for this, pass 0.
607    /// - `name`: The hook name. This should be [one of the documented names](https://mpv.io/manual/stable/#hooks).
608    ///   But if the name is unknown, the hook event will simply never be raised.
609    /// - `priority`: See remarks above. Use 0 as a neutral default.
610    pub fn hook_add(&self, userdata: u64, name: &str, priority: i32) -> Result<()> {
611        let owned_name = CString::new(name)?;
612
613        let err = unsafe { mpv::hook_add(self.handle, userdata, owned_name.as_ptr(), priority) };
614        error_to_result(err)
615    }
616
617    /// Respond to an [`Event::Hook`] event. You **must** call this after you have handled the event.
618    ///
619    /// There is no way to "cancel" or "stop" the hook.
620    ///
621    /// Calling this will typically unblock the player for whatever the hook is responsible for (e.g., for the `on_load` hook it lets it continue playback).
622    ///
623    /// # Params
624    /// - `id`: This must be the value of the [`Hook.id`](field@event::Hook::id) field for the corresponding [`Event::Hook`].
625    ///
626    /// # Warning
627    /// It is explicitly undefined behavior to call this more than once for each [`Event::Hook`], to pass an incorrect ID,
628    /// or to call this on a [`Handle`] different from the one that registered the handler and received the event.
629    ///
630    /// # Example
631    /// ```
632    ///# use libmpv_client::*;
633    ///#
634    ///# fn do_something_during_hook() {}
635    ///#
636    ///# fn example_func(ptr: *mut mpv_handle) -> Result<()> {
637    ///#     let handle = Handle::from_ptr(ptr);
638    /// match handle.wait_event(0.0)? {
639    ///     Event::Hook(hook) => {
640    ///         do_something_during_hook();
641    ///         // You MUST call hook_continue() on the provided Hook.id,
642    ///         // or else you'll hang mpv.
643    ///         handle.hook_continue(hook.id)?;
644    ///     }
645    ///     // ...
646    ///     event => {}
647    /// }
648    ///#     Ok(())
649    ///# }
650    /// ```
651    pub fn hook_continue(&self, id: u64) -> Result<()> {
652        let err = unsafe { mpv::hook_continue(self.handle, id) };
653        error_to_result(err)
654    }
655}
656
657/// An owned client created from a [`Handle`].
658///
659/// Unlike a [`Handle`], it is safe to call [`mpv_destroy`](libmpv_client_sys::destroy) and [`mpv_terminate_destroy`](libmpv_client_sys::terminate_destroy)
660/// on an owned [`Client`]; thus the [`Client::destroy()`] and [`Client::terminate_destroy()`] methods are provided here.
661///
662/// Additionally, since [`mpv_initialize`](libmpv_client_sys::initialize) should only be called on uninitialized instances
663/// of mpv, it only makes sense to call it on a created [`Client`], so that is exposed here as [`Client::initialize()`].
664///
665/// All other usage is the same as [`Handle`].
666///
667/// # Example
668/// ```rust
669///# use std::thread::sleep;
670///# use std::time::Duration;
671///# use libmpv_client::{Event, Handle};
672///#
673///# use libmpv_client_sys::mpv_handle;
674///#
675/// #[unsafe(no_mangle)]
676/// extern "C" fn mpv_open_cplugin(ptr: *mut mpv_handle) -> std::os::raw::c_int {
677///     let handle = Handle::from_ptr(ptr);
678///
679///     let second_client = handle.create_client("second client").unwrap();
680///
681///     // Note: in the case of a cplugin, the passed Handle MUST call wait_event
682///     // or else mpv will block the entire program waiting for a sign of life.
683///     let _ = handle.wait_event(-1.0);
684///
685///     loop {
686///         match second_client.wait_event(0.0) {
687///             Err(e) => {
688///                 println!("Second client got error: {e:?}");
689///             }
690///             Ok(event) => {
691///                 match event {
692///                     Event::Shutdown => {
693///                         println!("Goodbye from Rust!");
694///
695///                         // Clients must be destroyed in a timely manner!
696///                         // (though in this case it would get Drop'd anyway...)
697///                         second_client.destroy();
698///
699///                         // Handles require no additional cleanup!
700///                         return 0;
701///                     },
702///                     Event::None => {},
703///                     event => {
704///                         println!("Second client got event: {event:?}");
705///                     },
706///                 }
707///             }
708///         }
709///     }
710/// }
711/// ```
712pub struct Client(Handle);
713
714impl Client {
715    /// Initialize an uninitialized mpv instance. If the mpv instance is already running, an [`Error`] is returned.
716    ///
717    /// This function needs to be called to make full use of the client API if the client API handle was created with [`Handle::create()`].
718    ///
719    /// Only the following options are required to be set _before_ [`Client::initialize()`]:
720    /// - options which are only read at initialization time:
721    ///   - `config`
722    ///   - [`config-dir`](https://mpv.io/manual/stable/#options-config-dir)
723    ///   - [`input-conf`](https://mpv.io/manual/stable/#options-input-conf)
724    ///   - [`load-scripts`](https://mpv.io/manual/stable/#options-load-scripts)
725    ///   - [`script`](https://mpv.io/manual/stable/#options-scripts)
726    ///   - [`player-operation-mode`](https://mpv.io/manual/stable/#options-player-operation-mode)
727    ///   - `input-app-events` (macOS)
728    /// - [all encoding mode options](https://mpv.io/manual/stable/#encoding)
729    pub fn initialize(&self) -> Result<()> {
730        let err = unsafe { mpv::initialize(self.handle) };
731        error_to_result(err)
732    }
733
734    /// Disconnect and destroy the [`Client`] and its underlying [`Handle`]. The underlying [`mpv_handle`] will be deallocated with this API call.
735    ///
736    /// If the last [`mpv_handle`] is detached, the core player is destroyed.
737    /// In addition, if there are only weak handles (such as created by [`Handle::create_weak_client()`] or internal scripts), these handles will be sent [`Event::Shutdown`].
738    /// This function may block until these clients have responded to the shutdown event, and the core is finally destroyed.
739    ///
740    /// # Concurrency
741    /// Since the underlying [`mpv_handle`] is destroyed somewhere on the way, it's not safe to call other functions concurrently on the same handle.
742    ///
743    /// # Handles
744    /// Note that `mpv_destroy()` cannot be called from a cplugin client. The correct way to terminate (given a [`Handle`]) is to return from
745    /// the execution environment in which you were provided a [`mpv_handle`]. In the case of a cplugin, this means returning from `mpv_open_cplugin()`.
746    /// mpv will handle cleaning up the underlying client upon return.
747    ///
748    /// If a [`Handle`] wishes to terminate mpv, send `client.command(&["quit"])` before returning from `mpv_open_cplugin()`.
749    pub fn destroy(self) {
750        unsafe { mpv::destroy(self.handle) };
751        std::mem::forget(self); // forget to prevent Drop from calling destroy a second time
752    }
753
754    /// Similar to [`Client::destroy()`], but brings the player and all clients down as well and waits until all of them are destroyed. This function blocks.
755    ///
756    /// The advantage over [`Client::destroy()`] is that while [`Client::destroy()`] merely detaches the client handle from the player,
757    /// this function quits the player, waits until all other clients are destroyed (i.e., all [`mpv_handle`]s are detached), and also waits for the final termination of the player.
758    ///
759    /// # Concurrency
760    /// Since the underlying [`mpv_handle`] is destroyed somewhere on the way, it's not safe to call other functions concurrently on the same handle.
761    ///
762    /// # Handles
763    /// Note that `mpv_destroy()` cannot be called from a cplugin client. The correct way to terminate (given a [`Handle`]) is to return from
764    /// the execution environment in which you were provided a [`mpv_handle`]. In the case of a cplugin, this means returning from `mpv_open_cplugin()`.
765    /// mpv will handle cleaning up the underlying client upon return.
766    ///
767    /// If a [`Handle`] wishes to terminate mpv, send `client.command(&["quit"])` before returning from `mpv_open_cplugin()`.
768    pub fn terminate_destroy(self) {
769        unsafe { mpv::terminate_destroy(self.handle) }
770        std::mem::forget(self); // forget to prevent Drop from calling destroy a second time
771    }
772}
773
774impl Drop for Handle {
775    fn drop(&mut self) {
776        unsafe { mpv::destroy(self.handle) };
777    }
778}
779
780impl Deref for Client {
781    type Target = Handle;
782
783    fn deref(&self) -> &Self::Target {
784        &self.0
785    }
786}