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