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