1#![allow(non_upper_case_globals)]
2#![allow(non_camel_case_types)]
3#![allow(non_snake_case)]
4
5mod error;
6mod format;
7pub mod node;
8
9pub use error::{Error, Result};
10pub use format::Format;
11pub use node::Node;
12
13use std::ffi::{CStr, CString, c_char, c_void};
14use std::fmt;
15use std::mem::MaybeUninit;
16use std::ops::{Deref, DerefMut};
17use std::ptr::slice_from_raw_parts_mut;
18
19pub use ffi::mpv_handle;
20use ffi::*;
21
22use crate::node::from_mpv_node;
23
24pub struct Handle {
27 inner: [mpv_handle],
28}
29
30pub struct Client(*mut mpv_handle);
32
33pub enum Event {
36 None,
38 Shutdown,
41 LogMessage(LogMessage),
44 GetPropertyReply(Result<()>, u64, Property),
47 SetPropertyReply(Result<()>, u64),
50 CommandReply(Result<()>, u64), StartFile(StartFile),
56 EndFile(EndFile),
59 FileLoaded,
62 ClientMessage(ClientMessage),
68 VideoReconfig,
77 AudioReconfig,
80 Seek,
83 PlaybackRestart,
88 PropertyChange(u64, Property),
91 QueueOverflow,
99 Hook(u64, Hook),
104}
105
106pub struct Property(*const mpv_event_property);
108
109#[allow(dead_code)]
111pub struct LogMessage(*const mpv_event_log_message);
112
113pub struct StartFile(*const mpv_event_start_file);
115
116#[allow(dead_code)]
118pub struct EndFile(*const mpv_event_end_file);
119
120pub struct ClientMessage(*const mpv_event_client_message);
122
123pub struct Hook(*const mpv_event_hook);
125
126macro_rules! result {
127 ($f:expr) => {
128 match $f {
129 mpv_error_MPV_ERROR_SUCCESS => Ok(()),
130 e => Err(Error::new(e)),
131 }
132 };
133}
134
135macro_rules! result_with_code {
136 ($f:expr) => {
137 if $f >= mpv_error_MPV_ERROR_SUCCESS {
138 Ok($f)
139 } else {
140 Err(Error::new($f))
141 }
142 };
143}
144
145#[macro_export]
146macro_rules! osd {
147 ($client:expr, $duration:expr, $($arg:tt)*) => {
148 $client.command(&["show-text", &format!($($arg)*), &$duration.as_millis().to_string()])
149 }
150}
151
152#[macro_export]
153macro_rules! osd_async {
154 ($client:expr, $reply:expr, $duration:expr, $($arg:tt)*) => {
155 $client.command_async($reply, &["show-text", &format!($($arg)*), &$duration.as_millis().to_string()])
156 }
157}
158
159impl Handle {
160 #[inline]
172 pub fn from_ptr<'a>(ptr: *mut mpv_handle) -> &'a mut Self {
173 unsafe { &mut *(slice_from_raw_parts_mut(ptr, 1) as *mut Self) }
174 }
175
176 #[inline]
180 pub unsafe fn as_ptr(&self) -> *const mpv_handle {
181 self.inner.as_ptr()
182 }
183
184 #[inline]
189 pub unsafe fn as_mut_ptr(&mut self) -> *mut mpv_handle {
190 self.inner.as_mut_ptr()
191 }
192
193 pub fn create_client(&mut self, name: impl AsRef<str>) -> Result<Client> {
194 let name = CString::new(name.as_ref())?;
195 let handle = unsafe { mpv_create_client(self.as_mut_ptr(), name.as_ptr()) };
196 if handle.is_null() {
197 Err(Error::new(mpv_error_MPV_ERROR_NOMEM))
198 } else {
199 Ok(Client(handle))
200 }
201 }
202
203 pub fn create_weak_client(&mut self, name: impl AsRef<str>) -> Result<Client> {
204 let name = CString::new(name.as_ref())?;
205 let handle = unsafe { mpv_create_weak_client(self.as_mut_ptr(), name.as_ptr()) };
206 if handle.is_null() {
207 Err(Error::new(mpv_error_MPV_ERROR_NOMEM))
208 } else {
209 Ok(Client(handle))
210 }
211 }
212
213 pub fn initialize(&mut self) -> Result<()> {
214 unsafe { result!(mpv_initialize(self.as_mut_ptr())) }
215 }
216
217 pub fn wait_event(&mut self, timeout: f64) -> Event {
236 unsafe { Event::from_ptr(mpv_wait_event(self.as_mut_ptr(), timeout)) }
237 }
238
239 pub fn name<'a>(&mut self) -> &'a str {
242 unsafe {
243 CStr::from_ptr(mpv_client_name(self.as_mut_ptr()))
244 .to_str()
245 .unwrap_or("unknown")
246 }
247 }
248
249 #[inline]
261 pub fn id(&mut self) -> i64 {
262 unsafe { mpv_client_id(self.as_mut_ptr()) }
263 }
264
265 pub fn command<I, S>(&mut self, args: I) -> Result<()>
269 where
270 I: IntoIterator<Item = S>,
271 S: AsRef<str>,
272 {
273 let args: Vec<CString> = args.into_iter().map(|s| CString::new(s.as_ref()).unwrap()).collect();
274 let mut raw_args: Vec<*const c_char> = args.iter().map(|s| s.as_ptr()).collect();
275 raw_args.push(std::ptr::null()); unsafe { result!(mpv_command(self.as_mut_ptr(), raw_args.as_mut_ptr())) }
277 }
278
279 pub fn command_ret<I, S>(&mut self, args: I) -> Result<Node>
280 where
281 I: IntoIterator<Item = S>,
282 S: AsRef<str>,
283 {
284 let args: Vec<CString> = args.into_iter().map(|s| CString::new(s.as_ref()).unwrap()).collect();
285 let mut raw_args: Vec<*const c_char> = args.iter().map(|s| s.as_ptr()).collect();
286 raw_args.push(std::ptr::null()); let mut res = MaybeUninit::<mpv_node>::zeroed();
289 let ret = unsafe { mpv_command_ret(self.as_mut_ptr(), raw_args.as_mut_ptr(), res.as_mut_ptr()) };
290
291 result!(ret)?;
292 unsafe { Ok(from_mpv_node(res.assume_init_mut())) }
293 }
294
295 pub fn command_async<I, S>(&mut self, reply: u64, args: I) -> Result<()>
309 where
310 I: IntoIterator<Item = S>,
311 S: AsRef<str>,
312 {
313 let args: Vec<CString> = args.into_iter().map(|s| CString::new(s.as_ref()).unwrap()).collect();
314 let mut raw_args: Vec<*const c_char> = args.iter().map(|s| s.as_ptr()).collect();
315 raw_args.push(std::ptr::null()); unsafe { result!(mpv_command_async(self.as_mut_ptr(), reply, raw_args.as_mut_ptr())) }
317 }
318
319 pub fn set_property<T: Format>(&mut self, name: impl AsRef<str>, data: T) -> Result<()> {
320 let name = CString::new(name.as_ref())?;
321 let handle = unsafe { self.as_mut_ptr() };
322 data.to_mpv(|data| unsafe { result!(mpv_set_property(handle, name.as_ptr(), T::MPV_FORMAT, data)) })
323 }
324
325 pub fn get_property<T: Format>(&mut self, name: impl AsRef<str>) -> Result<T> {
332 let name = CString::new(name.as_ref())?;
333 let handle = unsafe { self.as_mut_ptr() };
334 T::from_mpv(|data| unsafe { result!(mpv_get_property(handle, name.as_ptr(), T::MPV_FORMAT, data)) })
335 }
336
337 pub fn observe_property<T: Format>(&mut self, reply: u64, name: impl AsRef<str>) -> Result<()> {
338 let name = CString::new(name.as_ref())?;
339 unsafe {
340 result!(mpv_observe_property(
341 self.as_mut_ptr(),
342 reply,
343 name.as_ptr(),
344 T::MPV_FORMAT
345 ))
346 }
347 }
348
349 pub fn unobserve_property(&mut self, registered_reply: u64) -> Result<i32> {
354 unsafe { result_with_code!(mpv_unobserve_property(self.as_mut_ptr(), registered_reply)) }
355 }
356
357 pub fn hook_add(&mut self, reply: u64, name: &str, priority: i32) -> Result<()> {
358 let name = CString::new(name)?;
359 unsafe { result!(mpv_hook_add(self.as_mut_ptr(), reply, name.as_ptr(), priority)) }
360 }
361
362 pub fn hook_continue(&mut self, id: u64) -> Result<()> {
363 unsafe { result!(mpv_hook_continue(self.as_mut_ptr(), id)) }
364 }
365}
366
367impl Client {
368 pub fn new() -> Result<Self> {
369 let handle = unsafe { mpv_create() };
370 if handle.is_null() {
371 Err(Error::new(mpv_error_MPV_ERROR_NOMEM))
372 } else {
373 Ok(Self(handle))
374 }
375 }
376
377 pub fn initialize(self) -> Result<Self> {
378 unsafe { result!(mpv_initialize(self.0)).map(|()| self) }
379 }
380}
381
382impl Drop for Client {
383 fn drop(&mut self) {
384 unsafe { mpv_destroy(self.0) }
385 }
386}
387
388impl Deref for Client {
389 type Target = Handle;
390
391 #[inline]
392 fn deref(&self) -> &Self::Target {
393 Handle::from_ptr(self.0)
394 }
395}
396
397impl DerefMut for Client {
398 #[inline]
399 fn deref_mut(&mut self) -> &mut Self::Target {
400 Handle::from_ptr(self.0)
401 }
402}
403
404unsafe impl Send for Client {}
405
406impl Event {
407 unsafe fn from_ptr(event: *const mpv_event) -> Event {
408 unsafe {
409 match (*event).event_id {
410 mpv_event_id_MPV_EVENT_SHUTDOWN => Event::Shutdown,
411 mpv_event_id_MPV_EVENT_LOG_MESSAGE => Event::LogMessage(LogMessage::from_ptr((*event).data)),
412 mpv_event_id_MPV_EVENT_GET_PROPERTY_REPLY => Event::GetPropertyReply(
413 result!((*event).error),
414 (*event).reply_userdata,
415 Property::from_ptr((*event).data),
416 ),
417 mpv_event_id_MPV_EVENT_SET_PROPERTY_REPLY => {
418 Event::SetPropertyReply(result!((*event).error), (*event).reply_userdata)
419 }
420 mpv_event_id_MPV_EVENT_COMMAND_REPLY => {
421 Event::CommandReply(result!((*event).error), (*event).reply_userdata)
422 }
423 mpv_event_id_MPV_EVENT_START_FILE => Event::StartFile(StartFile::from_ptr((*event).data)),
424 mpv_event_id_MPV_EVENT_END_FILE => Event::EndFile(EndFile::from_ptr((*event).data)),
425 mpv_event_id_MPV_EVENT_FILE_LOADED => Event::FileLoaded,
426 mpv_event_id_MPV_EVENT_CLIENT_MESSAGE => Event::ClientMessage(ClientMessage::from_ptr((*event).data)),
427 mpv_event_id_MPV_EVENT_VIDEO_RECONFIG => Event::VideoReconfig,
428 mpv_event_id_MPV_EVENT_AUDIO_RECONFIG => Event::AudioReconfig,
429 mpv_event_id_MPV_EVENT_SEEK => Event::Seek,
430 mpv_event_id_MPV_EVENT_PLAYBACK_RESTART => Event::PlaybackRestart,
431 mpv_event_id_MPV_EVENT_PROPERTY_CHANGE => {
432 Event::PropertyChange((*event).reply_userdata, Property::from_ptr((*event).data))
433 }
434 mpv_event_id_MPV_EVENT_QUEUE_OVERFLOW => Event::QueueOverflow,
435 mpv_event_id_MPV_EVENT_HOOK => Event::Hook((*event).reply_userdata, Hook::from_ptr((*event).data)),
436 _ => Event::None,
437 }
438 }
439 }
440}
441
442impl fmt::Display for Event {
443 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
444 let event = match *self {
445 Self::Shutdown => mpv_event_id_MPV_EVENT_SHUTDOWN,
446 Self::LogMessage(..) => mpv_event_id_MPV_EVENT_LOG_MESSAGE,
447 Self::GetPropertyReply(..) => mpv_event_id_MPV_EVENT_GET_PROPERTY_REPLY,
448 Self::SetPropertyReply(..) => mpv_event_id_MPV_EVENT_SET_PROPERTY_REPLY,
449 Self::CommandReply(..) => mpv_event_id_MPV_EVENT_COMMAND_REPLY,
450 Self::StartFile(..) => mpv_event_id_MPV_EVENT_START_FILE,
451 Self::EndFile(..) => mpv_event_id_MPV_EVENT_END_FILE,
452 Self::FileLoaded => mpv_event_id_MPV_EVENT_FILE_LOADED,
453 Self::ClientMessage(..) => mpv_event_id_MPV_EVENT_CLIENT_MESSAGE,
454 Self::VideoReconfig => mpv_event_id_MPV_EVENT_VIDEO_RECONFIG,
455 Self::AudioReconfig => mpv_event_id_MPV_EVENT_AUDIO_RECONFIG,
456 Self::Seek => mpv_event_id_MPV_EVENT_SEEK,
457 Self::PlaybackRestart => mpv_event_id_MPV_EVENT_PLAYBACK_RESTART,
458 Self::PropertyChange(..) => mpv_event_id_MPV_EVENT_PROPERTY_CHANGE,
459 Self::QueueOverflow => mpv_event_id_MPV_EVENT_QUEUE_OVERFLOW,
460 Self::Hook(..) => mpv_event_id_MPV_EVENT_HOOK,
461 _ => mpv_event_id_MPV_EVENT_NONE,
462 };
463
464 f.write_str(unsafe {
465 CStr::from_ptr(mpv_event_name(event))
466 .to_str()
467 .unwrap_or("unknown event")
468 })
469 }
470}
471
472impl Property {
473 fn from_ptr(ptr: *const c_void) -> Self {
476 assert!(!ptr.is_null());
477 Self(ptr as *const mpv_event_property)
478 }
479
480 pub fn name(&self) -> &str {
482 unsafe { CStr::from_ptr((*self.0).name) }.to_str().unwrap_or("unknown")
483 }
484
485 pub fn data<T: Format>(&self) -> Option<T> {
486 unsafe {
487 if (*self.0).format == T::MPV_FORMAT {
488 T::from_ptr((*self.0).data).ok()
489 } else {
490 None
491 }
492 }
493 }
494}
495
496impl fmt::Display for Property {
497 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
498 f.write_str(self.name())
499 }
500}
501
502impl LogMessage {
503 fn from_ptr(ptr: *const c_void) -> Self {
506 assert!(!ptr.is_null());
507 Self(ptr as *const mpv_event_log_message)
508 }
509}
510
511impl fmt::Display for LogMessage {
512 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
513 f.write_str("log message")
514 }
515}
516
517impl StartFile {
518 fn from_ptr(ptr: *const c_void) -> Self {
521 assert!(!ptr.is_null());
522 Self(ptr as *const mpv_event_start_file)
523 }
524
525 pub fn playlist_entry_id(&self) -> i64 {
527 unsafe { (*self.0).playlist_entry_id }
528 }
529}
530
531impl fmt::Display for StartFile {
532 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
533 f.write_str("start file")
534 }
535}
536
537impl EndFile {
538 fn from_ptr(ptr: *const c_void) -> Self {
541 assert!(!ptr.is_null());
542 Self(ptr as *const mpv_event_end_file)
543 }
544}
545
546impl fmt::Display for EndFile {
547 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
548 f.write_str("end file")
549 }
550}
551
552impl ClientMessage {
553 fn from_ptr(ptr: *const c_void) -> Self {
556 assert!(!ptr.is_null());
557 Self(ptr as *const mpv_event_client_message)
558 }
559
560 pub fn args<'a>(&self) -> Vec<&'a str> {
561 unsafe {
562 let args = std::slice::from_raw_parts((*self.0).args, (*self.0).num_args as usize);
563 args.iter().map(|arg| CStr::from_ptr(*arg).to_str().unwrap()).collect()
564 }
565 }
566}
567
568impl fmt::Display for ClientMessage {
569 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
570 f.write_str("client-message")
571 }
572}
573
574impl Hook {
575 fn from_ptr(ptr: *const c_void) -> Self {
578 assert!(!ptr.is_null());
579 Self(ptr as *const mpv_event_hook)
580 }
581
582 pub fn name(&self) -> &str {
584 unsafe { CStr::from_ptr((*self.0).name).to_str().unwrap_or("unknown") }
585 }
586
587 pub fn id(&self) -> u64 {
589 unsafe { (*self.0).id }
590 }
591}
592
593impl fmt::Display for Hook {
594 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
595 f.write_str(self.name())
596 }
597}