1use std::{fmt, num::NonZeroU32, ops::ControlFlow, ptr, sync::Mutex};
4
5use crate::{
6 Closure, SignalFlags, Type, Value, ffi, gobject_ffi, prelude::*, translate::*,
7 utils::is_canonical_pspec_name,
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<Box<dyn Fn(&[Value]) -> Option<Value> + Send + Sync + 'static>>,
20 accumulator: Option<
21 Box<
22 dyn Fn(&SignalInvocationHint, Value, &Value) -> ControlFlow<Value, Value>
23 + Send
24 + Sync
25 + 'static,
26 >,
27 >,
28}
29
30pub struct Signal {
33 name: String,
34 flags: SignalFlags,
35 param_types: Vec<SignalType>,
36 return_type: SignalType,
37 registration: Mutex<SignalRegistration>,
38}
39
40pub struct SignalClassHandlerToken(
43 pub(super) *mut gobject_ffi::GTypeInstance,
46 pub(super) Type,
49 pub(super) *const Value,
52);
53
54impl fmt::Debug for SignalClassHandlerToken {
55 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
56 f.debug_struct("SignalClassHandlerToken")
57 .field("type", &unsafe {
58 crate::Object::from_glib_borrow(self.0 as *mut gobject_ffi::GObject)
59 })
60 .finish()
61 }
62}
63
64#[repr(transparent)]
67pub struct SignalInvocationHint(gobject_ffi::GSignalInvocationHint);
68
69impl SignalInvocationHint {
70 #[inline]
71 pub fn detail(&self) -> Option<crate::Quark> {
72 unsafe { try_from_glib(self.0.detail).ok() }
73 }
74
75 #[inline]
76 pub fn run_type(&self) -> SignalFlags {
77 unsafe { from_glib(self.0.run_type) }
78 }
79}
80
81impl fmt::Debug for SignalInvocationHint {
82 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
83 f.debug_struct("SignalInvocationHint")
84 .field("detail", &self.detail())
85 .field("run_type", &self.run_type())
86 .finish()
87 }
88}
89
90pub struct SignalQuery(gobject_ffi::GSignalQuery);
93
94unsafe impl Send for SignalQuery {}
95unsafe impl Sync for SignalQuery {}
96
97impl SignalQuery {
98 #[inline]
101 pub fn signal_name<'a>(&self) -> &'a str {
102 unsafe {
103 let ptr = self.0.signal_name;
104 std::ffi::CStr::from_ptr(ptr).to_str().unwrap()
105 }
106 }
107
108 #[inline]
111 pub fn signal_id(&self) -> SignalId {
112 unsafe { SignalId::from_glib(self.0.signal_id) }
113 }
114
115 #[inline]
118 pub fn type_(&self) -> Type {
119 unsafe { from_glib(self.0.itype) }
120 }
121
122 #[inline]
125 pub fn flags(&self) -> SignalFlags {
126 unsafe { from_glib(self.0.signal_flags) }
127 }
128
129 #[inline]
132 pub fn return_type(&self) -> SignalType {
133 unsafe { from_glib(self.0.return_type) }
134 }
135
136 #[inline]
139 pub fn n_params(&self) -> u32 {
140 self.0.n_params
141 }
142
143 #[inline]
146 pub fn param_types(&self) -> &[SignalType] {
147 if self.n_params() == 0 {
148 return &[];
149 }
150
151 unsafe {
152 std::slice::from_raw_parts(
153 self.0.param_types as *const SignalType,
154 self.0.n_params as usize,
155 )
156 }
157 }
158}
159
160impl fmt::Debug for SignalQuery {
161 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
162 f.debug_struct("SignalQuery")
163 .field("signal_name", &self.signal_name())
164 .field("type", &self.type_())
165 .field("flags", &self.flags())
166 .field("return_type", &self.return_type())
167 .field("param_types", &self.param_types())
168 .finish()
169 }
170}
171
172#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
175pub struct SignalId(NonZeroU32);
176
177impl SignalId {
178 #[inline]
185 pub unsafe fn new(id: NonZeroU32) -> Self {
186 Self(id)
187 }
188
189 #[doc(alias = "g_signal_parse_name")]
190 #[inline]
191 pub fn parse_name(
192 name: &str,
193 type_: Type,
194 force_detail: bool,
195 ) -> Option<(Self, Option<crate::Quark>)> {
196 let mut signal_id = std::mem::MaybeUninit::uninit();
197 let mut detail_quark = std::mem::MaybeUninit::uninit();
198 unsafe {
199 let found: bool = name.run_with_gstr(|name| {
200 from_glib(gobject_ffi::g_signal_parse_name(
201 name.as_ptr(),
202 type_.into_glib(),
203 signal_id.as_mut_ptr(),
204 detail_quark.as_mut_ptr(),
205 force_detail.into_glib(),
206 ))
207 });
208
209 if found {
210 Some((
211 from_glib(signal_id.assume_init()),
212 crate::Quark::try_from_glib(detail_quark.assume_init()).ok(),
213 ))
214 } else {
215 None
216 }
217 }
218 }
219
220 #[doc(alias = "g_signal_lookup")]
223 #[inline]
224 pub fn lookup(name: &str, type_: Type) -> Option<Self> {
225 unsafe {
226 let signal_id = name.run_with_gstr(|name| {
227 gobject_ffi::g_signal_lookup(name.as_ptr(), type_.into_glib())
228 });
229 if signal_id == 0 {
230 None
231 } else {
232 Some(Self::new(NonZeroU32::new_unchecked(signal_id)))
233 }
234 }
235 }
236
237 #[doc(alias = "g_signal_query")]
240 #[inline]
241 pub fn query(&self) -> SignalQuery {
242 unsafe {
243 let mut query_ptr = std::mem::MaybeUninit::uninit();
244 gobject_ffi::g_signal_query(self.into_glib(), query_ptr.as_mut_ptr());
245 let query = query_ptr.assume_init();
246 debug_assert_ne!(query.signal_id, 0);
247 SignalQuery(query)
248 }
249 }
250
251 #[doc(alias = "g_signal_name")]
254 #[inline]
255 pub fn name<'a>(&self) -> &'a str {
256 unsafe {
257 let ptr = gobject_ffi::g_signal_name(self.into_glib());
258 std::ffi::CStr::from_ptr(ptr).to_str().unwrap()
259 }
260 }
261}
262
263#[doc(hidden)]
264impl FromGlib<u32> for SignalId {
265 #[inline]
266 unsafe fn from_glib(signal_id: u32) -> Self {
267 unsafe {
268 debug_assert_ne!(signal_id, 0);
269 Self::new(NonZeroU32::new_unchecked(signal_id))
270 }
271 }
272}
273
274#[doc(hidden)]
275impl IntoGlib for SignalId {
276 type GlibType = u32;
277
278 #[inline]
279 fn into_glib(self) -> u32 {
280 self.0.into()
281 }
282}
283
284#[derive(Copy, Clone, Hash)]
285#[repr(transparent)]
286pub struct SignalType(ffi::GType);
287
288impl SignalType {
289 #[inline]
290 pub fn with_static_scope(type_: Type) -> Self {
291 Self(type_.into_glib() | gobject_ffi::G_TYPE_FLAG_RESERVED_ID_BIT)
292 }
293
294 #[inline]
295 pub fn static_scope(&self) -> bool {
296 (self.0 & gobject_ffi::G_TYPE_FLAG_RESERVED_ID_BIT) != 0
297 }
298
299 #[inline]
300 pub fn type_(&self) -> Type {
301 (*self).into()
302 }
303}
304
305impl From<Type> for SignalType {
306 #[inline]
307 fn from(type_: Type) -> Self {
308 Self(type_.into_glib())
309 }
310}
311
312impl From<SignalType> for Type {
313 #[inline]
314 fn from(type_: SignalType) -> Self {
315 let type_ = type_.0 & (!gobject_ffi::G_TYPE_FLAG_RESERVED_ID_BIT);
317 unsafe { from_glib(type_) }
318 }
319}
320
321impl PartialEq<Type> for SignalType {
322 #[inline]
323 fn eq(&self, other: &Type) -> bool {
324 let type_: Type = (*self).into();
325 type_.eq(other)
326 }
327}
328
329impl std::fmt::Debug for SignalType {
330 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
331 let type_: Type = (*self).into();
332 f.debug_struct("SignalType")
333 .field("name", &type_.name())
334 .field("static_scope", &self.static_scope())
335 .finish()
336 }
337}
338
339#[doc(hidden)]
340impl FromGlib<ffi::GType> for SignalType {
341 #[inline]
342 unsafe fn from_glib(type_: ffi::GType) -> Self {
343 Self(type_)
344 }
345}
346
347#[doc(hidden)]
348impl IntoGlib for SignalType {
349 type GlibType = ffi::GType;
350
351 #[inline]
352 fn into_glib(self) -> ffi::GType {
353 self.0
354 }
355}
356
357#[allow(clippy::type_complexity)]
358enum SignalRegistration {
359 Unregistered {
360 class_handler: Option<Box<dyn Fn(&[Value]) -> Option<Value> + Send + Sync + 'static>>,
361 accumulator: Option<
362 Box<
363 dyn Fn(&SignalInvocationHint, Value, &Value) -> ControlFlow<Value, Value>
364 + Send
365 + Sync
366 + 'static,
367 >,
368 >,
369 },
370 Registered {
371 type_: Type,
372 signal_id: SignalId,
373 },
374}
375
376impl SignalBuilder {
377 pub fn param_types(
380 mut self,
381 param_types: impl IntoIterator<Item = impl Into<SignalType>>,
382 ) -> Self {
383 self.param_types = param_types
384 .into_iter()
385 .map(|t| t.into())
386 .collect::<Vec<_>>();
387 self
388 }
389
390 pub fn return_type<T: StaticType>(mut self) -> Self {
393 self.return_type = T::static_type().into();
394 self
395 }
396
397 pub fn return_type_from(mut self, type_: impl Into<SignalType>) -> Self {
400 self.return_type = type_.into();
401 self
402 }
403
404 pub fn run_first(mut self) -> Self {
407 self.flags |= SignalFlags::RUN_FIRST;
408 self
409 }
410
411 pub fn run_last(mut self) -> Self {
414 self.flags |= SignalFlags::RUN_LAST;
415 self
416 }
417
418 pub fn run_cleanup(mut self) -> Self {
421 self.flags |= SignalFlags::RUN_CLEANUP;
422 self
423 }
424
425 pub fn no_recurse(mut self) -> Self {
429 self.flags |= SignalFlags::NO_RECURSE;
430 self
431 }
432
433 pub fn detailed(mut self) -> Self {
437 self.flags |= SignalFlags::DETAILED;
438 self
439 }
440
441 pub fn action(mut self) -> Self {
444 self.flags |= SignalFlags::ACTION;
445 self
446 }
447
448 pub fn no_hooks(mut self) -> Self {
451 self.flags |= SignalFlags::NO_HOOKS;
452 self
453 }
454
455 pub fn must_collect(mut self) -> Self {
459 self.flags |= SignalFlags::MUST_COLLECT;
460 self
461 }
462
463 pub fn deprecated(mut self) -> Self {
466 self.flags |= SignalFlags::DEPRECATED;
467 self
468 }
469
470 pub fn flags(mut self, flags: SignalFlags) -> Self {
475 self.flags = flags;
476 self
477 }
478
479 pub fn class_handler<F: Fn(&[Value]) -> Option<Value> + Send + Sync + 'static>(
482 mut self,
483 func: F,
484 ) -> Self {
485 self.class_handler = Some(Box::new(func));
486 self
487 }
488
489 pub fn accumulator<
495 F: Fn(&SignalInvocationHint, Value, &Value) -> ControlFlow<Value, Value>
496 + Send
497 + Sync
498 + 'static,
499 >(
500 mut self,
501 func: F,
502 ) -> Self {
503 self.accumulator = Some(Box::new(func));
504 self
505 }
506
507 #[must_use = "Signal returned from the builder must be used for it to be registered"]
513 pub fn build(self) -> Signal {
514 let flags = if self.flags
515 & (SignalFlags::RUN_FIRST | SignalFlags::RUN_LAST | SignalFlags::RUN_CLEANUP)
516 == SignalFlags::empty()
517 {
518 self.flags | SignalFlags::RUN_LAST
519 } else {
520 self.flags
521 };
522
523 Signal {
524 name: self.name,
525 flags,
526 param_types: self.param_types.to_vec(),
527 return_type: self.return_type,
528 registration: Mutex::new(SignalRegistration::Unregistered {
529 class_handler: self.class_handler,
530 accumulator: self.accumulator,
531 }),
532 }
533 }
534}
535
536impl Signal {
537 pub fn builder(name: &str) -> SignalBuilder {
540 assert!(
541 is_canonical_pspec_name(name),
542 "{name} is not a valid canonical signal name",
543 );
544 SignalBuilder {
545 name: name.to_owned(),
546 param_types: Vec::default(),
547 return_type: <()>::static_type().into(),
548 flags: SignalFlags::empty(),
549 class_handler: None,
550 accumulator: None,
551 }
552 }
553
554 #[inline]
557 pub fn name(&self) -> &str {
558 &self.name
559 }
560
561 #[inline]
564 pub fn flags(&self) -> SignalFlags {
565 self.flags
566 }
567
568 #[inline]
571 pub fn param_types(&self) -> &[SignalType] {
572 &self.param_types
573 }
574
575 #[inline]
578 pub fn return_type(&self) -> SignalType {
579 self.return_type
580 }
581
582 #[inline]
587 pub fn signal_id(&self) -> SignalId {
588 match &*self.registration.lock().unwrap() {
589 SignalRegistration::Unregistered { .. } => panic!("Signal not registered yet"),
590 SignalRegistration::Registered { signal_id, .. } => *signal_id,
591 }
592 }
593
594 #[inline]
599 pub fn type_(&self) -> Type {
600 match &*self.registration.lock().unwrap() {
601 SignalRegistration::Unregistered { .. } => panic!("Signal not registered yet"),
602 SignalRegistration::Registered { type_, .. } => *type_,
603 }
604 }
605
606 pub(super) fn register(&self, type_: Type) {
607 let mut registration = self.registration.lock().unwrap();
608
609 let (class_handler, accumulator) = match &mut *registration {
610 SignalRegistration::Unregistered {
611 class_handler,
612 accumulator,
613 } => (class_handler.take(), accumulator.take()),
614 SignalRegistration::Registered { .. } => unreachable!(),
615 };
616
617 let return_type = self.return_type;
618
619 let class_handler = class_handler.map(|class_handler| {
620 Closure::new(move |values| {
621 let res = class_handler(values);
622
623 if return_type == Type::UNIT {
624 if let Some(ref v) = res {
625 panic!("Signal has no return value but class handler returned a value of type {}", v.type_());
626 }
627 } else {
628 match res {
629 None => {
630 panic!("Signal has a return value but class handler returned none");
631 }
632 Some(ref v) => {
633 assert!(v.type_().is_a(return_type.into()), "Signal has a return type of {} but class handler returned {}", Type::from(return_type), v.type_());
634 }
635 }
636 }
637
638 res
639 })
640 });
641
642 unsafe extern "C" fn accumulator_trampoline(
643 ihint: *mut gobject_ffi::GSignalInvocationHint,
644 return_accu: *mut gobject_ffi::GValue,
645 handler_return: *const gobject_ffi::GValue,
646 data: ffi::gpointer,
647 ) -> ffi::gboolean {
648 unsafe {
649 let accumulator = &*(data as *const (
650 SignalType,
651 Box<
652 dyn Fn(&SignalInvocationHint, Value, &Value) -> ControlFlow<Value, Value>
653 + Send
654 + Sync
655 + 'static,
656 >,
657 ));
658
659 let return_accu = &mut *(return_accu as *mut Value);
660 let handler_return = &*(handler_return as *const Value);
661 let return_type = accumulator.0;
662
663 assert!(
664 handler_return.type_().is_a(return_type.into()),
665 "Signal has a return type of {} but handler returned {}",
666 Type::from(return_type),
667 handler_return.type_()
668 );
669
670 let control_flow = (accumulator.1)(
671 &SignalInvocationHint(*ihint),
672 std::mem::replace(return_accu, Value::uninitialized()),
673 handler_return,
674 );
675
676 let res = match control_flow {
677 ControlFlow::Continue(val) => {
678 *return_accu = val;
679 true.into_glib()
680 }
681
682 ControlFlow::Break(val) => {
683 *return_accu = val;
684 false.into_glib()
685 }
686 };
687
688 assert!(
689 return_accu.type_().is_a(return_type.into()),
690 "Signal has a return type of {} but accumulator returned {}",
691 Type::from(return_type),
692 return_accu.type_()
693 );
694
695 res
696 }
697 }
698
699 let (accumulator, accumulator_trampoline) = match (accumulator, return_type != Type::UNIT) {
700 (Some(accumulator), true) => (
701 Box::into_raw(Box::new((return_type, accumulator))),
702 Some::<unsafe extern "C" fn(_, _, _, _) -> _>(accumulator_trampoline),
703 ),
704 _ => (ptr::null_mut(), None),
705 };
706
707 unsafe {
708 let signal_id = gobject_ffi::g_signal_newv(
709 self.name.to_glib_none().0,
710 type_.into_glib(),
711 self.flags.into_glib(),
712 class_handler.to_glib_none().0,
713 accumulator_trampoline,
714 accumulator as ffi::gpointer,
715 None,
716 return_type.into_glib(),
717 self.param_types.len() as u32,
718 self.param_types.as_ptr() as *mut _,
719 );
720 *registration = SignalRegistration::Registered {
721 type_,
722 signal_id: SignalId::from_glib(signal_id),
723 };
724 }
725 }
726}