1use std::{fmt, num::NonZeroU32, ptr, sync::Mutex};
4
5use crate::{
6 ffi, gobject_ffi, prelude::*, translate::*, utils::is_canonical_pspec_name, Closure,
7 SignalFlags, Type, Value,
8};
9
10#[allow(clippy::type_complexity)]
13#[must_use = "The builder must be built to be used"]
14pub struct SignalBuilder {
15 name: String,
16 flags: SignalFlags,
17 param_types: Vec<SignalType>,
18 return_type: SignalType,
19 class_handler: Option<
20 Box<dyn Fn(&SignalClassHandlerToken, &[Value]) -> Option<Value> + Send + Sync + 'static>,
21 >,
22 accumulator: Option<
23 Box<dyn Fn(&SignalInvocationHint, &mut Value, &Value) -> bool + Send + Sync + 'static>,
24 >,
25}
26
27pub struct Signal {
30 name: String,
31 flags: SignalFlags,
32 param_types: Vec<SignalType>,
33 return_type: SignalType,
34 registration: Mutex<SignalRegistration>,
35}
36
37pub struct SignalClassHandlerToken(
40 pub(super) *mut gobject_ffi::GTypeInstance,
43 pub(super) Type,
46 pub(super) *const Value,
49);
50
51impl fmt::Debug for SignalClassHandlerToken {
52 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
53 f.debug_struct("SignalClassHandlerToken")
54 .field("type", &unsafe {
55 crate::Object::from_glib_borrow(self.0 as *mut gobject_ffi::GObject)
56 })
57 .finish()
58 }
59}
60
61#[repr(transparent)]
64pub struct SignalInvocationHint(gobject_ffi::GSignalInvocationHint);
65
66impl SignalInvocationHint {
67 #[inline]
68 pub fn detail(&self) -> Option<crate::Quark> {
69 unsafe { try_from_glib(self.0.detail).ok() }
70 }
71
72 #[inline]
73 pub fn run_type(&self) -> SignalFlags {
74 unsafe { from_glib(self.0.run_type) }
75 }
76}
77
78impl fmt::Debug for SignalInvocationHint {
79 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
80 f.debug_struct("SignalInvocationHint")
81 .field("detail", &self.detail())
82 .field("run_type", &self.run_type())
83 .finish()
84 }
85}
86
87pub struct SignalQuery(gobject_ffi::GSignalQuery);
90
91unsafe impl Send for SignalQuery {}
92unsafe impl Sync for SignalQuery {}
93
94impl SignalQuery {
95 #[inline]
98 pub fn signal_name<'a>(&self) -> &'a str {
99 unsafe {
100 let ptr = self.0.signal_name;
101 std::ffi::CStr::from_ptr(ptr).to_str().unwrap()
102 }
103 }
104
105 #[inline]
108 pub fn signal_id(&self) -> SignalId {
109 unsafe { SignalId::from_glib(self.0.signal_id) }
110 }
111
112 #[inline]
115 pub fn type_(&self) -> Type {
116 unsafe { from_glib(self.0.itype) }
117 }
118
119 #[inline]
122 pub fn flags(&self) -> SignalFlags {
123 unsafe { from_glib(self.0.signal_flags) }
124 }
125
126 #[inline]
129 pub fn return_type(&self) -> SignalType {
130 unsafe { from_glib(self.0.return_type) }
131 }
132
133 #[inline]
136 pub fn n_params(&self) -> u32 {
137 self.0.n_params
138 }
139
140 #[inline]
143 pub fn param_types(&self) -> &[SignalType] {
144 if self.n_params() == 0 {
145 return &[];
146 }
147
148 unsafe {
149 std::slice::from_raw_parts(
150 self.0.param_types as *const SignalType,
151 self.0.n_params as usize,
152 )
153 }
154 }
155}
156
157impl fmt::Debug for SignalQuery {
158 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
159 f.debug_struct("SignalQuery")
160 .field("signal_name", &self.signal_name())
161 .field("type", &self.type_())
162 .field("flags", &self.flags())
163 .field("return_type", &self.return_type())
164 .field("param_types", &self.param_types())
165 .finish()
166 }
167}
168
169#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
172pub struct SignalId(NonZeroU32);
173
174impl SignalId {
175 #[inline]
182 pub unsafe fn new(id: NonZeroU32) -> Self {
183 Self(id)
184 }
185
186 #[doc(alias = "g_signal_parse_name")]
187 #[inline]
188 pub fn parse_name(
189 name: &str,
190 type_: Type,
191 force_detail: bool,
192 ) -> Option<(Self, Option<crate::Quark>)> {
193 let mut signal_id = std::mem::MaybeUninit::uninit();
194 let mut detail_quark = std::mem::MaybeUninit::uninit();
195 unsafe {
196 let found: bool = name.run_with_gstr(|name| {
197 from_glib(gobject_ffi::g_signal_parse_name(
198 name.as_ptr(),
199 type_.into_glib(),
200 signal_id.as_mut_ptr(),
201 detail_quark.as_mut_ptr(),
202 force_detail.into_glib(),
203 ))
204 });
205
206 if found {
207 Some((
208 from_glib(signal_id.assume_init()),
209 crate::Quark::try_from_glib(detail_quark.assume_init()).ok(),
210 ))
211 } else {
212 None
213 }
214 }
215 }
216
217 #[doc(alias = "g_signal_lookup")]
220 #[inline]
221 pub fn lookup(name: &str, type_: Type) -> Option<Self> {
222 unsafe {
223 let signal_id = name.run_with_gstr(|name| {
224 gobject_ffi::g_signal_lookup(name.as_ptr(), type_.into_glib())
225 });
226 if signal_id == 0 {
227 None
228 } else {
229 Some(Self::new(NonZeroU32::new_unchecked(signal_id)))
230 }
231 }
232 }
233
234 #[doc(alias = "g_signal_query")]
237 #[inline]
238 pub fn query(&self) -> SignalQuery {
239 unsafe {
240 let mut query_ptr = std::mem::MaybeUninit::uninit();
241 gobject_ffi::g_signal_query(self.into_glib(), query_ptr.as_mut_ptr());
242 let query = query_ptr.assume_init();
243 debug_assert_ne!(query.signal_id, 0);
244 SignalQuery(query)
245 }
246 }
247
248 #[doc(alias = "g_signal_name")]
251 #[inline]
252 pub fn name<'a>(&self) -> &'a str {
253 unsafe {
254 let ptr = gobject_ffi::g_signal_name(self.into_glib());
255 std::ffi::CStr::from_ptr(ptr).to_str().unwrap()
256 }
257 }
258}
259
260#[doc(hidden)]
261impl FromGlib<u32> for SignalId {
262 #[inline]
263 unsafe fn from_glib(signal_id: u32) -> Self {
264 debug_assert_ne!(signal_id, 0);
265 Self::new(NonZeroU32::new_unchecked(signal_id))
266 }
267}
268
269#[doc(hidden)]
270impl IntoGlib for SignalId {
271 type GlibType = u32;
272
273 #[inline]
274 fn into_glib(self) -> u32 {
275 self.0.into()
276 }
277}
278
279#[derive(Copy, Clone, Hash)]
280#[repr(transparent)]
281pub struct SignalType(ffi::GType);
282
283impl SignalType {
284 #[inline]
285 pub fn with_static_scope(type_: Type) -> Self {
286 Self(type_.into_glib() | gobject_ffi::G_TYPE_FLAG_RESERVED_ID_BIT)
287 }
288
289 #[inline]
290 pub fn static_scope(&self) -> bool {
291 (self.0 & gobject_ffi::G_TYPE_FLAG_RESERVED_ID_BIT) != 0
292 }
293
294 #[inline]
295 pub fn type_(&self) -> Type {
296 (*self).into()
297 }
298}
299
300impl From<Type> for SignalType {
301 #[inline]
302 fn from(type_: Type) -> Self {
303 Self(type_.into_glib())
304 }
305}
306
307impl From<SignalType> for Type {
308 #[inline]
309 fn from(type_: SignalType) -> Self {
310 let type_ = type_.0 & (!gobject_ffi::G_TYPE_FLAG_RESERVED_ID_BIT);
312 unsafe { from_glib(type_) }
313 }
314}
315
316impl PartialEq<Type> for SignalType {
317 #[inline]
318 fn eq(&self, other: &Type) -> bool {
319 let type_: Type = (*self).into();
320 type_.eq(other)
321 }
322}
323
324impl std::fmt::Debug for SignalType {
325 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
326 let type_: Type = (*self).into();
327 f.debug_struct("SignalType")
328 .field("name", &type_.name())
329 .field("static_scope", &self.static_scope())
330 .finish()
331 }
332}
333
334#[doc(hidden)]
335impl FromGlib<ffi::GType> for SignalType {
336 #[inline]
337 unsafe fn from_glib(type_: ffi::GType) -> Self {
338 Self(type_)
339 }
340}
341
342#[doc(hidden)]
343impl IntoGlib for SignalType {
344 type GlibType = ffi::GType;
345
346 #[inline]
347 fn into_glib(self) -> ffi::GType {
348 self.0
349 }
350}
351
352#[allow(clippy::type_complexity)]
353enum SignalRegistration {
354 Unregistered {
355 class_handler: Option<
356 Box<
357 dyn Fn(&SignalClassHandlerToken, &[Value]) -> Option<Value> + Send + Sync + 'static,
358 >,
359 >,
360 accumulator: Option<
361 Box<dyn Fn(&SignalInvocationHint, &mut Value, &Value) -> bool + Send + Sync + 'static>,
362 >,
363 },
364 Registered {
365 type_: Type,
366 signal_id: SignalId,
367 },
368}
369
370impl SignalBuilder {
371 pub fn param_types(
374 mut self,
375 param_types: impl IntoIterator<Item = impl Into<SignalType>>,
376 ) -> Self {
377 self.param_types = param_types
378 .into_iter()
379 .map(|t| t.into())
380 .collect::<Vec<_>>();
381 self
382 }
383
384 pub fn return_type<T: StaticType>(mut self) -> Self {
387 self.return_type = T::static_type().into();
388 self
389 }
390
391 pub fn return_type_from(mut self, type_: impl Into<SignalType>) -> Self {
394 self.return_type = type_.into();
395 self
396 }
397
398 pub fn run_first(mut self) -> Self {
401 self.flags |= SignalFlags::RUN_FIRST;
402 self
403 }
404
405 pub fn run_last(mut self) -> Self {
408 self.flags |= SignalFlags::RUN_LAST;
409 self
410 }
411
412 pub fn run_cleanup(mut self) -> Self {
415 self.flags |= SignalFlags::RUN_CLEANUP;
416 self
417 }
418
419 pub fn no_recurse(mut self) -> Self {
423 self.flags |= SignalFlags::NO_RECURSE;
424 self
425 }
426
427 pub fn detailed(mut self) -> Self {
431 self.flags |= SignalFlags::DETAILED;
432 self
433 }
434
435 pub fn action(mut self) -> Self {
438 self.flags |= SignalFlags::ACTION;
439 self
440 }
441
442 pub fn no_hooks(mut self) -> Self {
445 self.flags |= SignalFlags::NO_HOOKS;
446 self
447 }
448
449 pub fn must_collect(mut self) -> Self {
453 self.flags |= SignalFlags::MUST_COLLECT;
454 self
455 }
456
457 pub fn deprecated(mut self) -> Self {
460 self.flags |= SignalFlags::DEPRECATED;
461 self
462 }
463
464 pub fn flags(mut self, flags: SignalFlags) -> Self {
469 self.flags = flags;
470 self
471 }
472
473 pub fn class_handler<
476 F: Fn(&SignalClassHandlerToken, &[Value]) -> Option<Value> + Send + Sync + 'static,
477 >(
478 mut self,
479 func: F,
480 ) -> Self {
481 self.class_handler = Some(Box::new(func));
482 self
483 }
484
485 pub fn accumulator<
491 F: Fn(&SignalInvocationHint, &mut Value, &Value) -> bool + Send + Sync + 'static,
492 >(
493 mut self,
494 func: F,
495 ) -> Self {
496 self.accumulator = Some(Box::new(func));
497 self
498 }
499
500 #[must_use = "Signal returned from the builder must be used for it to be registered"]
506 pub fn build(self) -> Signal {
507 let flags = if self.flags
508 & (SignalFlags::RUN_FIRST | SignalFlags::RUN_LAST | SignalFlags::RUN_CLEANUP)
509 == SignalFlags::empty()
510 {
511 self.flags | SignalFlags::RUN_LAST
512 } else {
513 self.flags
514 };
515
516 Signal {
517 name: self.name,
518 flags,
519 param_types: self.param_types.to_vec(),
520 return_type: self.return_type,
521 registration: Mutex::new(SignalRegistration::Unregistered {
522 class_handler: self.class_handler,
523 accumulator: self.accumulator,
524 }),
525 }
526 }
527}
528
529impl Signal {
530 pub fn builder(name: &str) -> SignalBuilder {
533 assert!(
534 is_canonical_pspec_name(name),
535 "{name} is not a valid canonical signal name",
536 );
537 SignalBuilder {
538 name: name.to_owned(),
539 param_types: Vec::default(),
540 return_type: <()>::static_type().into(),
541 flags: SignalFlags::empty(),
542 class_handler: None,
543 accumulator: None,
544 }
545 }
546
547 #[inline]
550 pub fn name(&self) -> &str {
551 &self.name
552 }
553
554 #[inline]
557 pub fn flags(&self) -> SignalFlags {
558 self.flags
559 }
560
561 #[inline]
564 pub fn param_types(&self) -> &[SignalType] {
565 &self.param_types
566 }
567
568 #[inline]
571 pub fn return_type(&self) -> SignalType {
572 self.return_type
573 }
574
575 #[inline]
580 pub fn signal_id(&self) -> SignalId {
581 match &*self.registration.lock().unwrap() {
582 SignalRegistration::Unregistered { .. } => panic!("Signal not registered yet"),
583 SignalRegistration::Registered { signal_id, .. } => *signal_id,
584 }
585 }
586
587 #[inline]
592 pub fn type_(&self) -> Type {
593 match &*self.registration.lock().unwrap() {
594 SignalRegistration::Unregistered { .. } => panic!("Signal not registered yet"),
595 SignalRegistration::Registered { type_, .. } => *type_,
596 }
597 }
598
599 pub(super) fn register(&self, type_: Type) {
600 let mut registration = self.registration.lock().unwrap();
601
602 let (class_handler, accumulator) = match &mut *registration {
603 SignalRegistration::Unregistered {
604 class_handler,
605 accumulator,
606 } => (class_handler.take(), accumulator.take()),
607 SignalRegistration::Registered { .. } => unreachable!(),
608 };
609
610 let return_type = self.return_type;
611
612 let class_handler = class_handler.map(|class_handler| {
613 Closure::new(move |values| unsafe {
614 let instance = gobject_ffi::g_value_get_object(values[0].to_glib_none().0);
615 let res = class_handler(&SignalClassHandlerToken(instance as *mut _, return_type.into(), values.as_ptr()), values);
616
617 if return_type == Type::UNIT {
618 if let Some(ref v) = res {
619 panic!("Signal has no return value but class handler returned a value of type {}", v.type_());
620 }
621 } else {
622 match res {
623 None => {
624 panic!("Signal has a return value but class handler returned none");
625 }
626 Some(ref v) => {
627 assert!(v.type_().is_a(return_type.into()), "Signal has a return type of {} but class handler returned {}", Type::from(return_type), v.type_());
628 }
629 }
630 }
631
632 res
633 })
634 });
635
636 unsafe extern "C" fn accumulator_trampoline(
637 ihint: *mut gobject_ffi::GSignalInvocationHint,
638 return_accu: *mut gobject_ffi::GValue,
639 handler_return: *const gobject_ffi::GValue,
640 data: ffi::gpointer,
641 ) -> ffi::gboolean {
642 let accumulator = &*(data as *const (
643 SignalType,
644 Box<
645 dyn Fn(&SignalInvocationHint, &mut Value, &Value) -> bool
646 + Send
647 + Sync
648 + 'static,
649 >,
650 ));
651
652 let return_accu = &mut *(return_accu as *mut Value);
653 let handler_return = &*(handler_return as *const Value);
654 let return_type = accumulator.0;
655
656 assert!(
657 handler_return.type_().is_a(return_type.into()),
658 "Signal has a return type of {} but handler returned {}",
659 Type::from(return_type),
660 handler_return.type_()
661 );
662
663 let res = (accumulator.1)(&SignalInvocationHint(*ihint), return_accu, handler_return)
664 .into_glib();
665
666 assert!(
667 return_accu.type_().is_a(return_type.into()),
668 "Signal has a return type of {} but accumulator returned {}",
669 Type::from(return_type),
670 return_accu.type_()
671 );
672
673 res
674 }
675
676 let (accumulator, accumulator_trampoline) =
677 if let (Some(accumulator), true) = (accumulator, return_type != Type::UNIT) {
678 (
679 Box::into_raw(Box::new((return_type, accumulator))),
680 Some::<unsafe extern "C" fn(_, _, _, _) -> _>(accumulator_trampoline),
681 )
682 } else {
683 (ptr::null_mut(), None)
684 };
685
686 unsafe {
687 let signal_id = gobject_ffi::g_signal_newv(
688 self.name.to_glib_none().0,
689 type_.into_glib(),
690 self.flags.into_glib(),
691 class_handler.to_glib_none().0,
692 accumulator_trampoline,
693 accumulator as ffi::gpointer,
694 None,
695 return_type.into_glib(),
696 self.param_types.len() as u32,
697 self.param_types.as_ptr() as *mut _,
698 );
699 *registration = SignalRegistration::Registered {
700 type_,
701 signal_id: SignalId::from_glib(signal_id),
702 };
703 }
704 }
705}