1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use std::{fmt, num::NonZeroU32, ptr, sync::Mutex};
4
5use crate::{
6 prelude::*, translate::*, utils::is_canonical_pspec_name, Closure, IntoGStr, SignalFlags, Type,
7 Value,
8};
9
10// rustdoc-stripper-ignore-next
11/// Builder for signals.
12#[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
27// rustdoc-stripper-ignore-next
28/// Signal metadata.
29pub struct Signal {
30 name: String,
31 flags: SignalFlags,
32 param_types: Vec<SignalType>,
33 return_type: SignalType,
34 registration: Mutex<SignalRegistration>,
35}
36
37// rustdoc-stripper-ignore-next
38/// Token passed to signal class handlers.
39pub struct SignalClassHandlerToken(
40 // rustdoc-stripper-ignore-next
41 /// Instance for which the signal is emitted.
42 pub(super) *mut gobject_ffi::GTypeInstance,
43 // rustdoc-stripper-ignore-next
44 /// Return type.
45 pub(super) Type,
46 // rustdoc-stripper-ignore-next
47 /// Arguments value array.
48 pub(super) *const Value,
49);
50
51impl fmt::Debug for SignalClassHandlerToken {
52 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
53 f&mut DebugStruct<'_, '_>.debug_struct("SignalClassHandlerToken")
54 .field(name:"type", &unsafe {
55 crate::Object::from_glib_borrow(self.0 as *mut gobject_ffi::GObject)
56 })
57 .finish()
58 }
59}
60
61// rustdoc-stripper-ignore-next
62/// Signal invocation hint passed to signal accumulators.
63#[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&mut DebugStruct<'_, '_>.debug_struct("SignalInvocationHint")
81 .field("detail", &self.detail())
82 .field(name:"run_type", &self.run_type())
83 .finish()
84 }
85}
86
87// rustdoc-stripper-ignore-next
88/// In-depth information of a specific signal
89pub struct SignalQuery(gobject_ffi::GSignalQuery);
90
91unsafe impl Send for SignalQuery {}
92unsafe impl Sync for SignalQuery {}
93
94impl SignalQuery {
95 // rustdoc-stripper-ignore-next
96 /// The name of the signal.
97 #[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 // rustdoc-stripper-ignore-next
106 /// The ID of the signal.
107 #[inline]
108 pub fn signal_id(&self) -> SignalId {
109 unsafe { SignalId::from_glib(self.0.signal_id) }
110 }
111
112 // rustdoc-stripper-ignore-next
113 /// The instance type this signal can be emitted for.
114 #[inline]
115 pub fn type_(&self) -> Type {
116 unsafe { from_glib(self.0.itype) }
117 }
118
119 // rustdoc-stripper-ignore-next
120 /// The signal flags.
121 #[inline]
122 pub fn flags(&self) -> SignalFlags {
123 unsafe { from_glib(self.0.signal_flags) }
124 }
125
126 // rustdoc-stripper-ignore-next
127 /// The return type for the user callback.
128 #[inline]
129 pub fn return_type(&self) -> SignalType {
130 unsafe { from_glib(self.0.return_type) }
131 }
132
133 // rustdoc-stripper-ignore-next
134 /// The number of parameters the user callback takes.
135 #[inline]
136 pub fn n_params(&self) -> u32 {
137 self.0.n_params
138 }
139
140 // rustdoc-stripper-ignore-next
141 /// The parameters for the user callback.
142 #[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&mut DebugStruct<'_, '_>.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(name:"param_types", &self.param_types())
165 .finish()
166 }
167}
168
169// rustdoc-stripper-ignore-next
170/// Signal ID.
171#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
172pub struct SignalId(NonZeroU32);
173
174impl SignalId {
175 // rustdoc-stripper-ignore-next
176 /// Create a new Signal Identifier.
177 ///
178 /// # Safety
179 ///
180 /// The caller has to ensure it's a valid signal identifier.
181 #[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 // rustdoc-stripper-ignore-next
218 /// Find a SignalId by its `name`, and the `type` it connects to.
219 #[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 // rustdoc-stripper-ignore-next
235 /// Queries more in-depth information about the current signal.
236 #[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 // rustdoc-stripper-ignore-next
249 /// Find the signal name.
250 #[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(id: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 // Remove the extra-bit used for G_SIGNAL_TYPE_STATIC_SCOPE
311 let type_: usize = type_.0 & (!gobject_ffi::G_TYPE_FLAG_RESERVED_ID_BIT);
312 unsafe { from_glib(val: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&mut DebugStruct<'_, '_>.debug_struct("SignalType")
328 .field("name", &type_.name())
329 .field(name:"static_scope", &self.static_scope())
330 .finish()
331 }
332}
333
334impl std::fmt::Display for SignalType {
335 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
336 let type_: Type = (*self).into();
337 f&mut DebugStruct<'_, '_>.debug_struct("SignalType")
338 .field("name", &type_.name())
339 .field(name:"static_scope", &self.static_scope())
340 .finish()
341 }
342}
343
344#[doc(hidden)]
345impl FromGlib<ffi::GType> for SignalType {
346 #[inline]
347 unsafe fn from_glib(type_: ffi::GType) -> Self {
348 Self(type_)
349 }
350}
351
352#[doc(hidden)]
353impl IntoGlib for SignalType {
354 type GlibType = ffi::GType;
355
356 #[inline]
357 fn into_glib(self) -> ffi::GType {
358 self.0
359 }
360}
361
362#[allow(clippy::type_complexity)]
363enum SignalRegistration {
364 Unregistered {
365 class_handler: Option<
366 Box<
367 dyn Fn(&SignalClassHandlerToken, &[Value]) -> Option<Value> + Send + Sync + 'static,
368 >,
369 >,
370 accumulator: Option<
371 Box<dyn Fn(&SignalInvocationHint, &mut Value, &Value) -> bool + Send + Sync + 'static>,
372 >,
373 },
374 Registered {
375 type_: Type,
376 signal_id: SignalId,
377 },
378}
379
380impl SignalBuilder {
381 // rustdoc-stripper-ignore-next
382 /// The signal's parameters.
383 pub fn param_types(
384 mut self,
385 param_types: impl IntoIterator<Item = impl Into<SignalType>>,
386 ) -> Self {
387 self.param_types = param_types
388 .into_iter()
389 .map(|t| t.into())
390 .collect::<Vec<_>>();
391 self
392 }
393
394 // rustdoc-stripper-ignore-next
395 /// The signal's returned value type.
396 pub fn return_type<T: StaticType>(mut self) -> Self {
397 self.return_type = T::static_type().into();
398 self
399 }
400
401 // rustdoc-stripper-ignore-next
402 /// The signal's returned value type.
403 pub fn return_type_from(mut self, type_: impl Into<SignalType>) -> Self {
404 self.return_type = type_.into();
405 self
406 }
407
408 // rustdoc-stripper-ignore-next
409 /// Run the signal class handler in the first emission stage.
410 pub fn run_first(mut self) -> Self {
411 self.flags |= SignalFlags::RUN_FIRST;
412 self
413 }
414
415 // rustdoc-stripper-ignore-next
416 /// Run the signal class handler in the third emission stage.
417 pub fn run_last(mut self) -> Self {
418 self.flags |= SignalFlags::RUN_LAST;
419 self
420 }
421
422 // rustdoc-stripper-ignore-next
423 /// Run the signal class handler in the last emission stage.
424 pub fn run_cleanup(mut self) -> Self {
425 self.flags |= SignalFlags::RUN_CLEANUP;
426 self
427 }
428
429 // rustdoc-stripper-ignore-next
430 /// Signals being emitted for an object while currently being in emission for this very object
431 /// will not be emitted recursively, but instead cause the first emission to be restarted.
432 pub fn no_recurse(mut self) -> Self {
433 self.flags |= SignalFlags::NO_RECURSE;
434 self
435 }
436
437 // rustdoc-stripper-ignore-next
438 /// This signal supports "::detail" appendices to the signal name upon handler connections and
439 /// emissions.
440 pub fn detailed(mut self) -> Self {
441 self.flags |= SignalFlags::DETAILED;
442 self
443 }
444
445 // rustdoc-stripper-ignore-next
446 /// Action signals are signals that may freely be emitted on alive objects from user code.
447 pub fn action(mut self) -> Self {
448 self.flags |= SignalFlags::ACTION;
449 self
450 }
451
452 // rustdoc-stripper-ignore-next
453 /// No emissions hooks are supported for this signal.
454 pub fn no_hooks(mut self) -> Self {
455 self.flags |= SignalFlags::NO_HOOKS;
456 self
457 }
458
459 // rustdoc-stripper-ignore-next
460 /// Varargs signal emission will always collect the arguments, even if there are no signal
461 /// handlers connected.
462 pub fn must_collect(mut self) -> Self {
463 self.flags |= SignalFlags::MUST_COLLECT;
464 self
465 }
466
467 // rustdoc-stripper-ignore-next
468 /// The signal is deprecated and will be removed in a future version.
469 pub fn deprecated(mut self) -> Self {
470 self.flags |= SignalFlags::DEPRECATED;
471 self
472 }
473
474 // rustdoc-stripper-ignore-next
475 /// Explicitly set all flags.
476 ///
477 /// This overrides previously set flags on this builder.
478 pub fn flags(mut self, flags: SignalFlags) -> Self {
479 self.flags = flags;
480 self
481 }
482
483 // rustdoc-stripper-ignore-next
484 /// Class handler for this signal.
485 pub fn class_handler<
486 F: Fn(&SignalClassHandlerToken, &[Value]) -> Option<Value> + Send + Sync + 'static,
487 >(
488 mut self,
489 func: F,
490 ) -> Self {
491 self.class_handler = Some(Box::new(func));
492 self
493 }
494
495 // rustdoc-stripper-ignore-next
496 /// Accumulator for the return values of the signal.
497 ///
498 /// This is called if multiple signal handlers are connected to the signal for accumulating the
499 /// return values into a single value.
500 pub fn accumulator<
501 F: Fn(&SignalInvocationHint, &mut Value, &Value) -> bool + Send + Sync + 'static,
502 >(
503 mut self,
504 func: F,
505 ) -> Self {
506 self.accumulator = Some(Box::new(func));
507 self
508 }
509
510 // rustdoc-stripper-ignore-next
511 /// Build the signal.
512 ///
513 /// This does not register the signal yet, which only happens as part of object type
514 /// registration.
515 #[must_use = "Signal returned from the builder must be used for it to be registered"]
516 pub fn build(self) -> Signal {
517 let flags = if self.flags
518 & (SignalFlags::RUN_FIRST | SignalFlags::RUN_LAST | SignalFlags::RUN_CLEANUP)
519 == SignalFlags::empty()
520 {
521 self.flags | SignalFlags::RUN_LAST
522 } else {
523 self.flags
524 };
525
526 Signal {
527 name: self.name,
528 flags,
529 param_types: self.param_types.to_vec(),
530 return_type: self.return_type,
531 registration: Mutex::new(SignalRegistration::Unregistered {
532 class_handler: self.class_handler,
533 accumulator: self.accumulator,
534 }),
535 }
536 }
537}
538
539impl Signal {
540 // rustdoc-stripper-ignore-next
541 /// Create a new builder for a signal.
542 pub fn builder(name: &str) -> SignalBuilder {
543 assert!(
544 is_canonical_pspec_name(name),
545 "{name} is not a valid canonical signal name",
546 );
547 SignalBuilder {
548 name: name.to_owned(),
549 param_types: Vec::default(),
550 return_type: <()>::static_type().into(),
551 flags: SignalFlags::empty(),
552 class_handler: None,
553 accumulator: None,
554 }
555 }
556
557 // rustdoc-stripper-ignore-next
558 /// Name of the signal.
559 #[inline]
560 pub fn name(&self) -> &str {
561 &self.name
562 }
563
564 // rustdoc-stripper-ignore-next
565 /// Flags of the signal.
566 #[inline]
567 pub fn flags(&self) -> SignalFlags {
568 self.flags
569 }
570
571 // rustdoc-stripper-ignore-next
572 /// Parameter types of the signal.
573 #[inline]
574 pub fn param_types(&self) -> &[SignalType] {
575 &self.param_types
576 }
577
578 // rustdoc-stripper-ignore-next
579 /// Return type of the signal.
580 #[inline]
581 pub fn return_type(&self) -> SignalType {
582 self.return_type
583 }
584
585 // rustdoc-stripper-ignore-next
586 /// Signal ID.
587 ///
588 /// This will panic if called before the signal was registered.
589 #[inline]
590 pub fn signal_id(&self) -> SignalId {
591 match &*self.registration.lock().unwrap() {
592 SignalRegistration::Unregistered { .. } => panic!("Signal not registered yet"),
593 SignalRegistration::Registered { signal_id, .. } => *signal_id,
594 }
595 }
596
597 // rustdoc-stripper-ignore-next
598 /// Type this signal was registered for.
599 ///
600 /// This will panic if called before the signal was registered.
601 #[inline]
602 pub fn type_(&self) -> Type {
603 match &*self.registration.lock().unwrap() {
604 SignalRegistration::Unregistered { .. } => panic!("Signal not registered yet"),
605 SignalRegistration::Registered { type_, .. } => *type_,
606 }
607 }
608
609 pub(super) fn register(&self, type_: Type) {
610 let mut registration = self.registration.lock().unwrap();
611
612 let (class_handler, accumulator) = match &mut *registration {
613 SignalRegistration::Unregistered {
614 class_handler,
615 accumulator,
616 } => (class_handler.take(), accumulator.take()),
617 SignalRegistration::Registered { .. } => unreachable!(),
618 };
619
620 let return_type = self.return_type;
621
622 let class_handler = class_handler.map(|class_handler| {
623 Closure::new(move |values| unsafe {
624 let instance = gobject_ffi::g_value_get_object(values[0].to_glib_none().0);
625 let res = class_handler(&SignalClassHandlerToken(instance as *mut _, return_type.into(), values.as_ptr()), values);
626
627 if return_type == Type::UNIT {
628 if let Some(ref v) = res {
629 panic!("Signal has no return value but class handler returned a value of type {}", v.type_());
630 }
631 } else {
632 match res {
633 None => {
634 panic!("Signal has a return value but class handler returned none");
635 }
636 Some(ref v) => {
637 assert!(v.type_().is_a(return_type.into()), "Signal has a return type of {} but class handler returned {}", Type::from(return_type), v.type_());
638 }
639 }
640 }
641
642 res
643 })
644 });
645
646 unsafe extern "C" fn accumulator_trampoline(
647 ihint: *mut gobject_ffi::GSignalInvocationHint,
648 return_accu: *mut gobject_ffi::GValue,
649 handler_return: *const gobject_ffi::GValue,
650 data: ffi::gpointer,
651 ) -> ffi::gboolean {
652 let accumulator = &*(data as *const (
653 SignalType,
654 Box<
655 dyn Fn(&SignalInvocationHint, &mut Value, &Value) -> bool
656 + Send
657 + Sync
658 + 'static,
659 >,
660 ));
661
662 let return_accu = &mut *(return_accu as *mut Value);
663 let handler_return = &*(handler_return as *const Value);
664 let return_type = accumulator.0;
665
666 assert!(
667 handler_return.type_().is_a(return_type.into()),
668 "Signal has a return type of {} but handler returned {}",
669 Type::from(return_type),
670 handler_return.type_()
671 );
672
673 let res = (accumulator.1)(&SignalInvocationHint(*ihint), return_accu, handler_return)
674 .into_glib();
675
676 assert!(
677 return_accu.type_().is_a(return_type.into()),
678 "Signal has a return type of {} but accumulator returned {}",
679 Type::from(return_type),
680 return_accu.type_()
681 );
682
683 res
684 }
685
686 let (accumulator, accumulator_trampoline) =
687 if let (Some(accumulator), true) = (accumulator, return_type != Type::UNIT) {
688 (
689 Box::into_raw(Box::new((return_type, accumulator))),
690 Some::<unsafe extern "C" fn(_, _, _, _) -> _>(accumulator_trampoline),
691 )
692 } else {
693 (ptr::null_mut(), None)
694 };
695
696 unsafe {
697 let signal_id = gobject_ffi::g_signal_newv(
698 self.name.to_glib_none().0,
699 type_.into_glib(),
700 self.flags.into_glib(),
701 class_handler.to_glib_none().0,
702 accumulator_trampoline,
703 accumulator as ffi::gpointer,
704 None,
705 return_type.into_glib(),
706 self.param_types.len() as u32,
707 self.param_types.as_ptr() as *mut _,
708 );
709 *registration = SignalRegistration::Registered {
710 type_,
711 signal_id: SignalId::from_glib(signal_id),
712 };
713 }
714 }
715}
716