1use std::mem::transmute;
4
5use crate::{
6 ffi, gobject_ffi,
7 prelude::*,
8 signal::{connect_raw, SignalHandlerId},
9 translate::*,
10 Object, RustClosure, SignalGroup, Value,
11};
12
13impl SignalGroup {
14 #[doc(alias = "g_signal_group_new")]
15 pub fn new<T: IsA<Object>>() -> Self {
16 Self::with_type(T::static_type())
17 }
18 #[doc(alias = "g_signal_group_connect_closure")]
40 pub fn connect_closure(&self, signal_name: &str, after: bool, closure: RustClosure) {
41 unsafe {
42 gobject_ffi::g_signal_group_connect_closure(
43 self.to_glib_none().0,
44 signal_name.to_glib_none().0,
45 closure.as_ref().to_glib_none().0,
46 after.into_glib(),
47 );
48 }
49 }
50
51 #[doc(alias = "g_signal_group_connect")]
69 #[inline]
70 pub fn connect<F>(&self, signal_name: &str, after: bool, callback: F)
71 where
72 F: Fn(&[Value]) -> Option<Value> + Send + Sync + 'static,
73 {
74 self.connect_closure(signal_name, after, RustClosure::new(callback));
75 }
76
77 #[inline]
82 pub fn connect_local<F>(&self, signal_name: &str, after: bool, callback: F)
83 where
84 F: Fn(&[Value]) -> Option<Value> + 'static,
85 {
86 self.connect_closure(signal_name, after, RustClosure::new_local(callback));
87 }
88
89 #[inline]
90 pub fn connect_notify<F>(&self, name: Option<&str>, callback: F)
91 where
92 F: Fn(&crate::Object, &crate::ParamSpec) + Send + Sync + 'static,
93 {
94 let signal_name = if let Some(name) = name {
95 format!("notify::{name}")
96 } else {
97 "notify".into()
98 };
99
100 let closure = crate::RustClosure::new(move |values| {
101 let obj = values[0].get().unwrap();
102 let pspec = values[1].get().unwrap();
103 callback(obj, pspec);
104
105 None
106 });
107
108 self.connect_closure(&signal_name, false, closure);
109 }
110
111 #[inline]
112 pub fn connect_notify_local<F>(&self, name: Option<&str>, callback: F)
113 where
114 F: Fn(&crate::Object, &crate::ParamSpec) + 'static,
115 {
116 let signal_name = if let Some(name) = name {
117 format!("notify::{name}")
118 } else {
119 "notify".into()
120 };
121
122 let closure = crate::RustClosure::new_local(move |values| {
123 let obj = values[0].get().unwrap();
124 let pspec = values[1].get().unwrap();
125 callback(obj, pspec);
126
127 None
128 });
129
130 self.connect_closure(&signal_name, false, closure);
131 }
132
133 unsafe fn connect_bind_unsafe<F: Fn(&Self, &Object)>(&self, f: F) -> SignalHandlerId {
134 unsafe extern "C" fn bind_trampoline<F: Fn(&SignalGroup, &Object)>(
135 this: *mut crate::gobject_ffi::GSignalGroup,
136 instance: *mut crate::gobject_ffi::GObject,
137 f: ffi::gpointer,
138 ) {
139 let f: &F = &*(f as *const F);
140 f(&from_glib_borrow(this), &from_glib_borrow(instance))
141 }
142 let f: Box<F> = Box::new(f);
143 connect_raw(
144 self.as_ptr() as *mut _,
145 b"bind\0".as_ptr() as *const _,
146 Some(transmute::<*const (), unsafe extern "C" fn()>(
147 bind_trampoline::<F> as *const (),
148 )),
149 Box::into_raw(f),
150 )
151 }
152
153 unsafe fn connect_unbind_unsafe<F: Fn(&Self)>(&self, f: F) -> SignalHandlerId {
154 unsafe extern "C" fn unbind_trampoline<F: Fn(&SignalGroup)>(
155 this: *mut crate::gobject_ffi::GSignalGroup,
156 f: ffi::gpointer,
157 ) {
158 let f: &F = &*(f as *const F);
159 f(&from_glib_borrow(this))
160 }
161 let f: Box<F> = Box::new(f);
162 connect_raw(
163 self.as_ptr() as *mut _,
164 b"unbind\0".as_ptr() as *const _,
165 Some(transmute::<*const (), unsafe extern "C" fn()>(
166 unbind_trampoline::<F> as *const (),
167 )),
168 Box::into_raw(f),
169 )
170 }
171
172 #[doc(alias = "bind")]
186 pub fn connect_bind<F: Fn(&Self, &Object) + Send + Sync + 'static>(
187 &self,
188 f: F,
189 ) -> SignalHandlerId {
190 unsafe { self.connect_bind_unsafe(f) }
191 }
192
193 pub fn connect_bind_local<F: Fn(&Self, &Object) + 'static>(&self, f: F) -> SignalHandlerId {
198 let f = crate::thread_guard::ThreadGuard::new(f);
199
200 unsafe {
201 self.connect_bind_unsafe(move |s, o| {
202 (f.get_ref())(s, o);
203 })
204 }
205 }
206
207 #[doc(alias = "unbind")]
219 pub fn connect_unbind<F: Fn(&Self) + Send + Sync + 'static>(&self, f: F) -> SignalHandlerId {
220 unsafe { self.connect_unbind_unsafe(f) }
221 }
222
223 pub fn connect_unbind_local<F: Fn(&Self) + 'static>(&self, f: F) -> SignalHandlerId {
228 let f = crate::thread_guard::ThreadGuard::new(f);
229
230 unsafe {
231 self.connect_unbind_unsafe(move |s| {
232 (f.get_ref())(s);
233 })
234 }
235 }
236}
237
238#[cfg(test)]
239mod tests {
240 use std::{cell::RefCell, rc::Rc, sync::OnceLock};
241
242 use super::*;
243 use crate as glib;
244
245 mod imp {
246 use super::*;
247 use crate::subclass::{prelude::*, Signal};
248
249 #[derive(Default)]
250 pub struct SignalObject {}
251
252 #[glib::object_subclass]
253 impl ObjectSubclass for SignalObject {
254 const NAME: &'static str = "SignalObject";
255 type Type = super::SignalObject;
256 }
257
258 impl ObjectImpl for SignalObject {
259 fn signals() -> &'static [Signal] {
260 static SIGNALS: OnceLock<Vec<Signal>> = OnceLock::new();
261 SIGNALS.get_or_init(|| {
262 vec![
263 Signal::builder("sig-with-args")
264 .param_types([u32::static_type(), String::static_type()])
265 .build(),
266 Signal::builder("sig-with-ret")
267 .return_type::<String>()
268 .build(),
269 ]
270 })
271 }
272 }
273 }
274
275 wrapper! {
276 pub struct SignalObject(ObjectSubclass<imp::SignalObject>);
277 }
278
279 #[test]
280 fn group_emit() {
281 let group = SignalGroup::new::<SignalObject>();
282
283 let obj = Object::new::<SignalObject>();
284 let store = Rc::new(RefCell::new(String::new()));
285 group.connect_closure(
286 "sig-with-args",
287 false,
288 glib::closure_local!(
289 #[watch]
290 obj,
291 #[strong]
292 store,
293 move |o: &SignalObject, a: u32, b: &str| {
294 assert_eq!(o, obj);
295 store.replace(format!("a {a} b {b}"));
296 }
297 ),
298 );
299 group.connect_closure(
300 "sig-with-ret",
301 false,
302 glib::closure_local!(
303 #[watch]
304 obj,
305 move |o: &SignalObject| -> &'static crate::GStr {
306 assert_eq!(o, obj);
307 crate::gstr!("Hello")
308 }
309 ),
310 );
311 group.set_target(Some(&obj));
312 obj.emit_by_name::<()>("sig-with-args", &[&5u32, &"World"]);
313 assert_eq!(*store.borrow(), "a 5 b World");
314 let ret = obj.emit_by_name::<crate::GString>("sig-with-ret", &[]);
315 assert_eq!(ret, "Hello");
316 group.set_target(Object::NONE);
317 let ret = obj.emit_by_name::<Option<String>>("sig-with-ret", &[]);
318 assert_eq!(ret, None);
319 }
320}