1// Take a look at the license at the top of the repository in the LICENSE file.
2
3// rustdoc-stripper-ignore-next
4//! `IMPL` Low level signal support.
5
6use std::{mem, num::NonZeroU64};
7
8use ffi::gpointer;
9use gobject_ffi::{self, GCallback};
10use libc::{c_char, c_ulong, c_void};
11
12use crate::{prelude::*, translate::*};
13
14// rustdoc-stripper-ignore-next
15/// The id of a signal that is returned by `connect`.
16///
17/// This type does not implement `Clone` to prevent disconnecting
18/// the same signal handler multiple times.
19///
20/// ```ignore
21/// use glib::SignalHandlerId;
22/// use gtk::prelude::*;
23/// use std::cell::RefCell;
24///
25/// struct Button {
26/// widget: gtk::Button,
27/// clicked_handler_id: RefCell<Option<SignalHandlerId>>,
28/// }
29///
30/// impl Button {
31/// fn new() -> Self {
32/// let widget = gtk::Button::new();
33/// let clicked_handler_id = RefCell::new(Some(widget.connect_clicked(|_button| {
34/// // Do something.
35/// })));
36/// Self {
37/// widget,
38/// clicked_handler_id,
39/// }
40/// }
41///
42/// fn disconnect(&self) {
43/// if let Some(id) = self.clicked_handler_id.take() {
44/// self.widget.disconnect(id)
45/// }
46/// }
47/// }
48/// ```
49#[derive(Debug, Eq, PartialEq)]
50pub struct SignalHandlerId(NonZeroU64);
51
52impl SignalHandlerId {
53 // rustdoc-stripper-ignore-next
54 /// Returns the internal signal handler ID.
55 pub unsafe fn as_raw(&self) -> libc::c_ulong {
56 self.0.get() as libc::c_ulong
57 }
58}
59
60impl FromGlib<c_ulong> for SignalHandlerId {
61 #[inline]
62 unsafe fn from_glib(val: c_ulong) -> Self {
63 debug_assert_ne!(val, 0);
64 Self(NonZeroU64::new_unchecked(val as _))
65 }
66}
67
68pub unsafe fn connect_raw<F>(
69 receiver: *mut gobject_ffi::GObject,
70 signal_name: *const c_char,
71 trampoline: GCallback,
72 closure: *mut F,
73) -> SignalHandlerId {
74 unsafe extern "C" fn destroy_closure<F>(ptr: *mut c_void, _: *mut gobject_ffi::GClosure) {
75 // destroy
76 let _ = Box::<F>::from_raw(ptr as *mut _);
77 }
78 debug_assert_eq!(mem::size_of::<*mut F>(), mem::size_of::<gpointer>());
79 debug_assert!(trampoline.is_some());
80 let handle: u64 = gobject_ffi::g_signal_connect_data(
81 instance:receiver,
82 detailed_signal:signal_name,
83 c_handler:trampoline,
84 data:closure as *mut _,
85 destroy_data:Some(destroy_closure::<F>),
86 connect_flags:0,
87 );
88 debug_assert!(handle > 0);
89 from_glib(val:handle)
90}
91
92#[doc(alias = "g_signal_handler_block")]
93pub fn signal_handler_block<T: ObjectType>(instance: &T, handler_id: &SignalHandlerId) {
94 unsafe {
95 gobject_ffi::g_signal_handler_block(
96 instance:instance.as_object_ref().to_glib_none().0,
97 handler_id:handler_id.as_raw(),
98 );
99 }
100}
101
102#[doc(alias = "g_signal_handler_unblock")]
103pub fn signal_handler_unblock<T: ObjectType>(instance: &T, handler_id: &SignalHandlerId) {
104 unsafe {
105 gobject_ffi::g_signal_handler_unblock(
106 instance:instance.as_object_ref().to_glib_none().0,
107 handler_id:handler_id.as_raw(),
108 );
109 }
110}
111
112#[allow(clippy::needless_pass_by_value)]
113#[doc(alias = "g_signal_handler_disconnect")]
114pub fn signal_handler_disconnect<T: ObjectType>(instance: &T, handler_id: SignalHandlerId) {
115 unsafe {
116 gobject_ffi::g_signal_handler_disconnect(
117 instance:instance.as_object_ref().to_glib_none().0,
118 handler_id:handler_id.as_raw(),
119 );
120 }
121}
122
123#[doc(alias = "g_signal_stop_emission_by_name")]
124pub fn signal_stop_emission_by_name<T: ObjectType>(instance: &T, signal_name: &str) {
125 unsafe {
126 gobject_ffi::g_signal_stop_emission_by_name(
127 instance:instance.as_object_ref().to_glib_none().0,
128 detailed_signal:signal_name.to_glib_none().0,
129 );
130 }
131}
132
133#[doc(alias = "g_signal_has_handler_pending")]
134pub fn signal_has_handler_pending<T: ObjectType>(
135 instance: &T,
136 signal_id: crate::subclass::SignalId,
137 detail: Option<crate::Quark>,
138 may_be_blocked: bool,
139) -> bool {
140 unsafe {
141 from_glib(val:gobject_ffi::g_signal_has_handler_pending(
142 instance:instance.as_object_ref().to_glib_none().0,
143 signal_id:signal_id.into_glib(),
144 detail:detail.map_or(0, |d| d.into_glib()),
145 may_be_blocked:may_be_blocked.into_glib(),
146 ))
147 }
148}
149
150// rustdoc-stripper-ignore-next
151/// Whether to invoke the other event handlers.
152#[derive(Copy, Clone, Debug, PartialEq, Eq)]
153pub enum Propagation {
154 // Stop other handlers from being invoked for the event.
155 Stop,
156 // Propagate the event further.
157 Proceed,
158}
159
160impl Propagation {
161 // rustdoc-stripper-ignore-next
162 /// Returns `true` if this is a `Stop` variant.
163 pub fn is_stop(&self) -> bool {
164 matches!(self, Self::Stop)
165 }
166
167 // rustdoc-stripper-ignore-next
168 /// Returns `true` if this is a `Proceed` variant.
169 pub fn is_proceed(&self) -> bool {
170 matches!(self, Self::Proceed)
171 }
172}
173
174impl From<bool> for Propagation {
175 fn from(value: bool) -> Self {
176 if value {
177 Self::Stop
178 } else {
179 Self::Proceed
180 }
181 }
182}
183
184impl From<Propagation> for bool {
185 fn from(c: Propagation) -> Self {
186 match c {
187 Propagation::Stop => true,
188 Propagation::Proceed => false,
189 }
190 }
191}
192
193#[doc(hidden)]
194impl IntoGlib for Propagation {
195 type GlibType = ffi::gboolean;
196
197 #[inline]
198 fn into_glib(self) -> ffi::gboolean {
199 bool::from(self).into_glib()
200 }
201}
202
203#[doc(hidden)]
204impl FromGlib<ffi::gboolean> for Propagation {
205 #[inline]
206 unsafe fn from_glib(value: ffi::gboolean) -> Self {
207 bool::from_glib(val:value).into()
208 }
209}
210
211impl crate::ToValue for Propagation {
212 fn to_value(&self) -> crate::Value {
213 bool::from(*self).to_value()
214 }
215
216 fn value_type(&self) -> crate::Type {
217 <bool as crate::StaticType>::static_type()
218 }
219}
220
221impl From<Propagation> for crate::Value {
222 #[inline]
223 fn from(v: Propagation) -> Self {
224 bool::from(v).into()
225 }
226}
227