1use libmpv_sys::mpv_event;
20
21use crate::{mpv::mpv_err, *};
22
23use std::ffi::CString;
24use std::marker::PhantomData;
25use std::os::raw as ctype;
26use std::ptr::NonNull;
27use std::slice;
28use std::sync::atomic::Ordering;
29
30pub use libmpv_sys::mpv_event_id as EventId;
32pub mod mpv_event_id {
33 pub use libmpv_sys::mpv_event_id_MPV_EVENT_AUDIO_RECONFIG as AudioReconfig;
34 pub use libmpv_sys::mpv_event_id_MPV_EVENT_CLIENT_MESSAGE as ClientMessage;
35 pub use libmpv_sys::mpv_event_id_MPV_EVENT_COMMAND_REPLY as CommandReply;
36 pub use libmpv_sys::mpv_event_id_MPV_EVENT_END_FILE as EndFile;
37 pub use libmpv_sys::mpv_event_id_MPV_EVENT_FILE_LOADED as FileLoaded;
38 pub use libmpv_sys::mpv_event_id_MPV_EVENT_GET_PROPERTY_REPLY as GetPropertyReply;
39 pub use libmpv_sys::mpv_event_id_MPV_EVENT_HOOK as Hook;
40 pub use libmpv_sys::mpv_event_id_MPV_EVENT_LOG_MESSAGE as LogMessage;
41 pub use libmpv_sys::mpv_event_id_MPV_EVENT_NONE as None;
42 pub use libmpv_sys::mpv_event_id_MPV_EVENT_PLAYBACK_RESTART as PlaybackRestart;
43 pub use libmpv_sys::mpv_event_id_MPV_EVENT_PROPERTY_CHANGE as PropertyChange;
44 pub use libmpv_sys::mpv_event_id_MPV_EVENT_QUEUE_OVERFLOW as QueueOverflow;
45 pub use libmpv_sys::mpv_event_id_MPV_EVENT_SEEK as Seek;
46 pub use libmpv_sys::mpv_event_id_MPV_EVENT_SET_PROPERTY_REPLY as SetPropertyReply;
47 pub use libmpv_sys::mpv_event_id_MPV_EVENT_SHUTDOWN as Shutdown;
48 pub use libmpv_sys::mpv_event_id_MPV_EVENT_START_FILE as StartFile;
49 pub use libmpv_sys::mpv_event_id_MPV_EVENT_TICK as Tick;
50 pub use libmpv_sys::mpv_event_id_MPV_EVENT_VIDEO_RECONFIG as VideoReconfig;
51}
52
53impl Mpv {
54 pub fn create_event_context(&self) -> EventContext {
60 match self
61 .events_guard
62 .compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire)
63 {
64 Ok(_) => EventContext {
65 ctx: self.ctx,
66 _does_not_outlive: PhantomData::<&Self>,
67 },
68 Err(_) => panic!("Event context already exists"),
69 }
70 }
71}
72
73#[derive(Debug)]
74pub enum PropertyData<'a> {
76 Str(&'a str),
77 OsdStr(&'a str),
78 Flag(bool),
79 Int64(i64),
80 Double(ctype::c_double),
81 Node(&'a MpvNode),
82}
83
84impl<'a> PropertyData<'a> {
85 unsafe fn from_raw(format: MpvFormat, ptr: *mut ctype::c_void) -> Result<PropertyData<'a>> {
88 assert!(!ptr.is_null());
89 match format {
90 mpv_format::Flag => Ok(PropertyData::Flag(*(ptr as *mut bool))),
91 mpv_format::String => {
92 let char_ptr = *(ptr as *mut *mut ctype::c_char);
93 Ok(PropertyData::Str(mpv_cstr_to_str!(char_ptr)?))
94 }
95 mpv_format::OsdString => {
96 let char_ptr = *(ptr as *mut *mut ctype::c_char);
97 Ok(PropertyData::OsdStr(mpv_cstr_to_str!(char_ptr)?))
98 }
99 mpv_format::Double => Ok(PropertyData::Double(*(ptr as *mut f64))),
100 mpv_format::Int64 => Ok(PropertyData::Int64(*(ptr as *mut i64))),
101 mpv_format::Node => Ok(PropertyData::Node(&*(ptr as *mut MpvNode))),
102 mpv_format::None => unreachable!(),
103 _ => unimplemented!(),
104 }
105 }
106}
107
108#[derive(Debug)]
109pub enum Event<'a> {
110 Shutdown,
112 LogMessage {
114 prefix: &'a str,
115 level: &'a str,
116 text: &'a str,
117 log_level: LogLevel,
118 },
119 GetPropertyReply {
121 name: &'a str,
122 result: PropertyData<'a>,
123 reply_userdata: u64,
124 },
125 SetPropertyReply(u64),
127 CommandReply(u64),
129 StartFile,
131 EndFile(EndFileReason),
133 FileLoaded,
135 ClientMessage(Vec<&'a str>),
136 VideoReconfig,
137 AudioReconfig,
138 Seek,
140 PlaybackRestart,
141 PropertyChange {
143 name: &'a str,
144 change: PropertyData<'a>,
145 reply_userdata: u64,
146 },
147 QueueOverflow,
149 Deprecated(mpv_event),
151}
152
153pub struct EventContext<'parent> {
155 ctx: NonNull<libmpv_sys::mpv_handle>,
156 _does_not_outlive: PhantomData<&'parent Mpv>,
157}
158
159unsafe impl<'parent> Send for EventContext<'parent> {}
160
161impl<'parent> EventContext<'parent> {
162 pub fn enable_event(&self, ev: events::EventId) -> Result<()> {
164 mpv_err((), unsafe {
165 libmpv_sys::mpv_request_event(self.ctx.as_ptr(), ev, 1)
166 })
167 }
168
169 pub fn enable_all_events(&self) -> Result<()> {
171 for i in (2..9)
172 .chain(14..15)
173 .chain(16..19)
174 .chain(20..23)
175 .chain(23..26)
176 {
177 self.enable_event(i)?;
178 }
179 Ok(())
180 }
181
182 pub fn disable_event(&self, ev: events::EventId) -> Result<()> {
184 mpv_err((), unsafe {
185 libmpv_sys::mpv_request_event(self.ctx.as_ptr(), ev, 0)
186 })
187 }
188
189 pub fn disable_deprecated_events(&self) -> Result<()> {
191 self.disable_event(libmpv_sys::mpv_event_id_MPV_EVENT_IDLE)?;
192 Ok(())
193 }
194
195 pub fn disable_all_events(&self) -> Result<()> {
197 for i in 2..26 {
198 self.disable_event(i as _)?;
199 }
200 Ok(())
201 }
202
203 pub fn observe_property(&self, name: &str, format: Format, id: u64) -> Result<()> {
206 let name = CString::new(name)?;
207 mpv_err((), unsafe {
208 libmpv_sys::mpv_observe_property(
209 self.ctx.as_ptr(),
210 id,
211 name.as_ptr(),
212 format.as_mpv_format() as _,
213 )
214 })
215 }
216
217 pub fn unobserve_property(&self, id: u64) -> Result<()> {
219 mpv_err((), unsafe {
220 libmpv_sys::mpv_unobserve_property(self.ctx.as_ptr(), id)
221 })
222 }
223
224 pub fn wait_event(&mut self, timeout: f64) -> Option<Result<Event>> {
233 let event = unsafe { *libmpv_sys::mpv_wait_event(self.ctx.as_ptr(), timeout) };
234 if event.event_id != mpv_event_id::None {
235 if let Err(e) = mpv_err((), event.error) {
236 return Some(Err(e));
237 }
238 }
239
240 match event.event_id {
241 mpv_event_id::None => None,
242 mpv_event_id::Shutdown => Some(Ok(Event::Shutdown)),
243 mpv_event_id::LogMessage => {
244 let log_message =
245 unsafe { *(event.data as *mut libmpv_sys::mpv_event_log_message) };
246
247 let prefix = unsafe { mpv_cstr_to_str!(log_message.prefix) };
248 Some(prefix.and_then(|prefix| {
249 Ok(Event::LogMessage {
250 prefix,
251 level: unsafe { mpv_cstr_to_str!(log_message.level)? },
252 text: unsafe { mpv_cstr_to_str!(log_message.text)? },
253 log_level: log_message.log_level,
254 })
255 }))
256 }
257 mpv_event_id::GetPropertyReply => {
258 let property = unsafe { *(event.data as *mut libmpv_sys::mpv_event_property) };
259
260 let name = unsafe { mpv_cstr_to_str!(property.name) };
261 Some(name.and_then(|name| {
262 let result = unsafe { PropertyData::from_raw(property.format, property.data) }?;
264
265 Ok(Event::GetPropertyReply {
266 name,
267 result,
268 reply_userdata: event.reply_userdata,
269 })
270 }))
271 }
272 mpv_event_id::SetPropertyReply => Some(mpv_err(
273 Event::SetPropertyReply(event.reply_userdata),
274 event.error,
275 )),
276 mpv_event_id::CommandReply => Some(mpv_err(
277 Event::CommandReply(event.reply_userdata),
278 event.error,
279 )),
280 mpv_event_id::StartFile => Some(Ok(Event::StartFile)),
281 mpv_event_id::EndFile => {
282 let end_file = unsafe { *(event.data as *mut libmpv_sys::mpv_event_end_file) };
283
284 if let Err(e) = mpv_err((), end_file.error) {
285 Some(Err(e))
286 } else {
287 Some(Ok(Event::EndFile(end_file.reason as _)))
288 }
289 }
290 mpv_event_id::FileLoaded => Some(Ok(Event::FileLoaded)),
291 mpv_event_id::ClientMessage => {
292 let client_message =
293 unsafe { *(event.data as *mut libmpv_sys::mpv_event_client_message) };
294 let messages = unsafe {
295 slice::from_raw_parts_mut(client_message.args, client_message.num_args as _)
296 };
297 Some(Ok(Event::ClientMessage(
298 messages
299 .iter()
300 .map(|msg| unsafe { mpv_cstr_to_str!(*msg) })
301 .collect::<Result<Vec<_>>>()
302 .unwrap(),
303 )))
304 }
305 mpv_event_id::VideoReconfig => Some(Ok(Event::VideoReconfig)),
306 mpv_event_id::AudioReconfig => Some(Ok(Event::AudioReconfig)),
307 mpv_event_id::Seek => Some(Ok(Event::Seek)),
308 mpv_event_id::PlaybackRestart => Some(Ok(Event::PlaybackRestart)),
309 mpv_event_id::PropertyChange => {
310 let property = unsafe { *(event.data as *mut libmpv_sys::mpv_event_property) };
311
312 if property.format == mpv_format::None {
315 None
316 } else {
317 let name = unsafe { mpv_cstr_to_str!(property.name) };
318 Some(name.and_then(|name| {
319 let change =
321 unsafe { PropertyData::from_raw(property.format, property.data) }?;
322
323 Ok(Event::PropertyChange {
324 name,
325 change,
326 reply_userdata: event.reply_userdata,
327 })
328 }))
329 }
330 }
331 mpv_event_id::QueueOverflow => Some(Ok(Event::QueueOverflow)),
332 _ => Some(Ok(Event::Deprecated(event))),
333 }
334 }
335}