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