1 // Take a look at the license at the top of the repository in the LICENSE file.
2 
3 //! `IMPL` Low level signal support.
4 
5 use crate::object::ObjectType;
6 use crate::translate::{from_glib, FromGlib, IntoGlib, ToGlibPtr};
7 use ffi::{gboolean, gpointer};
8 use gobject_ffi::{self, GCallback};
9 use libc::{c_char, c_ulong, c_void};
10 use std::mem;
11 use std::num::NonZeroU64;
12 
13 /// The id of a signal that is returned by `connect`.
14 ///
15 /// This type does not implement `Clone` to prevent disconnecting
16 /// the same signal handler multiple times.
17 ///
18 /// ```ignore
19 /// use glib::SignalHandlerId;
20 /// use gtk::prelude::*;
21 /// use std::cell::RefCell;
22 ///
23 /// struct Button {
24 ///     widget: gtk::Button,
25 ///     clicked_handler_id: RefCell<Option<SignalHandlerId>>,
26 /// }
27 ///
28 /// impl Button {
29 ///     fn new() -> Self {
30 ///         let widget = gtk::Button::new();
31 ///         let clicked_handler_id = RefCell::new(Some(widget.connect_clicked(|_button| {
32 ///             // Do something.
33 ///         })));
34 ///         Self {
35 ///             widget,
36 ///             clicked_handler_id,
37 ///         }
38 ///     }
39 ///
40 ///     fn disconnect(&self) {
41 ///         if let Some(id) = self.clicked_handler_id.borrow_mut().take() {
42 ///             self.widget.disconnect(id)
43 ///         }
44 ///     }
45 /// }
46 /// ```
47 #[derive(Debug, Eq, PartialEq)]
48 pub struct SignalHandlerId(NonZeroU64);
49 
50 impl SignalHandlerId {
51     /// Returns the internal signal handler ID.
as_raw(&self) -> libc::c_ulong52     pub unsafe fn as_raw(&self) -> libc::c_ulong {
53         self.0.get() as libc::c_ulong
54     }
55 }
56 
57 impl FromGlib<c_ulong> for SignalHandlerId {
58     #[inline]
from_glib(val: c_ulong) -> Self59     unsafe fn from_glib(val: c_ulong) -> Self {
60         assert_ne!(val, 0);
61         Self(NonZeroU64::new_unchecked(val as u64))
62     }
63 }
64 
65 /// Whether to propagate the signal to the default handler.
66 ///
67 /// Don't inhibit default handlers without a reason, they're usually helpful.
68 #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
69 pub struct Inhibit(pub bool);
70 
71 #[doc(hidden)]
72 impl IntoGlib for Inhibit {
73     type GlibType = gboolean;
74 
75     #[inline]
into_glib(self) -> gboolean76     fn into_glib(self) -> gboolean {
77         self.0.into_glib()
78     }
79 }
80 
connect_raw<F>( receiver: *mut gobject_ffi::GObject, signal_name: *const c_char, trampoline: GCallback, closure: *mut F, ) -> SignalHandlerId81 pub unsafe fn connect_raw<F>(
82     receiver: *mut gobject_ffi::GObject,
83     signal_name: *const c_char,
84     trampoline: GCallback,
85     closure: *mut F,
86 ) -> SignalHandlerId {
87     unsafe extern "C" fn destroy_closure<F>(ptr: *mut c_void, _: *mut gobject_ffi::GClosure) {
88         // destroy
89         Box::<F>::from_raw(ptr as *mut _);
90     }
91     assert_eq!(mem::size_of::<*mut F>(), mem::size_of::<gpointer>());
92     assert!(trampoline.is_some());
93     let handle = gobject_ffi::g_signal_connect_data(
94         receiver,
95         signal_name,
96         trampoline,
97         closure as *mut _,
98         Some(destroy_closure::<F>),
99         0,
100     );
101     assert!(handle > 0);
102     from_glib(handle)
103 }
104 
105 #[doc(alias = "g_signal_handler_block")]
signal_handler_block<T: ObjectType>(instance: &T, handler_id: &SignalHandlerId)106 pub fn signal_handler_block<T: ObjectType>(instance: &T, handler_id: &SignalHandlerId) {
107     unsafe {
108         gobject_ffi::g_signal_handler_block(
109             instance.as_object_ref().to_glib_none().0,
110             handler_id.as_raw(),
111         );
112     }
113 }
114 
115 #[doc(alias = "g_signal_handler_unblock")]
signal_handler_unblock<T: ObjectType>(instance: &T, handler_id: &SignalHandlerId)116 pub fn signal_handler_unblock<T: ObjectType>(instance: &T, handler_id: &SignalHandlerId) {
117     unsafe {
118         gobject_ffi::g_signal_handler_unblock(
119             instance.as_object_ref().to_glib_none().0,
120             handler_id.as_raw(),
121         );
122     }
123 }
124 
125 #[allow(clippy::needless_pass_by_value)]
126 #[doc(alias = "g_signal_handler_disconnect")]
signal_handler_disconnect<T: ObjectType>(instance: &T, handler_id: SignalHandlerId)127 pub fn signal_handler_disconnect<T: ObjectType>(instance: &T, handler_id: SignalHandlerId) {
128     unsafe {
129         gobject_ffi::g_signal_handler_disconnect(
130             instance.as_object_ref().to_glib_none().0,
131             handler_id.as_raw(),
132         );
133     }
134 }
135 
136 #[doc(alias = "g_signal_stop_emission_by_name")]
signal_stop_emission_by_name<T: ObjectType>(instance: &T, signal_name: &str)137 pub fn signal_stop_emission_by_name<T: ObjectType>(instance: &T, signal_name: &str) {
138     unsafe {
139         gobject_ffi::g_signal_stop_emission_by_name(
140             instance.as_object_ref().to_glib_none().0,
141             signal_name.to_glib_none().0,
142         );
143     }
144 }
145 
146 #[doc(alias = "g_signal_has_handler_pending")]
signal_has_handler_pending<T: ObjectType>( instance: &T, signal_id: crate::subclass::SignalId, detail: Option<crate::Quark>, may_be_blocked: bool, ) -> bool147 pub fn signal_has_handler_pending<T: ObjectType>(
148     instance: &T,
149     signal_id: crate::subclass::SignalId,
150     detail: Option<crate::Quark>,
151     may_be_blocked: bool,
152 ) -> bool {
153     unsafe {
154         from_glib(gobject_ffi::g_signal_has_handler_pending(
155             instance.as_object_ref().to_glib_none().0,
156             signal_id.into_glib(),
157             detail.map_or(0, |d| d.into_glib()),
158             may_be_blocked.into_glib(),
159         ))
160     }
161 }
162