1#![doc(html_root_url = "https://docs.rs/glib-signal/0.4.0/")]
2#![cfg_attr(docsrs, feature(doc_notable_trait, doc_cfg))]
3
4#[cfg(feature = "futures")]
5pub use self::signal_stream::{ConnectEof, OnceFuture, SignalStream};
6#[doc(hidden)]
7pub use glib; pub use {
9 self::{
10 borrowed_object::BorrowedObject,
11 from_values::FromValues,
12 pointer::Pointer,
13 value_option::{PrimitiveValue, ToValueOption},
14 },
15 glib::SignalFlags,
16};
17use {
18 glib::{
19 object::{ObjectExt, ObjectType},
20 subclass::{signal::SignalBuilder, SignalId},
21 translate::{from_glib, IntoGlib, ToGlibPtr},
22 types::StaticType,
23 value::FromValue,
24 BoolError, Closure, Quark, SignalHandlerId,
25 },
26 std::{fmt::Debug, marker::PhantomData},
27};
28
29#[cfg(feature = "futures")]
30mod signal_stream;
31
32mod borrowed_object;
33
34mod pointer;
35
36mod value_option;
37
38mod from_values;
39
40mod macros;
41
42pub trait Signal: Copy + Debug {
43 type Object: ObjectType;
44 type Arguments: for<'a> FromValues<'a> + 'static;
45 type Return: ToValueOption;
46
47 const NAME: &'static str;
48 const FLAGS: SignalFlags = SignalFlags::empty();
49
50 fn signal() -> SignalId {
51 SignalId::lookup(Self::NAME, <Self::Object as StaticType>::static_type()).expect(Self::NAME)
52 }
53}
54
55pub trait DetailedSignal: Copy + Debug + Into<ConnectDetails<Self>> {
56 type Signal: Signal;
57 type Object: ObjectType;
58 type Arguments: for<'a> FromValues<'a> + 'static;
59 type Return: ToValueOption;
60
61 const DETAIL: Option<&'static str>;
62
63 fn detail() -> Option<Quark> {
64 match Self::DETAIL {
65 Some(detail) => Some(Quark::try_from_str(detail).expect(detail)),
66 None => None,
67 }
68 }
69
70 fn create_detail() -> Quark {
71 Quark::from_str(Self::DETAIL.expect("detail string required"))
72 }
73}
74
75impl<T: Signal> DetailedSignal for T {
76 type Arguments = <Self::Signal as Signal>::Arguments;
77 type Object = <Self::Signal as Signal>::Object;
78 type Return = <Self::Signal as Signal>::Return;
79 type Signal = Self;
80
81 const DETAIL: Option<&'static str> = None;
82}
83
84pub trait BuildableSignal: Signal {
85 fn builder<F: FnOnce(SignalBuilder) -> glib::subclass::Signal>(f: F) -> glib::subclass::Signal;
86}
87
88impl<T: Signal> BuildableSignal for T
89where
90 <Self::Return as ToValueOption>::Type: StaticType,
91{
92 fn builder<F: FnOnce(SignalBuilder) -> glib::subclass::Signal>(f: F) -> glib::subclass::Signal {
93 let builder = glib::subclass::Signal::builder(Self::NAME)
94 .param_types(<Self::Arguments as FromValues>::static_types())
95 .return_type::<<Self::Return as ToValueOption>::Type>()
96 .flags(Self::FLAGS);
97 f(builder)
98 }
99}
100
101pub trait BuildSignal: BuildableSignal {
102 fn build() -> glib::subclass::Signal {
103 Self::builder(|b| b.build())
104 }
105}
106
107#[cfg_attr(docsrs, doc(notable_trait))]
108pub trait Notifies<T: Signal>: ObjectType {}
109
110#[derive(Copy, Clone, Debug)]
111pub struct ConnectDetails<S = ()> {
112 signal: SignalId,
113 detail: Option<Quark>,
114 pub run_after: bool,
115 _signal: PhantomData<S>,
116}
117
118impl<S> ConnectDetails<S> {
119 pub unsafe fn with_parts(signal: SignalId, detail: Option<Quark>, run_after: bool) -> Self {
120 Self {
121 signal,
122 detail,
123 run_after,
124 _signal: PhantomData,
125 }
126 }
127
128 pub fn normalize(&self) -> ConnectDetails<()> {
129 unsafe { ConnectDetails::with_parts(self.signal(), self.detail(), self.run_after) }
130 }
131
132 pub fn signal(&self) -> SignalId {
133 self.signal
134 }
135
136 pub fn detail(&self) -> Option<Quark> {
137 self.detail
138 }
139}
140
141impl<S: DetailedSignal> ConnectDetails<S> {
142 pub fn new() -> Self {
143 Self::with_after(false)
144 }
145
146 pub fn with_after(run_after: bool) -> Self {
147 Self {
148 signal: <S::Signal as Signal>::signal(),
149 detail: S::detail(),
150 run_after,
151 _signal: PhantomData,
152 }
153 }
154
155 pub fn set_detail(&mut self, detail: Quark) {
156 assert!(self.detail.is_none());
157 self.detail = Some(detail);
158 }
159}
160
161impl<S: Signal> ConnectDetails<S> {
162 pub fn with_detail<D: Into<Option<Quark>>>(detail: D) -> Self {
163 Self {
164 signal: S::signal(),
165 detail: detail.into(),
166 run_after: false,
167 _signal: PhantomData,
168 }
169 }
170}
171
172impl ConnectDetails<()> {
173 pub fn with_name<O: StaticType>(signal: &str, after: bool) -> Option<Self> {
174 let (signal, detail) = SignalId::parse_name(signal, O::static_type(), false)?;
175 Some(unsafe { Self::with_parts(signal, detail, after) })
176 }
177}
178
179impl<S: DetailedSignal> From<ConnectDetails<S>> for ConnectDetails<()> {
180 fn from(v: ConnectDetails<S>) -> Self {
181 v.normalize()
182 }
183}
184
185impl<S: DetailedSignal> From<S> for ConnectDetails<S> {
186 fn from(_: S) -> Self {
187 Self::new()
188 }
189}
190
191pub trait ObjectSignalExt: ObjectType {
192 unsafe fn handle_closure(&self, signal: &ConnectDetails, callback: &Closure) -> Result<SignalHandlerId, BoolError>;
193 fn remove_handle(&self, handle: SignalHandlerId);
194
195 fn handle<S, S_, C>(&self, signal: S_, callback: C) -> SignalHandlerId
196 where
197 C: Fn(&Self, S::Arguments) -> <S::Return as ToValueOption>::Type,
198 S: DetailedSignal,
199 S_: Into<ConnectDetails<S>>,
200 Self: Notifies<S::Signal>;
201
202 #[cfg(feature = "futures")]
203 fn signal_stream<S, S_>(&self, signal: S_) -> SignalStream<Self, S::Arguments>
204 where
205 S: DetailedSignal,
206 S_: Into<ConnectDetails<S>>,
207 Self: Notifies<S::Signal>,
208 <S::Return as ToValueOption>::Type: Default;
209}
210
211impl<O: ObjectType> ObjectSignalExt for O
212where
213 for<'a> BorrowedObject<'a, O>: FromValue<'a>,
214{
215 unsafe fn handle_closure(&self, signal: &ConnectDetails, callback: &Closure) -> Result<SignalHandlerId, BoolError> {
216 let handle = glib::gobject_ffi::g_signal_connect_closure_by_id(
217 self.as_object_ref().to_glib_none().0,
218 signal.signal().into_glib(),
219 signal.detail().map(|q| q.into_glib()).unwrap_or(0),
220 callback.to_glib_none().0,
221 signal.run_after.into_glib(),
222 );
223 match handle {
224 0 => Err(glib::bool_error!(
225 "failed to connect signal {:?} of type {:?}",
226 signal,
227 Self::static_type()
228 )),
229 handle => Ok(from_glib(handle)),
230 }
231 }
232
233 fn handle<S, S_, C>(&self, signal: S_, callback: C) -> SignalHandlerId
234 where
235 C: Fn(&Self, S::Arguments) -> <S::Return as ToValueOption>::Type,
236 S: DetailedSignal,
237 S_: Into<ConnectDetails<S>>,
238 Self: Notifies<S::Signal>,
239 {
240 let signal = signal.into();
241 unsafe {
242 let callback = Closure::new_unsafe(move |values| {
243 let (this, args) = values.split_first().unwrap();
244 let this: BorrowedObject<Self> = this.get().unwrap();
245 let args = S::Arguments::from_values(args).unwrap();
246 callback(&this, args).into().to_value_option()
247 });
248 self.handle_closure(&signal.normalize(), &callback).unwrap()
249 }
250 }
251
252 fn remove_handle(&self, handle: SignalHandlerId) {
253 self.disconnect(handle)
254 }
255
256 #[cfg(feature = "futures")]
257 fn signal_stream<S, S_>(&self, signal: S_) -> SignalStream<Self, S::Arguments>
258 where
259 S: DetailedSignal,
260 S_: Into<ConnectDetails<S>>,
261 Self: Notifies<S::Signal>,
262 <S::Return as ToValueOption>::Type: Default,
263 {
264 let signal = signal.into();
265 SignalStream::connect(self, signal, |_, _| Default::default())
266 }
267}