1use std::{fmt, num::NonZeroU32, ops::ControlFlow, 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<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 debug_assert_ne!(signal_id, 0);
268 Self::new(NonZeroU32::new_unchecked(signal_id))
269 }
270}
271
272#[doc(hidden)]
273impl IntoGlib for SignalId {
274 type GlibType = u32;
275
276 #[inline]
277 fn into_glib(self) -> u32 {
278 self.0.into()
279 }
280}
281
282#[derive(Copy, Clone, Hash)]
283#[repr(transparent)]
284pub struct SignalType(ffi::GType);
285
286impl SignalType {
287 #[inline]
288 pub fn with_static_scope(type_: Type) -> Self {
289 Self(type_.into_glib() | gobject_ffi::G_TYPE_FLAG_RESERVED_ID_BIT)
290 }
291
292 #[inline]
293 pub fn static_scope(&self) -> bool {
294 (self.0 & gobject_ffi::G_TYPE_FLAG_RESERVED_ID_BIT) != 0
295 }
296
297 #[inline]
298 pub fn type_(&self) -> Type {
299 (*self).into()
300 }
301}
302
303impl From<Type> for SignalType {
304 #[inline]
305 fn from(type_: Type) -> Self {
306 Self(type_.into_glib())
307 }
308}
309
310impl From<SignalType> for Type {
311 #[inline]
312 fn from(type_: SignalType) -> Self {
313 let type_ = type_.0 & (!gobject_ffi::G_TYPE_FLAG_RESERVED_ID_BIT);
315 unsafe { from_glib(type_) }
316 }
317}
318
319impl PartialEq<Type> for SignalType {
320 #[inline]
321 fn eq(&self, other: &Type) -> bool {
322 let type_: Type = (*self).into();
323 type_.eq(other)
324 }
325}
326
327impl std::fmt::Debug for SignalType {
328 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
329 let type_: Type = (*self).into();
330 f.debug_struct("SignalType")
331 .field("name", &type_.name())
332 .field("static_scope", &self.static_scope())
333 .finish()
334 }
335}
336
337#[doc(hidden)]
338impl FromGlib<ffi::GType> for SignalType {
339 #[inline]
340 unsafe fn from_glib(type_: ffi::GType) -> Self {
341 Self(type_)
342 }
343}
344
345#[doc(hidden)]
346impl IntoGlib for SignalType {
347 type GlibType = ffi::GType;
348
349 #[inline]
350 fn into_glib(self) -> ffi::GType {
351 self.0
352 }
353}
354
355#[allow(clippy::type_complexity)]
356enum SignalRegistration {
357 Unregistered {
358 class_handler: Option<Box<dyn Fn(&[Value]) -> Option<Value> + Send + Sync + 'static>>,
359 accumulator: Option<
360 Box<
361 dyn Fn(&SignalInvocationHint, Value, &Value) -> ControlFlow<Value, Value>
362 + Send
363 + Sync
364 + 'static,
365 >,
366 >,
367 },
368 Registered {
369 type_: Type,
370 signal_id: SignalId,
371 },
372}
373
374impl SignalBuilder {
375 pub fn param_types(
378 mut self,
379 param_types: impl IntoIterator<Item = impl Into<SignalType>>,
380 ) -> Self {
381 self.param_types = param_types
382 .into_iter()
383 .map(|t| t.into())
384 .collect::<Vec<_>>();
385 self
386 }
387
388 pub fn return_type<T: StaticType>(mut self) -> Self {
391 self.return_type = T::static_type().into();
392 self
393 }
394
395 pub fn return_type_from(mut self, type_: impl Into<SignalType>) -> Self {
398 self.return_type = type_.into();
399 self
400 }
401
402 pub fn run_first(mut self) -> Self {
405 self.flags |= SignalFlags::RUN_FIRST;
406 self
407 }
408
409 pub fn run_last(mut self) -> Self {
412 self.flags |= SignalFlags::RUN_LAST;
413 self
414 }
415
416 pub fn run_cleanup(mut self) -> Self {
419 self.flags |= SignalFlags::RUN_CLEANUP;
420 self
421 }
422
423 pub fn no_recurse(mut self) -> Self {
427 self.flags |= SignalFlags::NO_RECURSE;
428 self
429 }
430
431 pub fn detailed(mut self) -> Self {
435 self.flags |= SignalFlags::DETAILED;
436 self
437 }
438
439 pub fn action(mut self) -> Self {
442 self.flags |= SignalFlags::ACTION;
443 self
444 }
445
446 pub fn no_hooks(mut self) -> Self {
449 self.flags |= SignalFlags::NO_HOOKS;
450 self
451 }
452
453 pub fn must_collect(mut self) -> Self {
457 self.flags |= SignalFlags::MUST_COLLECT;
458 self
459 }
460
461 pub fn deprecated(mut self) -> Self {
464 self.flags |= SignalFlags::DEPRECATED;
465 self
466 }
467
468 pub fn flags(mut self, flags: SignalFlags) -> Self {
473 self.flags = flags;
474 self
475 }
476
477 pub fn class_handler<F: Fn(&[Value]) -> Option<Value> + Send + Sync + 'static>(
480 mut self,
481 func: F,
482 ) -> Self {
483 self.class_handler = Some(Box::new(func));
484 self
485 }
486
487 pub fn accumulator<
493 F: Fn(&SignalInvocationHint, Value, &Value) -> ControlFlow<Value, Value>
494 + Send
495 + Sync
496 + 'static,
497 >(
498 mut self,
499 func: F,
500 ) -> Self {
501 self.accumulator = Some(Box::new(func));
502 self
503 }
504
505 #[must_use = "Signal returned from the builder must be used for it to be registered"]
511 pub fn build(self) -> Signal {
512 let flags = if self.flags
513 & (SignalFlags::RUN_FIRST | SignalFlags::RUN_LAST | SignalFlags::RUN_CLEANUP)
514 == SignalFlags::empty()
515 {
516 self.flags | SignalFlags::RUN_LAST
517 } else {
518 self.flags
519 };
520
521 Signal {
522 name: self.name,
523 flags,
524 param_types: self.param_types.to_vec(),
525 return_type: self.return_type,
526 registration: Mutex::new(SignalRegistration::Unregistered {
527 class_handler: self.class_handler,
528 accumulator: self.accumulator,
529 }),
530 }
531 }
532}
533
534impl Signal {
535 pub fn builder(name: &str) -> SignalBuilder {
538 assert!(
539 is_canonical_pspec_name(name),
540 "{name} is not a valid canonical signal name",
541 );
542 SignalBuilder {
543 name: name.to_owned(),
544 param_types: Vec::default(),
545 return_type: <()>::static_type().into(),
546 flags: SignalFlags::empty(),
547 class_handler: None,
548 accumulator: None,
549 }
550 }
551
552 #[inline]
555 pub fn name(&self) -> &str {
556 &self.name
557 }
558
559 #[inline]
562 pub fn flags(&self) -> SignalFlags {
563 self.flags
564 }
565
566 #[inline]
569 pub fn param_types(&self) -> &[SignalType] {
570 &self.param_types
571 }
572
573 #[inline]
576 pub fn return_type(&self) -> SignalType {
577 self.return_type
578 }
579
580 #[inline]
585 pub fn signal_id(&self) -> SignalId {
586 match &*self.registration.lock().unwrap() {
587 SignalRegistration::Unregistered { .. } => panic!("Signal not registered yet"),
588 SignalRegistration::Registered { signal_id, .. } => *signal_id,
589 }
590 }
591
592 #[inline]
597 pub fn type_(&self) -> Type {
598 match &*self.registration.lock().unwrap() {
599 SignalRegistration::Unregistered { .. } => panic!("Signal not registered yet"),
600 SignalRegistration::Registered { type_, .. } => *type_,
601 }
602 }
603
604 pub(super) fn register(&self, type_: Type) {
605 let mut registration = self.registration.lock().unwrap();
606
607 let (class_handler, accumulator) = match &mut *registration {
608 SignalRegistration::Unregistered {
609 class_handler,
610 accumulator,
611 } => (class_handler.take(), accumulator.take()),
612 SignalRegistration::Registered { .. } => unreachable!(),
613 };
614
615 let return_type = self.return_type;
616
617 let class_handler = class_handler.map(|class_handler| {
618 Closure::new(move |values| {
619 let res = class_handler(values);
620
621 if return_type == Type::UNIT {
622 if let Some(ref v) = res {
623 panic!("Signal has no return value but class handler returned a value of type {}", v.type_());
624 }
625 } else {
626 match res {
627 None => {
628 panic!("Signal has a return value but class handler returned none");
629 }
630 Some(ref v) => {
631 assert!(v.type_().is_a(return_type.into()), "Signal has a return type of {} but class handler returned {}", Type::from(return_type), v.type_());
632 }
633 }
634 }
635
636 res
637 })
638 });
639
640 unsafe extern "C" fn accumulator_trampoline(
641 ihint: *mut gobject_ffi::GSignalInvocationHint,
642 return_accu: *mut gobject_ffi::GValue,
643 handler_return: *const gobject_ffi::GValue,
644 data: ffi::gpointer,
645 ) -> ffi::gboolean {
646 let accumulator = &*(data as *const (
647 SignalType,
648 Box<
649 dyn Fn(&SignalInvocationHint, Value, &Value) -> ControlFlow<Value, Value>
650 + Send
651 + Sync
652 + 'static,
653 >,
654 ));
655
656 let return_accu = &mut *(return_accu as *mut Value);
657 let handler_return = &*(handler_return as *const Value);
658 let return_type = accumulator.0;
659
660 assert!(
661 handler_return.type_().is_a(return_type.into()),
662 "Signal has a return type of {} but handler returned {}",
663 Type::from(return_type),
664 handler_return.type_()
665 );
666
667 let control_flow = (accumulator.1)(
668 &SignalInvocationHint(*ihint),
669 std::mem::replace(return_accu, Value::uninitialized()),
670 handler_return,
671 );
672
673 let res = match control_flow {
674 ControlFlow::Continue(val) => {
675 *return_accu = val;
676 true.into_glib()
677 }
678
679 ControlFlow::Break(val) => {
680 *return_accu = val;
681 false.into_glib()
682 }
683 };
684
685 assert!(
686 return_accu.type_().is_a(return_type.into()),
687 "Signal has a return type of {} but accumulator returned {}",
688 Type::from(return_type),
689 return_accu.type_()
690 );
691
692 res
693 }
694
695 let (accumulator, accumulator_trampoline) =
696 if let (Some(accumulator), true) = (accumulator, return_type != Type::UNIT) {
697 (
698 Box::into_raw(Box::new((return_type, accumulator))),
699 Some::<unsafe extern "C" fn(_, _, _, _) -> _>(accumulator_trampoline),
700 )
701 } else {
702 (ptr::null_mut(), None)
703 };
704
705 unsafe {
706 let signal_id = gobject_ffi::g_signal_newv(
707 self.name.to_glib_none().0,
708 type_.into_glib(),
709 self.flags.into_glib(),
710 class_handler.to_glib_none().0,
711 accumulator_trampoline,
712 accumulator as ffi::gpointer,
713 None,
714 return_type.into_glib(),
715 self.param_types.len() as u32,
716 self.param_types.as_ptr() as *mut _,
717 );
718 *registration = SignalRegistration::Registered {
719 type_,
720 signal_id: SignalId::from_glib(signal_id),
721 };
722 }
723 }
724}