glib_signal/
lib.rs

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; // for macro use
8pub 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}