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