glib/subclass/
signal.rs

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    ffi, gobject_ffi, prelude::*, translate::*, utils::is_canonical_pspec_name, Closure,
7    SignalFlags, Type, 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.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// 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.debug_struct("SignalInvocationHint")
81            .field("detail", &self.detail())
82            .field("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.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// 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(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_ = 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    // rustdoc-stripper-ignore-next
372    /// The signal's parameters.
373    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    // rustdoc-stripper-ignore-next
385    /// The signal's returned value type.
386    pub fn return_type<T: StaticType>(mut self) -> Self {
387        self.return_type = T::static_type().into();
388        self
389    }
390
391    // rustdoc-stripper-ignore-next
392    /// The signal's returned value type.
393    pub fn return_type_from(mut self, type_: impl Into<SignalType>) -> Self {
394        self.return_type = type_.into();
395        self
396    }
397
398    // rustdoc-stripper-ignore-next
399    /// Run the signal class handler in the first emission stage.
400    pub fn run_first(mut self) -> Self {
401        self.flags |= SignalFlags::RUN_FIRST;
402        self
403    }
404
405    // rustdoc-stripper-ignore-next
406    /// Run the signal class handler in the third emission stage.
407    pub fn run_last(mut self) -> Self {
408        self.flags |= SignalFlags::RUN_LAST;
409        self
410    }
411
412    // rustdoc-stripper-ignore-next
413    /// Run the signal class handler in the last emission stage.
414    pub fn run_cleanup(mut self) -> Self {
415        self.flags |= SignalFlags::RUN_CLEANUP;
416        self
417    }
418
419    // rustdoc-stripper-ignore-next
420    /// Signals being emitted for an object while currently being in emission for this very object
421    /// will not be emitted recursively, but instead cause the first emission to be restarted.
422    pub fn no_recurse(mut self) -> Self {
423        self.flags |= SignalFlags::NO_RECURSE;
424        self
425    }
426
427    // rustdoc-stripper-ignore-next
428    /// This signal supports "::detail" appendices to the signal name upon handler connections and
429    /// emissions.
430    pub fn detailed(mut self) -> Self {
431        self.flags |= SignalFlags::DETAILED;
432        self
433    }
434
435    // rustdoc-stripper-ignore-next
436    /// Action signals are signals that may freely be emitted on alive objects from user code.
437    pub fn action(mut self) -> Self {
438        self.flags |= SignalFlags::ACTION;
439        self
440    }
441
442    // rustdoc-stripper-ignore-next
443    /// No emissions hooks are supported for this signal.
444    pub fn no_hooks(mut self) -> Self {
445        self.flags |= SignalFlags::NO_HOOKS;
446        self
447    }
448
449    // rustdoc-stripper-ignore-next
450    /// Varargs signal emission will always collect the arguments, even if there are no signal
451    /// handlers connected.
452    pub fn must_collect(mut self) -> Self {
453        self.flags |= SignalFlags::MUST_COLLECT;
454        self
455    }
456
457    // rustdoc-stripper-ignore-next
458    /// The signal is deprecated and will be removed in a future version.
459    pub fn deprecated(mut self) -> Self {
460        self.flags |= SignalFlags::DEPRECATED;
461        self
462    }
463
464    // rustdoc-stripper-ignore-next
465    /// Explicitly set all flags.
466    ///
467    /// This overrides previously set flags on this builder.
468    pub fn flags(mut self, flags: SignalFlags) -> Self {
469        self.flags = flags;
470        self
471    }
472
473    // rustdoc-stripper-ignore-next
474    /// Class handler for this signal.
475    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    // rustdoc-stripper-ignore-next
486    /// Accumulator for the return values of the signal.
487    ///
488    /// This is called if multiple signal handlers are connected to the signal for accumulating the
489    /// return values into a single value.
490    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    // rustdoc-stripper-ignore-next
501    /// Build the signal.
502    ///
503    /// This does not register the signal yet, which only happens as part of object type
504    /// registration.
505    #[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    // rustdoc-stripper-ignore-next
531    /// Create a new builder for a signal.
532    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    // rustdoc-stripper-ignore-next
548    /// Name of the signal.
549    #[inline]
550    pub fn name(&self) -> &str {
551        &self.name
552    }
553
554    // rustdoc-stripper-ignore-next
555    /// Flags of the signal.
556    #[inline]
557    pub fn flags(&self) -> SignalFlags {
558        self.flags
559    }
560
561    // rustdoc-stripper-ignore-next
562    /// Parameter types of the signal.
563    #[inline]
564    pub fn param_types(&self) -> &[SignalType] {
565        &self.param_types
566    }
567
568    // rustdoc-stripper-ignore-next
569    /// Return type of the signal.
570    #[inline]
571    pub fn return_type(&self) -> SignalType {
572        self.return_type
573    }
574
575    // rustdoc-stripper-ignore-next
576    /// Signal ID.
577    ///
578    /// This will panic if called before the signal was registered.
579    #[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    // rustdoc-stripper-ignore-next
588    /// Type this signal was registered for.
589    ///
590    /// This will panic if called before the signal was registered.
591    #[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}