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