1use std::mem::transmute;
4
5use glib::{
6 ControlFlow,
7 ffi::{gboolean, gpointer},
8 source::Priority,
9 translate::*,
10};
11
12use crate::{Bus, BusSyncReply, Message, MessageType, ffi};
13
14#[cfg(feature = "futures")]
15pub use crate::bus_futures::BusStream;
16#[cfg(feature = "futures")]
17use futures_util::{StreamExt, stream::FusedStream};
18
19unsafe extern "C" fn trampoline_watch<F: FnMut(&Bus, &Message) -> ControlFlow + Send + 'static>(
20 bus: *mut ffi::GstBus,
21 msg: *mut ffi::GstMessage,
22 func: gpointer,
23) -> gboolean {
24 unsafe {
25 let func: &mut F = &mut *(func as *mut F);
26 func(&from_glib_borrow(bus), &Message::from_glib_borrow(msg)).into_glib()
27 }
28}
29
30unsafe extern "C" fn destroy_closure_watch<
31 F: FnMut(&Bus, &Message) -> ControlFlow + Send + 'static,
32>(
33 ptr: gpointer,
34) {
35 unsafe {
36 let _ = Box::<F>::from_raw(ptr as *mut _);
37 }
38}
39
40fn into_raw_watch<F: FnMut(&Bus, &Message) -> ControlFlow + Send + 'static>(func: F) -> gpointer {
41 #[allow(clippy::type_complexity)]
42 let func: Box<F> = Box::new(func);
43 Box::into_raw(func) as gpointer
44}
45
46unsafe extern "C" fn trampoline_watch_local<F: FnMut(&Bus, &Message) -> ControlFlow + 'static>(
47 bus: *mut ffi::GstBus,
48 msg: *mut ffi::GstMessage,
49 func: gpointer,
50) -> gboolean {
51 unsafe {
52 let func: &mut glib::thread_guard::ThreadGuard<F> =
53 &mut *(func as *mut glib::thread_guard::ThreadGuard<F>);
54 (func.get_mut())(&from_glib_borrow(bus), &Message::from_glib_borrow(msg)).into_glib()
55 }
56}
57
58unsafe extern "C" fn destroy_closure_watch_local<
59 F: FnMut(&Bus, &Message) -> ControlFlow + 'static,
60>(
61 ptr: gpointer,
62) {
63 unsafe {
64 let _ = Box::<glib::thread_guard::ThreadGuard<F>>::from_raw(ptr as *mut _);
65 }
66}
67
68fn into_raw_watch_local<F: FnMut(&Bus, &Message) -> ControlFlow + 'static>(func: F) -> gpointer {
69 #[allow(clippy::type_complexity)]
70 let func: Box<glib::thread_guard::ThreadGuard<F>> =
71 Box::new(glib::thread_guard::ThreadGuard::new(func));
72 Box::into_raw(func) as gpointer
73}
74
75unsafe extern "C" fn trampoline_sync<
76 F: Fn(&Bus, &Message) -> BusSyncReply + Send + Sync + 'static,
77>(
78 bus: *mut ffi::GstBus,
79 msg: *mut ffi::GstMessage,
80 func: gpointer,
81) -> ffi::GstBusSyncReply {
82 unsafe {
83 let f: &F = &*(func as *const F);
84 let res = f(&from_glib_borrow(bus), &Message::from_glib_borrow(msg)).into_glib();
85
86 if res == ffi::GST_BUS_DROP {
87 ffi::gst_mini_object_unref(msg as *mut _);
88 }
89
90 res
91 }
92}
93
94unsafe extern "C" fn destroy_closure_sync<
95 F: Fn(&Bus, &Message) -> BusSyncReply + Send + Sync + 'static,
96>(
97 ptr: gpointer,
98) {
99 unsafe {
100 let _ = Box::<F>::from_raw(ptr as *mut _);
101 }
102}
103
104fn into_raw_sync<F: Fn(&Bus, &Message) -> BusSyncReply + Send + Sync + 'static>(
105 func: F,
106) -> gpointer {
107 let func: Box<F> = Box::new(func);
108 Box::into_raw(func) as gpointer
109}
110
111impl Bus {
112 #[doc(alias = "gst_bus_add_signal_watch")]
130 #[doc(alias = "gst_bus_add_signal_watch_full")]
131 pub fn add_signal_watch_full(&self, priority: Priority) {
132 unsafe {
133 ffi::gst_bus_add_signal_watch_full(self.to_glib_none().0, priority.into_glib());
134 }
135 }
136
137 #[doc(alias = "gst_bus_create_watch")]
148 pub fn create_watch<F>(&self, name: Option<&str>, priority: Priority, func: F) -> glib::Source
149 where
150 F: FnMut(&Bus, &Message) -> ControlFlow + Send + 'static,
151 {
152 skip_assert_initialized!();
153 unsafe {
154 let source = ffi::gst_bus_create_watch(self.to_glib_none().0);
155 glib::ffi::g_source_set_callback(
156 source,
157 Some(transmute::<
158 *mut (),
159 unsafe extern "C" fn(glib::ffi::gpointer) -> i32,
160 >(trampoline_watch::<F> as *mut ())),
161 into_raw_watch(func),
162 Some(destroy_closure_watch::<F>),
163 );
164 glib::ffi::g_source_set_priority(source, priority.into_glib());
165
166 if let Some(name) = name {
167 glib::ffi::g_source_set_name(source, name.to_glib_none().0);
168 }
169
170 from_glib_full(source)
171 }
172 }
173
174 #[doc(alias = "gst_bus_add_watch")]
199 #[doc(alias = "gst_bus_add_watch_full")]
200 pub fn add_watch<F>(&self, func: F) -> Result<BusWatchGuard, glib::BoolError>
201 where
202 F: FnMut(&Bus, &Message) -> ControlFlow + Send + 'static,
203 {
204 unsafe {
205 let res = ffi::gst_bus_add_watch_full(
206 self.to_glib_none().0,
207 glib::ffi::G_PRIORITY_DEFAULT,
208 Some(trampoline_watch::<F>),
209 into_raw_watch(func),
210 Some(destroy_closure_watch::<F>),
211 );
212
213 if res == 0 {
214 Err(glib::bool_error!("Bus already has a watch"))
215 } else {
216 Ok(BusWatchGuard { bus: self.clone() })
217 }
218 }
219 }
220
221 #[doc(alias = "gst_bus_add_watch")]
222 #[doc(alias = "gst_bus_add_watch_full")]
223 pub fn add_watch_local<F>(&self, func: F) -> Result<BusWatchGuard, glib::BoolError>
224 where
225 F: FnMut(&Bus, &Message) -> ControlFlow + 'static,
226 {
227 unsafe {
228 let ctx = glib::MainContext::ref_thread_default();
229 let _acquire = ctx
230 .acquire()
231 .expect("thread default main context already acquired by another thread");
232
233 let res = ffi::gst_bus_add_watch_full(
234 self.to_glib_none().0,
235 glib::ffi::G_PRIORITY_DEFAULT,
236 Some(trampoline_watch_local::<F>),
237 into_raw_watch_local(func),
238 Some(destroy_closure_watch_local::<F>),
239 );
240
241 if res == 0 {
242 Err(glib::bool_error!("Bus already has a watch"))
243 } else {
244 Ok(BusWatchGuard { bus: self.clone() })
245 }
246 }
247 }
248
249 #[doc(alias = "gst_bus_set_sync_handler")]
263 pub fn set_sync_handler<F>(&self, func: F)
264 where
265 F: Fn(&Bus, &Message) -> BusSyncReply + Send + Sync + 'static,
266 {
267 unsafe {
268 let bus = self.to_glib_none().0;
269
270 #[allow(clippy::manual_dangling_ptr)]
271 #[cfg(not(feature = "v1_18"))]
272 {
273 static SET_ONCE_QUARK: std::sync::OnceLock<glib::Quark> =
274 std::sync::OnceLock::new();
275
276 let set_once_quark = SET_ONCE_QUARK
277 .get_or_init(|| glib::Quark::from_str("gstreamer-rs-sync-handler"));
278
279 if crate::version() < (1, 16, 3, 0) {
282 if !glib::gobject_ffi::g_object_get_qdata(
283 bus as *mut _,
284 set_once_quark.into_glib(),
285 )
286 .is_null()
287 {
288 panic!("Bus sync handler can only be set once");
289 }
290
291 glib::gobject_ffi::g_object_set_qdata(
292 bus as *mut _,
293 set_once_quark.into_glib(),
294 1 as *mut _,
295 );
296 }
297 }
298
299 ffi::gst_bus_set_sync_handler(
300 bus,
301 Some(trampoline_sync::<F>),
302 into_raw_sync(func),
303 Some(destroy_closure_sync::<F>),
304 )
305 }
306 }
307
308 pub fn unset_sync_handler(&self) {
309 #[cfg(not(feature = "v1_18"))]
310 {
311 if crate::version() < (1, 16, 3, 0) {
314 return;
315 }
316 }
317
318 unsafe {
319 use std::ptr;
320
321 ffi::gst_bus_set_sync_handler(self.to_glib_none().0, None, ptr::null_mut(), None)
322 }
323 }
324
325 #[doc(alias = "gst_bus_pop")]
326 pub fn iter(&self) -> Iter<'_> {
327 self.iter_timed(Some(crate::ClockTime::ZERO))
328 }
329
330 #[doc(alias = "gst_bus_timed_pop")]
331 pub fn iter_timed(&self, timeout: impl Into<Option<crate::ClockTime>>) -> Iter<'_> {
332 Iter {
333 bus: self,
334 timeout: timeout.into(),
335 }
336 }
337
338 #[doc(alias = "gst_bus_pop_filtered")]
339 pub fn iter_filtered<'a>(
340 &'a self,
341 msg_types: &'a [MessageType],
342 ) -> impl Iterator<Item = Message> + 'a {
343 self.iter_timed_filtered(Some(crate::ClockTime::ZERO), msg_types)
344 }
345
346 #[doc(alias = "gst_bus_timed_pop_filtered")]
347 pub fn iter_timed_filtered<'a>(
348 &'a self,
349 timeout: impl Into<Option<crate::ClockTime>>,
350 msg_types: &'a [MessageType],
351 ) -> impl Iterator<Item = Message> + 'a {
352 self.iter_timed(timeout)
353 .filter(move |msg| msg_types.contains(&msg.type_()))
354 }
355
356 #[doc(alias = "gst_bus_timed_pop_filtered")]
374 pub fn timed_pop_filtered(
375 &self,
376 timeout: impl Into<Option<crate::ClockTime>>,
377 msg_types: &[MessageType],
378 ) -> Option<Message> {
379 let Some(timeout) = timeout.into() else {
381 loop {
382 let msg = self.timed_pop(None)?;
383 if msg_types.contains(&msg.type_()) {
384 return Some(msg);
385 }
386 }
387 };
388
389 let total = timeout;
391 let start = std::time::Instant::now();
392
393 loop {
394 let elapsed = crate::ClockTime::from_nseconds(start.elapsed().as_nanos() as u64);
395
396 let remaining = total.checked_sub(elapsed)?;
398
399 let msg = self.timed_pop(Some(remaining))?;
400
401 if msg_types.contains(&msg.type_()) {
402 return Some(msg);
403 }
404
405 }
407 }
408
409 #[doc(alias = "gst_bus_pop_filtered")]
423 pub fn pop_filtered(&self, msg_types: &[MessageType]) -> Option<Message> {
424 loop {
425 let msg = self.pop()?;
426 if msg_types.contains(&msg.type_()) {
427 return Some(msg);
428 }
429 }
430 }
431
432 #[cfg(feature = "futures")]
433 pub fn stream(&self) -> BusStream {
434 BusStream::new(self)
435 }
436
437 #[cfg(feature = "futures")]
438 pub fn stream_filtered<'a>(
439 &self,
440 message_types: &'a [MessageType],
441 ) -> impl FusedStream<Item = Message> + Unpin + Send + 'a + use<'a> {
442 self.stream().filter(move |message| {
443 let message_type = message.type_();
444
445 std::future::ready(message_types.contains(&message_type))
446 })
447 }
448}
449
450#[must_use = "iterators are lazy and do nothing unless consumed"]
451#[derive(Debug)]
452pub struct Iter<'a> {
453 bus: &'a Bus,
454 timeout: Option<crate::ClockTime>,
455}
456
457impl Iterator for Iter<'_> {
458 type Item = Message;
459
460 fn next(&mut self) -> Option<Message> {
461 self.bus.timed_pop(self.timeout)
462 }
463}
464
465#[derive(Debug)]
470#[must_use = "if unused the bus watch will immediately be removed"]
471pub struct BusWatchGuard {
472 bus: Bus,
473}
474
475impl Drop for BusWatchGuard {
476 fn drop(&mut self) {
477 let _ = self.bus.remove_watch();
478 }
479}
480
481#[cfg(test)]
482mod tests {
483 use std::sync::{Arc, Mutex};
484
485 use super::*;
486
487 #[test]
488 fn test_sync_handler() {
489 crate::init().unwrap();
490
491 let bus = Bus::new();
492 let msgs = Arc::new(Mutex::new(Vec::new()));
493 let msgs_clone = msgs.clone();
494 bus.set_sync_handler(move |_, msg| {
495 msgs_clone.lock().unwrap().push(msg.clone());
496 BusSyncReply::Pass
497 });
498
499 bus.post(crate::message::Eos::new()).unwrap();
500
501 let msgs = msgs.lock().unwrap();
502 assert_eq!(msgs.len(), 1);
503 match msgs[0].view() {
504 crate::MessageView::Eos(_) => (),
505 _ => unreachable!(),
506 }
507 }
508}