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")]
29 pub fn connect_closure(&self, signal_name: &str, after: bool, closure: RustClosure) {
30 unsafe {
31 gobject_ffi::g_signal_group_connect_closure(
32 self.to_glib_none().0,
33 signal_name.to_glib_none().0,
34 closure.as_ref().to_glib_none().0,
35 after.into_glib(),
36 );
37 }
38 }
39
40 #[doc(alias = "g_signal_group_connect")]
49 #[inline]
50 pub fn connect<F>(&self, signal_name: &str, after: bool, callback: F)
51 where
52 F: Fn(&[Value]) -> Option<Value> + Send + Sync + 'static,
53 {
54 self.connect_closure(signal_name, after, RustClosure::new(callback));
55 }
56
57 #[inline]
62 pub fn connect_local<F>(&self, signal_name: &str, after: bool, callback: F)
63 where
64 F: Fn(&[Value]) -> Option<Value> + 'static,
65 {
66 self.connect_closure(signal_name, after, RustClosure::new_local(callback));
67 }
68
69 #[inline]
70 pub fn connect_notify<F>(&self, name: Option<&str>, callback: F)
71 where
72 F: Fn(&crate::Object, &crate::ParamSpec) + Send + Sync + 'static,
73 {
74 let signal_name = if let Some(name) = name {
75 format!("notify::{name}")
76 } else {
77 "notify".into()
78 };
79
80 let closure = crate::RustClosure::new(move |values| {
81 let obj = values[0].get().unwrap();
82 let pspec = values[1].get().unwrap();
83 callback(obj, pspec);
84
85 None
86 });
87
88 self.connect_closure(&signal_name, false, closure);
89 }
90
91 #[inline]
92 pub fn connect_notify_local<F>(&self, name: Option<&str>, callback: F)
93 where
94 F: Fn(&crate::Object, &crate::ParamSpec) + 'static,
95 {
96 let signal_name = if let Some(name) = name {
97 format!("notify::{name}")
98 } else {
99 "notify".into()
100 };
101
102 let closure = crate::RustClosure::new_local(move |values| {
103 let obj = values[0].get().unwrap();
104 let pspec = values[1].get().unwrap();
105 callback(obj, pspec);
106
107 None
108 });
109
110 self.connect_closure(&signal_name, false, closure);
111 }
112
113 unsafe fn connect_bind_unsafe<F: Fn(&Self, &Object)>(&self, f: F) -> SignalHandlerId {
114 unsafe extern "C" fn bind_trampoline<F: Fn(&SignalGroup, &Object)>(
115 this: *mut crate::gobject_ffi::GSignalGroup,
116 instance: *mut crate::gobject_ffi::GObject,
117 f: ffi::gpointer,
118 ) {
119 let f: &F = &*(f as *const F);
120 f(&from_glib_borrow(this), &from_glib_borrow(instance))
121 }
122 let f: Box<F> = Box::new(f);
123 connect_raw(
124 self.as_ptr() as *mut _,
125 b"bind\0".as_ptr() as *const _,
126 Some(transmute::<*const (), unsafe extern "C" fn()>(
127 bind_trampoline::<F> as *const (),
128 )),
129 Box::into_raw(f),
130 )
131 }
132
133 unsafe fn connect_unbind_unsafe<F: Fn(&Self)>(&self, f: F) -> SignalHandlerId {
134 unsafe extern "C" fn unbind_trampoline<F: Fn(&SignalGroup)>(
135 this: *mut crate::gobject_ffi::GSignalGroup,
136 f: ffi::gpointer,
137 ) {
138 let f: &F = &*(f as *const F);
139 f(&from_glib_borrow(this))
140 }
141 let f: Box<F> = Box::new(f);
142 connect_raw(
143 self.as_ptr() as *mut _,
144 b"unbind\0".as_ptr() as *const _,
145 Some(transmute::<*const (), unsafe extern "C" fn()>(
146 unbind_trampoline::<F> as *const (),
147 )),
148 Box::into_raw(f),
149 )
150 }
151
152 #[doc(alias = "bind")]
159 pub fn connect_bind<F: Fn(&Self, &Object) + Send + Sync + 'static>(
160 &self,
161 f: F,
162 ) -> SignalHandlerId {
163 unsafe { self.connect_bind_unsafe(f) }
164 }
165
166 pub fn connect_bind_local<F: Fn(&Self, &Object) + 'static>(&self, f: F) -> SignalHandlerId {
171 let f = crate::thread_guard::ThreadGuard::new(f);
172
173 unsafe {
174 self.connect_bind_unsafe(move |s, o| {
175 (f.get_ref())(s, o);
176 })
177 }
178 }
179
180 #[doc(alias = "unbind")]
186 pub fn connect_unbind<F: Fn(&Self) + Send + Sync + 'static>(&self, f: F) -> SignalHandlerId {
187 unsafe { self.connect_unbind_unsafe(f) }
188 }
189
190 pub fn connect_unbind_local<F: Fn(&Self) + 'static>(&self, f: F) -> SignalHandlerId {
195 let f = crate::thread_guard::ThreadGuard::new(f);
196
197 unsafe {
198 self.connect_unbind_unsafe(move |s| {
199 (f.get_ref())(s);
200 })
201 }
202 }
203}
204
205#[cfg(test)]
206mod tests {
207 use std::{cell::RefCell, rc::Rc, sync::OnceLock};
208
209 use super::*;
210 use crate as glib;
211
212 mod imp {
213 use super::*;
214 use crate::subclass::{prelude::*, Signal};
215
216 #[derive(Default)]
217 pub struct SignalObject {}
218
219 #[glib::object_subclass]
220 impl ObjectSubclass for SignalObject {
221 const NAME: &'static str = "SignalObject";
222 type Type = super::SignalObject;
223 }
224
225 impl ObjectImpl for SignalObject {
226 fn signals() -> &'static [Signal] {
227 static SIGNALS: OnceLock<Vec<Signal>> = OnceLock::new();
228 SIGNALS.get_or_init(|| {
229 vec![
230 Signal::builder("sig-with-args")
231 .param_types([u32::static_type(), String::static_type()])
232 .build(),
233 Signal::builder("sig-with-ret")
234 .return_type::<String>()
235 .build(),
236 ]
237 })
238 }
239 }
240 }
241
242 wrapper! {
243 pub struct SignalObject(ObjectSubclass<imp::SignalObject>);
244 }
245
246 #[test]
247 fn group_emit() {
248 let group = SignalGroup::new::<SignalObject>();
249
250 let obj = Object::new::<SignalObject>();
251 let store = Rc::new(RefCell::new(String::new()));
252 group.connect_closure(
253 "sig-with-args",
254 false,
255 glib::closure_local!(
256 #[watch]
257 obj,
258 #[strong]
259 store,
260 move |o: &SignalObject, a: u32, b: &str| {
261 assert_eq!(o, obj);
262 store.replace(format!("a {a} b {b}"));
263 }
264 ),
265 );
266 group.connect_closure(
267 "sig-with-ret",
268 false,
269 glib::closure_local!(
270 #[watch]
271 obj,
272 move |o: &SignalObject| -> &'static crate::GStr {
273 assert_eq!(o, obj);
274 crate::gstr!("Hello")
275 }
276 ),
277 );
278 group.set_target(Some(&obj));
279 obj.emit_by_name::<()>("sig-with-args", &[&5u32, &"World"]);
280 assert_eq!(*store.borrow(), "a 5 b World");
281 let ret = obj.emit_by_name::<crate::GString>("sig-with-ret", &[]);
282 assert_eq!(ret, "Hello");
283 group.set_target(Object::NONE);
284 let ret = obj.emit_by_name::<Option<String>>("sig-with-ret", &[]);
285 assert_eq!(ret, None);
286 }
287}