1 // Copyright (C) 2017 Sebastian Dröge <sebastian@centricular.com>
2 //
3 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6 // option. This file may not be copied, modified, or distributed
7 // except according to those terms.
8 
9 use glib;
10 use glib::object::{Cast, ObjectExt};
11 use glib::signal::SignalHandlerId;
12 use glib::translate::*;
13 use glib::IsA;
14 
15 use gobject_sys;
16 
17 use ObjectFlags;
18 
19 pub trait GstObjectExtManual: 'static {
connect_deep_notify<F: Fn(&Self, &::Object, &glib::ParamSpec) + Send + Sync + 'static>( &self, name: Option<&str>, f: F, ) -> SignalHandlerId20     fn connect_deep_notify<F: Fn(&Self, &::Object, &glib::ParamSpec) + Send + Sync + 'static>(
21         &self,
22         name: Option<&str>,
23         f: F,
24     ) -> SignalHandlerId;
25 
set_object_flags(&self, flags: ObjectFlags)26     fn set_object_flags(&self, flags: ObjectFlags);
27 
unset_object_flags(&self, flags: ObjectFlags)28     fn unset_object_flags(&self, flags: ObjectFlags);
29 
get_object_flags(&self) -> ObjectFlags30     fn get_object_flags(&self) -> ObjectFlags;
31 }
32 
33 impl<O: IsA<::Object>> GstObjectExtManual for O {
connect_deep_notify<F: Fn(&Self, &::Object, &glib::ParamSpec) + Send + Sync + 'static>( &self, name: Option<&str>, f: F, ) -> SignalHandlerId34     fn connect_deep_notify<F: Fn(&Self, &::Object, &glib::ParamSpec) + Send + Sync + 'static>(
35         &self,
36         name: Option<&str>,
37         f: F,
38     ) -> SignalHandlerId {
39         let signal_name = if let Some(name) = name {
40             format!("deep-notify::{}", name)
41         } else {
42             "deep-notify".into()
43         };
44 
45         let obj: glib::Object =
46             unsafe { from_glib_borrow(self.as_ptr() as *mut gobject_sys::GObject) };
47 
48         obj.connect(signal_name.as_str(), false, move |values| {
49             // It would be nice to display the actual signal name in the panic messages below,
50             // but that would require to copy `signal_name` so as to move it into the closure
51             // which seems too much for the messages of development errors
52             let obj: O = unsafe {
53                 values[0]
54                     .get::<::Object>()
55                     .unwrap_or_else(|err| {
56                         panic!("Object signal \"deep-notify\": values[0]: {}", err)
57                     })
58                     .expect("Object signal \"deep-notify\": values[0] not defined")
59                     .unsafe_cast()
60             };
61             let prop_obj: ::Object = values[1]
62                 .get()
63                 .unwrap_or_else(|err| panic!("Object signal \"deep-notify\": values[1]: {}", err))
64                 .expect("Object signal \"deep-notify\": values[1] not defined");
65 
66             let pspec = unsafe {
67                 let pspec = gobject_sys::g_value_get_param(values[2].to_glib_none().0);
68                 from_glib_none(pspec)
69             };
70 
71             f(&obj, &prop_obj, &pspec);
72 
73             None
74         })
75         .unwrap()
76     }
77 
set_object_flags(&self, flags: ObjectFlags)78     fn set_object_flags(&self, flags: ObjectFlags) {
79         unsafe {
80             let ptr: *mut gst_sys::GstObject = self.as_ptr() as *mut _;
81             let _guard = ::utils::MutexGuard::lock(&(*ptr).lock);
82             (*ptr).flags |= flags.to_glib();
83         }
84     }
85 
unset_object_flags(&self, flags: ObjectFlags)86     fn unset_object_flags(&self, flags: ObjectFlags) {
87         unsafe {
88             let ptr: *mut gst_sys::GstObject = self.as_ptr() as *mut _;
89             let _guard = ::utils::MutexGuard::lock(&(*ptr).lock);
90             (*ptr).flags &= !flags.to_glib();
91         }
92     }
93 
get_object_flags(&self) -> ObjectFlags94     fn get_object_flags(&self) -> ObjectFlags {
95         unsafe {
96             let ptr: *mut gst_sys::GstObject = self.as_ptr() as *mut _;
97             let _guard = ::utils::MutexGuard::lock(&(*ptr).lock);
98             from_glib((*ptr).flags)
99         }
100     }
101 }
102 
103 #[cfg(test)]
104 mod tests {
105     use super::*;
106     use prelude::*;
107     use std::sync::{Arc, Mutex};
108 
109     #[test]
test_deep_notify()110     fn test_deep_notify() {
111         ::init().unwrap();
112 
113         let bin = ::Bin::new(None);
114         let identity = ::ElementFactory::make("identity", Some("id")).unwrap();
115         bin.add(&identity).unwrap();
116 
117         let notify = Arc::new(Mutex::new(None));
118         let notify_clone = notify.clone();
119         bin.connect_deep_notify(None, move |_, id, prop| {
120             *notify_clone.lock().unwrap() = Some((id.clone(), prop.get_name()));
121         });
122 
123         identity.set_property("silent", &false).unwrap();
124         assert_eq!(
125             *notify.lock().unwrap(),
126             Some((identity.upcast::<::Object>(), String::from("silent")))
127         );
128     }
129 }
130