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
109pub struct LogMessage(*const mpv_event_log_message);
111
112pub struct StartFile(*const mpv_event_start_file);
114
115pub struct EndFile(*const mpv_event_end_file);
117
118pub struct ClientMessage(*const mpv_event_client_message);
120
121pub struct Hook(*const mpv_event_hook);
123
124macro_rules! result {
125 ($f:expr) => {
126 match $f {
127 mpv_error_MPV_ERROR_SUCCESS => Ok(()),
128 e => Err(Error::new(e)),
129 }
130 };
131}
132
133macro_rules! result_with_code {
134 ($f:expr) => {
135 if $f >= mpv_error_MPV_ERROR_SUCCESS {
136 Ok($f)
137 } else {
138 Err(Error::new($f))
139 }
140 };
141}
142
143#[macro_export]
144macro_rules! osd {
145 ($client:expr, $duration:expr, $($arg:tt)*) => {
146 $client.command(&["show-text", &format!($($arg)*), &$duration.as_millis().to_string()])
147 }
148}
149
150#[macro_export]
151macro_rules! osd_async {
152 ($client:expr, $reply:expr, $duration:expr, $($arg:tt)*) => {
153 $client.command_async($reply, &["show-text", &format!($($arg)*), &$duration.as_millis().to_string()])
154 }
155}
156
157impl Handle {
158 #[inline]
170 pub fn from_ptr<'a>(ptr: *mut mpv_handle) -> &'a mut Self {
171 unsafe { &mut *(slice_from_raw_parts_mut(ptr, 1) as *mut Self) }
172 }
173
174 #[inline]
178 pub unsafe fn as_ptr(&self) -> *const mpv_handle {
179 self.inner.as_ptr()
180 }
181
182 #[inline]
187 pub unsafe fn as_mut_ptr(&mut self) -> *mut mpv_handle {
188 self.inner.as_mut_ptr()
189 }
190
191 pub fn create_client(&mut self, name: impl AsRef<str>) -> Result<Client> {
192 let name = CString::new(name.as_ref())?;
193 let handle = unsafe { mpv_create_client(self.as_mut_ptr(), name.as_ptr()) };
194 if handle.is_null() {
195 Err(Error::new(mpv_error_MPV_ERROR_NOMEM))
196 } else {
197 Ok(Client(handle))
198 }
199 }
200
201 pub fn create_weak_client(&mut self, name: impl AsRef<str>) -> Result<Client> {
202 let name = CString::new(name.as_ref())?;
203 let handle = unsafe { mpv_create_weak_client(self.as_mut_ptr(), name.as_ptr()) };
204 if handle.is_null() {
205 Err(Error::new(mpv_error_MPV_ERROR_NOMEM))
206 } else {
207 Ok(Client(handle))
208 }
209 }
210
211 pub fn initialize(&mut self) -> Result<()> {
212 unsafe { result!(mpv_initialize(self.as_mut_ptr())) }
213 }
214
215 pub fn wait_event(&mut self, timeout: f64) -> Event {
234 unsafe { Event::from_ptr(mpv_wait_event(self.as_mut_ptr(), timeout)) }
235 }
236
237 pub fn name<'a>(&mut self) -> &'a str {
240 unsafe {
241 CStr::from_ptr(mpv_client_name(self.as_mut_ptr()))
242 .to_str()
243 .unwrap_or("unknown")
244 }
245 }
246
247 #[inline]
259 pub fn id(&mut self) -> i64 {
260 unsafe { mpv_client_id(self.as_mut_ptr()) }
261 }
262
263 pub fn command<I, S>(&mut self, args: I) -> Result<()>
267 where
268 I: IntoIterator<Item = S>,
269 S: AsRef<str>,
270 {
271 let args: Vec<CString> = args.into_iter().map(|s| CString::new(s.as_ref()).unwrap()).collect();
272 let mut raw_args: Vec<*const c_char> = args.iter().map(|s| s.as_ptr()).collect();
273 raw_args.push(std::ptr::null()); unsafe { result!(mpv_command(self.as_mut_ptr(), raw_args.as_mut_ptr())) }
275 }
276
277 pub fn command_ret<I, S>(&mut self, args: I) -> Result<Node>
278 where
279 I: IntoIterator<Item = S>,
280 S: AsRef<str>,
281 {
282 let args: Vec<CString> = args.into_iter().map(|s| CString::new(s.as_ref()).unwrap()).collect();
283 let mut raw_args: Vec<*const c_char> = args.iter().map(|s| s.as_ptr()).collect();
284 raw_args.push(std::ptr::null()); let mut res = MaybeUninit::<mpv_node>::zeroed();
287 let ret = unsafe { mpv_command_ret(self.as_mut_ptr(), raw_args.as_mut_ptr(), res.as_mut_ptr()) };
288
289 result!(ret)?;
290 unsafe { Ok(from_mpv_node(res.assume_init_mut())) }
291 }
292
293 pub fn command_async<I, S>(&mut self, reply: u64, args: I) -> Result<()>
307 where
308 I: IntoIterator<Item = S>,
309 S: AsRef<str>,
310 {
311 let args: Vec<CString> = args.into_iter().map(|s| CString::new(s.as_ref()).unwrap()).collect();
312 let mut raw_args: Vec<*const c_char> = args.iter().map(|s| s.as_ptr()).collect();
313 raw_args.push(std::ptr::null()); unsafe { result!(mpv_command_async(self.as_mut_ptr(), reply, raw_args.as_mut_ptr())) }
315 }
316
317 pub fn set_property<T: Format>(&mut self, name: impl AsRef<str>, data: T) -> Result<()> {
318 let name = CString::new(name.as_ref())?;
319 let handle = unsafe { self.as_mut_ptr() };
320 data.to_mpv(|data| unsafe { result!(mpv_set_property(handle, name.as_ptr(), T::MPV_FORMAT, data)) })
321 }
322
323 pub fn get_property<T: Format>(&mut self, name: impl AsRef<str>) -> Result<T> {
330 let name = CString::new(name.as_ref())?;
331 let handle = unsafe { self.as_mut_ptr() };
332 T::from_mpv(|data| unsafe { result!(mpv_get_property(handle, name.as_ptr(), T::MPV_FORMAT, data)) })
333 }
334
335 pub fn observe_property<T: Format>(&mut self, reply: u64, name: impl AsRef<str>) -> Result<()> {
336 let name = CString::new(name.as_ref())?;
337 unsafe {
338 result!(mpv_observe_property(
339 self.as_mut_ptr(),
340 reply,
341 name.as_ptr(),
342 T::MPV_FORMAT
343 ))
344 }
345 }
346
347 pub fn unobserve_property(&mut self, registered_reply: u64) -> Result<i32> {
352 unsafe { result_with_code!(mpv_unobserve_property(self.as_mut_ptr(), registered_reply)) }
353 }
354
355 pub fn hook_add(&mut self, reply: u64, name: &str, priority: i32) -> Result<()> {
356 let name = CString::new(name)?;
357 unsafe { result!(mpv_hook_add(self.as_mut_ptr(), reply, name.as_ptr(), priority)) }
358 }
359
360 pub fn hook_continue(&mut self, id: u64) -> Result<()> {
361 unsafe { result!(mpv_hook_continue(self.as_mut_ptr(), id)) }
362 }
363}
364
365impl Client {
366 pub fn new() -> Result<Self> {
367 let handle = unsafe { mpv_create() };
368 if handle.is_null() {
369 Err(Error::new(mpv_error_MPV_ERROR_NOMEM))
370 } else {
371 Ok(Self(handle))
372 }
373 }
374
375 pub fn initialize(self) -> Result<Self> {
376 unsafe { result!(mpv_initialize(self.0)).map(|()| self) }
377 }
378}
379
380impl Drop for Client {
381 fn drop(&mut self) {
382 unsafe { mpv_destroy(self.0) }
383 }
384}
385
386impl Deref for Client {
387 type Target = Handle;
388
389 #[inline]
390 fn deref(&self) -> &Self::Target {
391 Handle::from_ptr(self.0)
392 }
393}
394
395impl DerefMut for Client {
396 #[inline]
397 fn deref_mut(&mut self) -> &mut Self::Target {
398 Handle::from_ptr(self.0)
399 }
400}
401
402unsafe impl Send for Client {}
403
404impl Event {
405 unsafe fn from_ptr(event: *const mpv_event) -> Event {
406 unsafe {
407 match (*event).event_id {
408 mpv_event_id_MPV_EVENT_SHUTDOWN => Event::Shutdown,
409 mpv_event_id_MPV_EVENT_LOG_MESSAGE => Event::LogMessage(LogMessage::from_ptr((*event).data)),
410 mpv_event_id_MPV_EVENT_GET_PROPERTY_REPLY => Event::GetPropertyReply(
411 result!((*event).error),
412 (*event).reply_userdata,
413 Property::from_ptr((*event).data),
414 ),
415 mpv_event_id_MPV_EVENT_SET_PROPERTY_REPLY => {
416 Event::SetPropertyReply(result!((*event).error), (*event).reply_userdata)
417 }
418 mpv_event_id_MPV_EVENT_COMMAND_REPLY => {
419 Event::CommandReply(result!((*event).error), (*event).reply_userdata)
420 }
421 mpv_event_id_MPV_EVENT_START_FILE => Event::StartFile(StartFile::from_ptr((*event).data)),
422 mpv_event_id_MPV_EVENT_END_FILE => Event::EndFile(EndFile::from_ptr((*event).data)),
423 mpv_event_id_MPV_EVENT_FILE_LOADED => Event::FileLoaded,
424 mpv_event_id_MPV_EVENT_CLIENT_MESSAGE => Event::ClientMessage(ClientMessage::from_ptr((*event).data)),
425 mpv_event_id_MPV_EVENT_VIDEO_RECONFIG => Event::VideoReconfig,
426 mpv_event_id_MPV_EVENT_AUDIO_RECONFIG => Event::AudioReconfig,
427 mpv_event_id_MPV_EVENT_SEEK => Event::Seek,
428 mpv_event_id_MPV_EVENT_PLAYBACK_RESTART => Event::PlaybackRestart,
429 mpv_event_id_MPV_EVENT_PROPERTY_CHANGE => {
430 Event::PropertyChange((*event).reply_userdata, Property::from_ptr((*event).data))
431 }
432 mpv_event_id_MPV_EVENT_QUEUE_OVERFLOW => Event::QueueOverflow,
433 mpv_event_id_MPV_EVENT_HOOK => Event::Hook((*event).reply_userdata, Hook::from_ptr((*event).data)),
434 _ => Event::None,
435 }
436 }
437 }
438}
439
440impl fmt::Display for Event {
441 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
442 let event = match *self {
443 Self::Shutdown => mpv_event_id_MPV_EVENT_SHUTDOWN,
444 Self::LogMessage(..) => mpv_event_id_MPV_EVENT_LOG_MESSAGE,
445 Self::GetPropertyReply(..) => mpv_event_id_MPV_EVENT_GET_PROPERTY_REPLY,
446 Self::SetPropertyReply(..) => mpv_event_id_MPV_EVENT_SET_PROPERTY_REPLY,
447 Self::CommandReply(..) => mpv_event_id_MPV_EVENT_COMMAND_REPLY,
448 Self::StartFile(..) => mpv_event_id_MPV_EVENT_START_FILE,
449 Self::EndFile(..) => mpv_event_id_MPV_EVENT_END_FILE,
450 Self::FileLoaded => mpv_event_id_MPV_EVENT_FILE_LOADED,
451 Self::ClientMessage(..) => mpv_event_id_MPV_EVENT_CLIENT_MESSAGE,
452 Self::VideoReconfig => mpv_event_id_MPV_EVENT_VIDEO_RECONFIG,
453 Self::AudioReconfig => mpv_event_id_MPV_EVENT_AUDIO_RECONFIG,
454 Self::Seek => mpv_event_id_MPV_EVENT_SEEK,
455 Self::PlaybackRestart => mpv_event_id_MPV_EVENT_PLAYBACK_RESTART,
456 Self::PropertyChange(..) => mpv_event_id_MPV_EVENT_PROPERTY_CHANGE,
457 Self::QueueOverflow => mpv_event_id_MPV_EVENT_QUEUE_OVERFLOW,
458 Self::Hook(..) => mpv_event_id_MPV_EVENT_HOOK,
459 _ => mpv_event_id_MPV_EVENT_NONE,
460 };
461
462 f.write_str(unsafe {
463 CStr::from_ptr(mpv_event_name(event))
464 .to_str()
465 .unwrap_or("unknown event")
466 })
467 }
468}
469
470impl Property {
471 fn from_ptr(ptr: *const c_void) -> Self {
474 assert!(!ptr.is_null());
475 Self(ptr as *const mpv_event_property)
476 }
477
478 pub fn name(&self) -> &str {
480 unsafe { CStr::from_ptr((*self.0).name) }.to_str().unwrap_or("unknown")
481 }
482
483 pub fn data<T: Format>(&self) -> Option<T> {
484 unsafe {
485 if (*self.0).format == T::MPV_FORMAT {
486 T::from_ptr((*self.0).data).ok()
487 } else {
488 None
489 }
490 }
491 }
492}
493
494impl fmt::Display for Property {
495 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
496 f.write_str(self.name())
497 }
498}
499
500impl LogMessage {
501 fn from_ptr(ptr: *const c_void) -> Self {
504 assert!(!ptr.is_null());
505 Self(ptr as *const mpv_event_log_message)
506 }
507}
508
509impl fmt::Display for LogMessage {
510 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
511 f.write_str("log message")
512 }
513}
514
515impl StartFile {
516 fn from_ptr(ptr: *const c_void) -> Self {
519 assert!(!ptr.is_null());
520 Self(ptr as *const mpv_event_start_file)
521 }
522
523 pub fn playlist_entry_id(&self) -> i64 {
525 unsafe { (*self.0).playlist_entry_id }
526 }
527}
528
529impl fmt::Display for StartFile {
530 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
531 f.write_str("start file")
532 }
533}
534
535impl EndFile {
536 fn from_ptr(ptr: *const c_void) -> Self {
539 assert!(!ptr.is_null());
540 Self(ptr as *const mpv_event_end_file)
541 }
542}
543
544impl fmt::Display for EndFile {
545 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
546 f.write_str("end file")
547 }
548}
549
550impl ClientMessage {
551 fn from_ptr(ptr: *const c_void) -> Self {
554 assert!(!ptr.is_null());
555 Self(ptr as *const mpv_event_client_message)
556 }
557
558 pub fn args<'a>(&self) -> Vec<&'a str> {
559 unsafe {
560 let args = std::slice::from_raw_parts((*self.0).args, (*self.0).num_args as usize);
561 args.iter().map(|arg| CStr::from_ptr(*arg).to_str().unwrap()).collect()
562 }
563 }
564}
565
566impl fmt::Display for ClientMessage {
567 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
568 f.write_str("client-message")
569 }
570}
571
572impl Hook {
573 fn from_ptr(ptr: *const c_void) -> Self {
576 assert!(!ptr.is_null());
577 Self(ptr as *const mpv_event_hook)
578 }
579
580 pub fn name(&self) -> &str {
582 unsafe { CStr::from_ptr((*self.0).name).to_str().unwrap_or("unknown") }
583 }
584
585 pub fn id(&self) -> u64 {
587 unsafe { (*self.0).id }
588 }
589}
590
591impl fmt::Display for Hook {
592 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
593 f.write_str(self.name())
594 }
595}