gio/dbus_connection.rs
1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use std::{boxed::Box as Box_, future::Future, marker::PhantomData, num::NonZeroU32};
4
5use crate::{
6 ActionGroup, DBusConnection, DBusInterfaceInfo, DBusMessage, DBusMethodInvocation,
7 DBusPropertyInfoFlags, DBusSignalFlags, MenuModel, ffi,
8};
9use futures_channel::mpsc;
10use futures_core::{FusedStream, Stream};
11use glib::{WeakRef, prelude::*, translate::*, variant::VariantTypeMismatchError};
12use pin_project_lite::pin_project;
13
14pub trait DBusMethodCall: Sized {
15 fn parse_call(
16 obj_path: &str,
17 interface: Option<&str>,
18 method: &str,
19 params: glib::Variant,
20 ) -> Result<Self, glib::Error>;
21}
22
23// rustdoc-stripper-ignore-next
24/// Handle method invocations.
25pub struct MethodCallBuilder<'a, T> {
26 registration: RegistrationBuilder<'a>,
27 capture_type: PhantomData<T>,
28}
29
30impl<'a, T: DBusMethodCall> MethodCallBuilder<'a, T> {
31 // rustdoc-stripper-ignore-next
32 /// Handle invocation of a parsed method call.
33 ///
34 /// For each DBus method call parse the call, and then invoke the given closure
35 /// with
36 ///
37 /// 1. the DBus connection object,
38 /// 2. the name of the sender of the method call,
39 /// 3. the parsed call, and
40 /// 4. the method invocation object.
41 ///
42 /// The closure **must** return a value through the invocation object in all
43 /// code paths, using any of its `return_` functions, such as
44 /// [`DBusMethodInvocation::return_result`] or
45 /// [`DBusMethodInvocation::return_future_local`], to finish the call.
46 ///
47 /// If direct access to the invocation object is not needed,
48 /// [`invoke_and_return`] and [`invoke_and_return_future_local`] provide a
49 /// safer interface where the callback returns a result directly.
50 pub fn invoke<F>(self, f: F) -> RegistrationBuilder<'a>
51 where
52 F: Fn(DBusConnection, Option<&str>, T, DBusMethodInvocation) + 'static,
53 {
54 self.registration.method_call(
55 move |connection, sender, obj_path, interface, method, params, invocation| {
56 match T::parse_call(obj_path, interface, method, params) {
57 Ok(call) => f(connection, sender, call, invocation),
58 Err(error) => invocation.return_gerror(error),
59 }
60 },
61 )
62 }
63
64 // rustdoc-stripper-ignore-next
65 /// Handle invocation of a parsed method call.
66 ///
67 /// For each DBus method call parse the call, and then invoke the given closure
68 /// with
69 ///
70 /// 1. the DBus connection object,
71 /// 2. the name of the sender of the method call, and
72 /// 3. the parsed call.
73 ///
74 /// The return value of the closure is then returned on the method call.
75 /// If the returned variant value is not a tuple, it is automatically wrapped
76 /// in a single element tuple, as DBus methods must always return tuples.
77 /// See [`DBusMethodInvocation::return_result`] for details.
78 pub fn invoke_and_return<F>(self, f: F) -> RegistrationBuilder<'a>
79 where
80 F: Fn(DBusConnection, Option<&str>, T) -> Result<Option<glib::Variant>, glib::Error>
81 + 'static,
82 {
83 self.invoke(move |connection, sender, call, invocation| {
84 invocation.return_result(f(connection, sender, call))
85 })
86 }
87
88 // rustdoc-stripper-ignore-next
89 /// Handle an async invocation of a parsed method call.
90 ///
91 /// For each DBus method call parse the call, and then invoke the given closure
92 /// with
93 ///
94 /// 1. the DBus connection object,
95 /// 2. the name of the sender of the method call, and
96 /// 3. the parsed call.
97 ///
98 /// The output of the future is then returned on the method call.
99 /// If the returned variant value is not a tuple, it is automatically wrapped
100 /// in a single element tuple, as DBus methods must always return tuples.
101 /// See [`DBusMethodInvocation::return_future_local`] for details.
102 pub fn invoke_and_return_future_local<F, Fut>(self, f: F) -> RegistrationBuilder<'a>
103 where
104 F: Fn(DBusConnection, Option<&str>, T) -> Fut + 'static,
105 Fut: Future<Output = Result<Option<glib::Variant>, glib::Error>> + 'static,
106 {
107 self.invoke(move |connection, sender, call, invocation| {
108 invocation.return_future_local(f(connection, sender, call));
109 })
110 }
111}
112
113#[derive(Debug, Eq, PartialEq)]
114pub struct RegistrationId(NonZeroU32);
115#[derive(Debug, Eq, PartialEq)]
116pub struct ActionGroupExportId(NonZeroU32);
117#[derive(Debug, Eq, PartialEq)]
118pub struct MenuModelExportId(NonZeroU32);
119#[derive(Debug, Eq, PartialEq)]
120pub struct FilterId(NonZeroU32);
121
122#[derive(Debug, Eq, PartialEq)]
123pub struct SignalSubscriptionId(NonZeroU32);
124
125// rustdoc-stripper-ignore-next
126/// A strong subscription to a D-Bus signal.
127///
128/// Keep a reference to a D-Bus connection to maintain a subscription on a
129/// D-Bus signal even if the connection has no other strong reference.
130///
131/// When dropped, unsubscribes from signal on the connection, and then drop the
132/// reference on the connection. If no other strong reference on the connection
133/// exists the connection is closed and destroyed.
134#[derive(Debug)]
135pub struct SignalSubscription(DBusConnection, Option<SignalSubscriptionId>);
136
137impl SignalSubscription {
138 // rustdoc-stripper-ignore-next
139 /// Downgrade this signal subscription to a weak one.
140 #[must_use]
141 pub fn downgrade(mut self) -> WeakSignalSubscription {
142 WeakSignalSubscription(self.0.downgrade(), self.1.take())
143 }
144}
145
146impl Drop for SignalSubscription {
147 fn drop(&mut self) {
148 if let Some(id) = self.1.take() {
149 #[allow(deprecated)]
150 self.0.signal_unsubscribe(id);
151 }
152 }
153}
154
155// rustdoc-stripper-ignore-next
156/// A weak subscription to a D-Bus signal.
157///
158/// Like [`SignalSubscription`] but hold only a weak reference to the D-Bus
159/// connection the signal is subscribed on, i.e. maintain the subscription on
160/// the D-Bus signal only as long as some strong reference exists on the
161/// corresponding D-Bus connection.
162///
163/// When dropped, unsubscribes from signal on the connection if it still exists,
164/// and then drop the reference on the connection. If no other strong reference
165/// on the connection exists the connection is closed and destroyed.
166#[derive(Debug)]
167pub struct WeakSignalSubscription(WeakRef<DBusConnection>, Option<SignalSubscriptionId>);
168
169impl WeakSignalSubscription {
170 // rustdoc-stripper-ignore-next
171 /// Upgrade this signal subscription to a strong one.
172 #[must_use]
173 pub fn upgrade(mut self) -> Option<SignalSubscription> {
174 self.0
175 .upgrade()
176 .map(|c| SignalSubscription(c, self.1.take()))
177 }
178}
179
180impl Drop for WeakSignalSubscription {
181 fn drop(&mut self) {
182 if let Some(id) = self.1.take()
183 && let Some(connection) = self.0.upgrade()
184 {
185 #[allow(deprecated)]
186 connection.signal_unsubscribe(id);
187 }
188 }
189}
190
191// rustdoc-stripper-ignore-next
192/// An emitted D-Bus signal.
193#[derive(Debug, Copy, Clone)]
194pub struct DBusSignalRef<'a> {
195 // rustdoc-stripper-ignore-next
196 /// The connection the signal was emitted on.
197 pub connection: &'a DBusConnection,
198 // rustdoc-stripper-ignore-next
199 /// The bus name of the sender which emitted the signal.
200 pub sender_name: &'a str,
201 // rustdoc-stripper-ignore-next
202 /// The path of the object on `sender` the signal was emitted from.
203 pub object_path: &'a str,
204 // rustdoc-stripper-ignore-next
205 /// The interface the signal belongs to.
206 pub interface_name: &'a str,
207 // rustdoc-stripper-ignore-next
208 /// The name of the emitted signal.
209 pub signal_name: &'a str,
210 // rustdoc-stripper-ignore-next
211 /// Parameters the signal was emitted with.
212 pub parameters: &'a glib::Variant,
213}
214
215pin_project! {
216 // rustdoc-stripper-ignore-next
217 /// A subscribed stream.
218 ///
219 /// A stream which wraps an inner stream of type `S` while holding on to a
220 /// subscription handle `H` to keep a subscription alive.
221 #[derive(Debug)]
222 #[must_use = "streams do nothing unless polled"]
223 pub struct SubscribedSignalStream<H, S> {
224 #[pin]
225 stream: S,
226 subscription: H,
227 }
228}
229
230impl<S> SubscribedSignalStream<SignalSubscription, S> {
231 // rustdoc-stripper-ignore-next
232 /// Downgrade the inner signal subscription to a weak one.
233 ///
234 /// See [`SignalSubscription::downgrade`] and [`WeakSignalSubscription`].
235 pub fn downgrade(self) -> SubscribedSignalStream<WeakSignalSubscription, S> {
236 SubscribedSignalStream {
237 subscription: self.subscription.downgrade(),
238 stream: self.stream,
239 }
240 }
241}
242
243impl<S> SubscribedSignalStream<WeakSignalSubscription, S> {
244 // rustdoc-stripper-ignore-next
245 /// Upgrade the inner signal subscription to a strong one.
246 ///
247 /// See [`WeakSignalSubscription::upgrade`] and [`SignalSubscription`].
248 pub fn downgrade(self) -> Option<SubscribedSignalStream<SignalSubscription, S>> {
249 self.subscription
250 .upgrade()
251 .map(|subscription| SubscribedSignalStream {
252 subscription,
253 stream: self.stream,
254 })
255 }
256}
257
258impl<H, S> Stream for SubscribedSignalStream<H, S>
259where
260 S: Stream,
261{
262 type Item = S::Item;
263
264 fn poll_next(
265 self: std::pin::Pin<&mut Self>,
266 cx: &mut std::task::Context<'_>,
267 ) -> std::task::Poll<Option<Self::Item>> {
268 let this = self.project();
269 this.stream.poll_next(cx)
270 }
271
272 fn size_hint(&self) -> (usize, Option<usize>) {
273 self.stream.size_hint()
274 }
275}
276
277impl<H, S> FusedStream for SubscribedSignalStream<H, S>
278where
279 S: FusedStream,
280{
281 fn is_terminated(&self) -> bool {
282 self.stream.is_terminated()
283 }
284}
285
286// rustdoc-stripper-ignore-next
287/// Build a registered DBus object, by handling different parts of DBus.
288#[must_use = "The builder must be built to be used"]
289pub struct RegistrationBuilder<'a> {
290 connection: &'a DBusConnection,
291 object_path: &'a str,
292 interface_info: &'a DBusInterfaceInfo,
293 #[allow(clippy::type_complexity)]
294 method_call: Option<
295 Box_<
296 dyn Fn(
297 DBusConnection,
298 Option<&str>,
299 &str,
300 Option<&str>,
301 &str,
302 glib::Variant,
303 DBusMethodInvocation,
304 ),
305 >,
306 >,
307 #[allow(clippy::type_complexity)]
308 get_property: Option<
309 Box_<
310 dyn Fn(
311 DBusConnection,
312 Option<&str>,
313 &str,
314 &str,
315 &str,
316 ) -> Result<glib::Variant, glib::Error>,
317 >,
318 >,
319 #[allow(clippy::type_complexity)]
320 set_property: Option<
321 Box_<
322 dyn Fn(
323 DBusConnection,
324 Option<&str>,
325 &str,
326 &str,
327 &str,
328 glib::Variant,
329 ) -> Result<(), glib::Error>,
330 >,
331 >,
332}
333
334impl<'a> RegistrationBuilder<'a> {
335 pub fn method_call<
336 F: Fn(
337 DBusConnection,
338 Option<&str>,
339 &str,
340 Option<&str>,
341 &str,
342 glib::Variant,
343 DBusMethodInvocation,
344 ) + 'static,
345 >(
346 mut self,
347 f: F,
348 ) -> Self {
349 self.method_call = Some(Box_::new(f));
350 self
351 }
352
353 // rustdoc-stripper-ignore-next
354 /// Handle method calls on this object.
355 ///
356 /// Return a builder for method calls which parses method names and
357 /// parameters with the given [`DBusMethodCall`] and then allows to dispatch
358 /// the parsed call either synchronously or asynchronously.
359 pub fn typed_method_call<T: DBusMethodCall>(self) -> MethodCallBuilder<'a, T> {
360 MethodCallBuilder {
361 registration: self,
362 capture_type: Default::default(),
363 }
364 }
365
366 #[doc(alias = "get_property")]
367 pub fn property<
368 F: Fn(DBusConnection, Option<&str>, &str, &str, &str) -> Result<glib::Variant, glib::Error>
369 + 'static,
370 >(
371 mut self,
372 f: F,
373 ) -> Self {
374 self.get_property = Some(Box_::new(f));
375 self
376 }
377
378 pub fn set_property<
379 F: Fn(
380 DBusConnection,
381 Option<&str>,
382 &str,
383 &str,
384 &str,
385 glib::Variant,
386 ) -> Result<(), glib::Error>
387 + 'static,
388 >(
389 mut self,
390 f: F,
391 ) -> Self {
392 self.set_property = Some(Box_::new(f));
393 self
394 }
395
396 pub fn build(self) -> Result<RegistrationId, glib::Error> {
397 const PROPERTIES_INTERFACE: &str = "org.freedesktop.DBus.Properties";
398 unsafe {
399 let mut error = std::ptr::null_mut();
400 let interface_info = self.interface_info.clone();
401 // We handle get_/set_property ourselves because the closure versions can't return errors.
402 let id = ffi::g_dbus_connection_register_object_with_closures(
403 self.connection.to_glib_none().0,
404 self.object_path.to_glib_none().0,
405 self.interface_info.to_glib_none().0,
406 self.method_call
407 .map(move |f| {
408 glib::Closure::new_local(move |args| {
409 let conn = args[0].get::<DBusConnection>().unwrap();
410 let sender = args[1].get::<Option<&str>>().unwrap();
411 let object_path = args[2].get::<&str>().unwrap();
412 let interface_name = args[3].get::<Option<&str>>().unwrap();
413 let method_name = args[4].get::<&str>().unwrap();
414 let parameters = args[5].get::<glib::Variant>().unwrap();
415
416 // Work around GLib memory leak: Assume that the invocation is passed
417 // as `transfer full` into the closure.
418 //
419 // This workaround is not going to break with future versions of
420 // GLib as fixing the bug was considered a breaking API change.
421 //
422 // See https://gitlab.gnome.org/GNOME/glib/-/merge_requests/4427
423 let invocation = from_glib_full(glib::gobject_ffi::g_value_get_object(
424 args[6].as_ptr(),
425 )
426 as *mut ffi::GDBusMethodInvocation);
427
428 if interface_name == Some(PROPERTIES_INTERFACE)
429 && method_name == "Get"
430 && let Some(get_property) = self.get_property.as_deref()
431 {
432 handle_get_property(
433 conn,
434 sender,
435 object_path,
436 parameters,
437 invocation,
438 get_property,
439 );
440 } else if interface_name == Some(PROPERTIES_INTERFACE)
441 && method_name == "Set"
442 && let Some(set_property) = self.set_property.as_deref()
443 {
444 handle_set_property(
445 conn,
446 sender,
447 object_path,
448 parameters,
449 invocation,
450 set_property,
451 );
452 } else if interface_name == Some(PROPERTIES_INTERFACE)
453 && method_name == "GetAll"
454 && let Some(get_property) = self.get_property.as_deref()
455 {
456 handle_get_all_properties(
457 conn,
458 sender,
459 object_path,
460 &interface_info,
461 invocation,
462 get_property,
463 );
464 } else {
465 f(
466 conn,
467 sender,
468 object_path,
469 interface_name,
470 method_name,
471 parameters,
472 invocation,
473 );
474 }
475
476 None
477 })
478 })
479 .to_glib_none()
480 .0,
481 std::ptr::null_mut(),
482 std::ptr::null_mut(),
483 &mut error,
484 );
485
486 if error.is_null() {
487 Ok(RegistrationId(NonZeroU32::new_unchecked(id)))
488 } else {
489 Err(from_glib_full(error))
490 }
491 }
492 }
493}
494
495fn handle_get_property(
496 connection: DBusConnection,
497 sender: Option<&str>,
498 object_path: &str,
499 parameters: glib::Variant,
500 invocation: DBusMethodInvocation,
501 get_property_func: &dyn Fn(
502 DBusConnection,
503 Option<&str>,
504 &str,
505 &str,
506 &str,
507 ) -> Result<glib::Variant, glib::Error>,
508) {
509 let (interface_name, property_name): (String, String) = parameters
510 .get()
511 .expect("parameters are guaranteed to have correct types by gdbus");
512 let result = get_property_func(
513 connection,
514 sender,
515 object_path,
516 &interface_name,
517 &property_name,
518 );
519 invocation.return_result(result.map(|variant| Some(variant.to_variant())));
520}
521
522fn handle_set_property(
523 connection: DBusConnection,
524 sender: Option<&str>,
525 object_path: &str,
526 parameters: glib::Variant,
527 invocation: DBusMethodInvocation,
528 set_property_func: &dyn Fn(
529 DBusConnection,
530 Option<&str>,
531 &str,
532 &str,
533 &str,
534 glib::Variant,
535 ) -> Result<(), glib::Error>,
536) {
537 let (interface_name, property_name, value): (String, String, _) = parameters
538 .get()
539 .expect("parameters are guaranteed to have correct types by gdbus");
540 let result = set_property_func(
541 connection,
542 sender,
543 object_path,
544 &interface_name,
545 &property_name,
546 value,
547 )
548 .map(|_| None);
549 invocation.return_result(result);
550}
551
552// Re-implementation of `invoke_get_all_properties_in_idle_cb` in gdbusconnection.c
553fn handle_get_all_properties(
554 connection: DBusConnection,
555 sender: Option<&str>,
556 object_path: &str,
557 interface_info: &DBusInterfaceInfo,
558 invocation: DBusMethodInvocation,
559 get_property_func: &dyn Fn(
560 DBusConnection,
561 Option<&str>,
562 &str,
563 &str,
564 &str,
565 ) -> Result<glib::Variant, glib::Error>,
566) {
567 // Interface name is already validated by gdbus
568
569 let interface_name = interface_info.name();
570 let readable_properties = interface_info
571 .properties()
572 .filter(|p| p.flags().contains(DBusPropertyInfoFlags::READABLE));
573
574 let property_values = glib::VariantDict::default();
575 for property in readable_properties {
576 let property_name = property.name();
577 if let Ok(value) = get_property_func(
578 connection.clone(),
579 sender,
580 object_path,
581 interface_name,
582 property_name,
583 ) {
584 property_values.insert(property_name, value);
585 }
586 }
587
588 invocation.return_value(Some(&(property_values,).to_variant()));
589}
590
591impl DBusConnection {
592 /// Registers callbacks for exported objects at @object_path with the
593 /// D-Bus interface that is described in @interface_info.
594 ///
595 /// Calls to functions in @vtable (and @user_data_free_func) will happen
596 /// in the thread-default main context
597 /// (see [`glib::MainContext::push_thread_default()`][crate::glib::MainContext::push_thread_default()])
598 /// of the thread you are calling this method from.
599 ///
600 /// Note that all #GVariant values passed to functions in @vtable will match
601 /// the signature given in @interface_info - if a remote caller passes
602 /// incorrect values, the `org.freedesktop.DBus.Error.InvalidArgs`
603 /// is returned to the remote caller.
604 ///
605 /// Additionally, if the remote caller attempts to invoke methods or
606 /// access properties not mentioned in @interface_info the
607 /// `org.freedesktop.DBus.Error.UnknownMethod` resp.
608 /// `org.freedesktop.DBus.Error.InvalidArgs` errors
609 /// are returned to the caller.
610 ///
611 /// It is considered a programming error if the
612 /// #GDBusInterfaceGetPropertyFunc function in @vtable returns a
613 /// #GVariant of incorrect type.
614 ///
615 /// If an existing callback is already registered at @object_path and
616 /// @interface_name, then @error is set to [`IOErrorEnum::Exists`][crate::IOErrorEnum::Exists].
617 ///
618 /// GDBus automatically implements the standard D-Bus interfaces
619 /// org.freedesktop.DBus.Properties, org.freedesktop.DBus.Introspectable
620 /// and org.freedesktop.Peer, so you don't have to implement those for the
621 /// objects you export. You can implement org.freedesktop.DBus.Properties
622 /// yourself, e.g. to handle getting and setting of properties asynchronously.
623 ///
624 /// Note that the reference count on @interface_info will be
625 /// incremented by 1 (unless allocated statically, e.g. if the
626 /// reference count is -1, see g_dbus_interface_info_ref()) for as long
627 /// as the object is exported. Also note that @vtable will be copied.
628 ///
629 /// See this [server][`DBusConnection`][crate::DBusConnection]#an-example-d-bus-server]
630 /// for an example of how to use this method.
631 /// ## `object_path`
632 /// the object path to register at
633 /// ## `interface_info`
634 /// introspection data for the interface
635 /// ## `vtable`
636 /// a #GDBusInterfaceVTable to call into or [`None`]
637 ///
638 /// # Returns
639 ///
640 /// 0 if @error is set, otherwise a registration id (never 0)
641 /// that can be used with g_dbus_connection_unregister_object()
642 #[doc(alias = "g_dbus_connection_register_object")]
643 #[doc(alias = "g_dbus_connection_register_object_with_closures")]
644 pub fn register_object<'a>(
645 &'a self,
646 object_path: &'a str,
647 interface_info: &'a DBusInterfaceInfo,
648 ) -> RegistrationBuilder<'a> {
649 RegistrationBuilder {
650 connection: self,
651 object_path,
652 interface_info,
653 method_call: None,
654 get_property: None,
655 set_property: None,
656 }
657 }
658
659 /// Unregisters an object.
660 /// ## `registration_id`
661 /// a registration id obtained from
662 /// g_dbus_connection_register_object()
663 ///
664 /// # Returns
665 ///
666 /// [`true`] if the object was unregistered, [`false`] otherwise
667 #[doc(alias = "g_dbus_connection_unregister_object")]
668 pub fn unregister_object(
669 &self,
670 registration_id: RegistrationId,
671 ) -> Result<(), glib::error::BoolError> {
672 unsafe {
673 glib::result_from_gboolean!(
674 ffi::g_dbus_connection_unregister_object(
675 self.to_glib_none().0,
676 registration_id.0.into()
677 ),
678 "Failed to unregister D-Bus object"
679 )
680 }
681 }
682
683 /// Exports @action_group on @self at @object_path.
684 ///
685 /// The implemented D-Bus API should be considered private. It is
686 /// subject to change in the future.
687 ///
688 /// A given object path can only have one action group exported on it.
689 /// If this constraint is violated, the export will fail and 0 will be
690 /// returned (with @error set accordingly).
691 ///
692 /// You can unexport the action group using
693 /// [`unexport_action_group()`][Self::unexport_action_group()] with the return value of
694 /// this function.
695 ///
696 /// The thread default main context is taken at the time of this call.
697 /// All incoming action activations and state change requests are
698 /// reported from this context. Any changes on the action group that
699 /// cause it to emit signals must also come from this same context.
700 /// Since incoming action activations and state change requests are
701 /// rather likely to cause changes on the action group, this effectively
702 /// limits a given action group to being exported from only one main
703 /// context.
704 /// ## `object_path`
705 /// a D-Bus object path
706 /// ## `action_group`
707 /// an action group
708 ///
709 /// # Returns
710 ///
711 /// the ID of the export (never zero), or 0 in case of failure
712 #[doc(alias = "g_dbus_connection_export_action_group")]
713 pub fn export_action_group<P: IsA<ActionGroup>>(
714 &self,
715 object_path: &str,
716 action_group: &P,
717 ) -> Result<ActionGroupExportId, glib::Error> {
718 unsafe {
719 let mut error = std::ptr::null_mut();
720 let id = ffi::g_dbus_connection_export_action_group(
721 self.to_glib_none().0,
722 object_path.to_glib_none().0,
723 action_group.as_ref().to_glib_none().0,
724 &mut error,
725 );
726 if error.is_null() {
727 Ok(ActionGroupExportId(NonZeroU32::new_unchecked(id)))
728 } else {
729 Err(from_glib_full(error))
730 }
731 }
732 }
733
734 /// Reverses the effect of a previous call to
735 /// [`export_action_group()`][Self::export_action_group()].
736 ///
737 /// It is an error to call this function with an ID that wasn’t returned from
738 /// [`export_action_group()`][Self::export_action_group()] or to call it with the same
739 /// ID more than once.
740 /// ## `export_id`
741 /// the ID from [`export_action_group()`][Self::export_action_group()]
742 #[doc(alias = "g_dbus_connection_unexport_action_group")]
743 pub fn unexport_action_group(&self, export_id: ActionGroupExportId) {
744 unsafe {
745 ffi::g_dbus_connection_unexport_action_group(self.to_glib_none().0, export_id.0.into());
746 }
747 }
748
749 /// Exports @menu on @self at @object_path.
750 ///
751 /// The implemented D-Bus API should be considered private.
752 /// It is subject to change in the future.
753 ///
754 /// An object path can only have one menu model exported on it. If this
755 /// constraint is violated, the export will fail and 0 will be
756 /// returned (with @error set accordingly).
757 ///
758 /// Exporting menus with sections containing more than
759 /// `G_MENU_EXPORTER_MAX_SECTION_SIZE` items is not supported and results in
760 /// undefined behavior.
761 ///
762 /// You can unexport the menu model using
763 /// g_dbus_connection_unexport_menu_model() with the return value of
764 /// this function.
765 /// ## `object_path`
766 /// a D-Bus object path
767 /// ## `menu`
768 /// a #GMenuModel
769 ///
770 /// # Returns
771 ///
772 /// the ID of the export (never zero), or 0 in case of failure
773 #[doc(alias = "g_dbus_connection_export_menu_model")]
774 pub fn export_menu_model<P: IsA<MenuModel>>(
775 &self,
776 object_path: &str,
777 menu: &P,
778 ) -> Result<MenuModelExportId, glib::Error> {
779 unsafe {
780 let mut error = std::ptr::null_mut();
781 let id = ffi::g_dbus_connection_export_menu_model(
782 self.to_glib_none().0,
783 object_path.to_glib_none().0,
784 menu.as_ref().to_glib_none().0,
785 &mut error,
786 );
787 if error.is_null() {
788 Ok(MenuModelExportId(NonZeroU32::new_unchecked(id)))
789 } else {
790 Err(from_glib_full(error))
791 }
792 }
793 }
794
795 /// Reverses the effect of a previous call to
796 /// g_dbus_connection_export_menu_model().
797 ///
798 /// It is an error to call this function with an ID that wasn't returned
799 /// from g_dbus_connection_export_menu_model() or to call it with the
800 /// same ID more than once.
801 /// ## `export_id`
802 /// the ID from g_dbus_connection_export_menu_model()
803 #[doc(alias = "g_dbus_connection_unexport_menu_model")]
804 pub fn unexport_menu_model(&self, export_id: MenuModelExportId) {
805 unsafe {
806 ffi::g_dbus_connection_unexport_menu_model(self.to_glib_none().0, export_id.0.into());
807 }
808 }
809
810 /// Adds a message filter. Filters are handlers that are run on all
811 /// incoming and outgoing messages, prior to standard dispatch. Filters
812 /// are run in the order that they were added. The same handler can be
813 /// added as a filter more than once, in which case it will be run more
814 /// than once. Filters added during a filter callback won't be run on
815 /// the message being processed. Filter functions are allowed to modify
816 /// and even drop messages.
817 ///
818 /// Note that filters are run in a dedicated message handling thread so
819 /// they can't block and, generally, can't do anything but signal a
820 /// worker thread. Also note that filters are rarely needed - use API
821 /// such as g_dbus_connection_send_message_with_reply(),
822 /// g_dbus_connection_signal_subscribe() or g_dbus_connection_call() instead.
823 ///
824 /// If a filter consumes an incoming message the message is not
825 /// dispatched anywhere else - not even the standard dispatch machinery
826 /// (that API such as g_dbus_connection_signal_subscribe() and
827 /// g_dbus_connection_send_message_with_reply() relies on) will see the
828 /// message. Similarly, if a filter consumes an outgoing message, the
829 /// message will not be sent to the other peer.
830 ///
831 /// If @user_data_free_func is non-[`None`], it will be called (in the
832 /// thread-default main context of the thread you are calling this
833 /// method from) at some point after @user_data is no longer
834 /// needed. (It is not guaranteed to be called synchronously when the
835 /// filter is removed, and may be called after @self has been
836 /// destroyed.)
837 /// ## `filter_function`
838 /// a filter function
839 ///
840 /// # Returns
841 ///
842 /// a filter identifier that can be used with
843 /// g_dbus_connection_remove_filter()
844 #[doc(alias = "g_dbus_connection_add_filter")]
845 pub fn add_filter<
846 P: Fn(&DBusConnection, &DBusMessage, bool) -> Option<DBusMessage> + 'static,
847 >(
848 &self,
849 filter_function: P,
850 ) -> FilterId {
851 let filter_function_data: Box_<P> = Box_::new(filter_function);
852 unsafe extern "C" fn filter_function_func<
853 P: Fn(&DBusConnection, &DBusMessage, bool) -> Option<DBusMessage> + 'static,
854 >(
855 connection: *mut ffi::GDBusConnection,
856 message: *mut ffi::GDBusMessage,
857 incoming: glib::ffi::gboolean,
858 user_data: glib::ffi::gpointer,
859 ) -> *mut ffi::GDBusMessage {
860 unsafe {
861 let connection = from_glib_borrow(connection);
862 let message = from_glib_full(message);
863 let incoming = from_glib(incoming);
864 let callback: &P = &*(user_data as *mut _);
865 let res = (*callback)(&connection, &message, incoming);
866 res.into_glib_ptr()
867 }
868 }
869 let filter_function = Some(filter_function_func::<P> as _);
870 unsafe extern "C" fn user_data_free_func_func<
871 P: Fn(&DBusConnection, &DBusMessage, bool) -> Option<DBusMessage> + 'static,
872 >(
873 data: glib::ffi::gpointer,
874 ) {
875 unsafe {
876 let _callback: Box_<P> = Box_::from_raw(data as *mut _);
877 }
878 }
879 let destroy_call3 = Some(user_data_free_func_func::<P> as _);
880 let super_callback0: Box_<P> = filter_function_data;
881 unsafe {
882 let id = ffi::g_dbus_connection_add_filter(
883 self.to_glib_none().0,
884 filter_function,
885 Box_::into_raw(super_callback0) as *mut _,
886 destroy_call3,
887 );
888 FilterId(NonZeroU32::new_unchecked(id))
889 }
890 }
891
892 /// Removes a filter.
893 ///
894 /// Note that since filters run in a different thread, there is a race
895 /// condition where it is possible that the filter will be running even
896 /// after calling g_dbus_connection_remove_filter(), so you cannot just
897 /// free data that the filter might be using. Instead, you should pass
898 /// a #GDestroyNotify to g_dbus_connection_add_filter(), which will be
899 /// called when it is guaranteed that the data is no longer needed.
900 /// ## `filter_id`
901 /// an identifier obtained from g_dbus_connection_add_filter()
902 #[doc(alias = "g_dbus_connection_remove_filter")]
903 pub fn remove_filter(&self, filter_id: FilterId) {
904 unsafe {
905 ffi::g_dbus_connection_remove_filter(self.to_glib_none().0, filter_id.0.into());
906 }
907 }
908
909 // rustdoc-stripper-ignore-next
910 /// Subscribe to a D-Bus signal.
911 ///
912 /// See [`Self::signal_subscribe`] for arguments.
913 ///
914 /// Return a signal subscription which keeps a reference to this D-Bus
915 /// connection and unsubscribes from the signal when dropped.
916 ///
917 /// To avoid reference cycles you may wish to downgrade the returned
918 /// subscription to a weak one with [`SignalSubscription::downgrade`].
919 #[must_use]
920 pub fn subscribe_to_signal<P: Fn(DBusSignalRef) + 'static>(
921 &self,
922 sender: Option<&str>,
923 interface_name: Option<&str>,
924 member: Option<&str>,
925 object_path: Option<&str>,
926 arg0: Option<&str>,
927 flags: DBusSignalFlags,
928 callback: P,
929 ) -> SignalSubscription {
930 #[allow(deprecated)]
931 let id = self.signal_subscribe(
932 sender,
933 interface_name,
934 member,
935 object_path,
936 arg0,
937 flags,
938 move |connection, sender_name, object_path, interface_name, signal_name, parameters| {
939 callback(DBusSignalRef {
940 connection,
941 sender_name,
942 object_path,
943 interface_name,
944 signal_name,
945 parameters,
946 });
947 },
948 );
949 SignalSubscription(self.clone(), Some(id))
950 }
951
952 /// Subscribes to signals on @self and invokes @callback whenever
953 /// the signal is received. Note that @callback will be invoked in the
954 /// thread-default main context (see [`glib::MainContext::push_thread_default()`][crate::glib::MainContext::push_thread_default()])
955 /// of the thread you are calling this method from.
956 ///
957 /// If @self is not a message bus connection, @sender must be
958 /// [`None`].
959 ///
960 /// If @sender is a well-known name note that @callback is invoked with
961 /// the unique name for the owner of @sender, not the well-known name
962 /// as one would expect. This is because the message bus rewrites the
963 /// name. As such, to avoid certain race conditions, users should be
964 /// tracking the name owner of the well-known name and use that when
965 /// processing the received signal.
966 ///
967 /// If one of [`DBusSignalFlags::MATCH_ARG0_NAMESPACE`][crate::DBusSignalFlags::MATCH_ARG0_NAMESPACE] or
968 /// [`DBusSignalFlags::MATCH_ARG0_PATH`][crate::DBusSignalFlags::MATCH_ARG0_PATH] are given, @arg0 is
969 /// interpreted as part of a namespace or path. The first argument
970 /// of a signal is matched against that part as specified by D-Bus.
971 ///
972 /// If @user_data_free_func is non-[`None`], it will be called (in the
973 /// thread-default main context of the thread you are calling this
974 /// method from) at some point after @user_data is no longer
975 /// needed. (It is not guaranteed to be called synchronously when the
976 /// signal is unsubscribed from, and may be called after @self
977 /// has been destroyed.)
978 ///
979 /// As @callback is potentially invoked in a different thread from where it’s
980 /// emitted, it’s possible for this to happen after
981 /// g_dbus_connection_signal_unsubscribe() has been called in another thread.
982 /// Due to this, @user_data should have a strong reference which is freed with
983 /// @user_data_free_func, rather than pointing to data whose lifecycle is tied
984 /// to the signal subscription. For example, if a #GObject is used to store the
985 /// subscription ID from g_dbus_connection_signal_subscribe(), a strong reference
986 /// to that #GObject must be passed to @user_data, and g_object_unref() passed to
987 /// @user_data_free_func. You are responsible for breaking the resulting
988 /// reference count cycle by explicitly unsubscribing from the signal when
989 /// dropping the last external reference to the #GObject. Alternatively, a weak
990 /// reference may be used.
991 ///
992 /// It is guaranteed that if you unsubscribe from a signal using
993 /// g_dbus_connection_signal_unsubscribe() from the same thread which made the
994 /// corresponding g_dbus_connection_signal_subscribe() call, @callback will not
995 /// be invoked after g_dbus_connection_signal_unsubscribe() returns.
996 ///
997 /// The returned subscription identifier is an opaque value which is guaranteed
998 /// to never be zero.
999 ///
1000 /// This function can never fail.
1001 /// ## `sender`
1002 /// sender name to match on (unique or well-known name)
1003 /// or [`None`] to listen from all senders
1004 /// ## `interface_name`
1005 /// D-Bus interface name to match on or [`None`] to
1006 /// match on all interfaces
1007 /// ## `member`
1008 /// D-Bus signal name to match on or [`None`] to match on
1009 /// all signals
1010 /// ## `object_path`
1011 /// object path to match on or [`None`] to match on
1012 /// all object paths
1013 /// ## `arg0`
1014 /// contents of first string argument to match on or [`None`]
1015 /// to match on all kinds of arguments
1016 /// ## `flags`
1017 /// #GDBusSignalFlags describing how arg0 is used in subscribing to the
1018 /// signal
1019 /// ## `callback`
1020 /// callback to invoke when there is a signal matching the requested data
1021 ///
1022 /// # Returns
1023 ///
1024 /// a subscription identifier that can be used with g_dbus_connection_signal_unsubscribe()
1025 #[doc(alias = "g_dbus_connection_signal_subscribe")]
1026 #[allow(clippy::too_many_arguments)]
1027 #[deprecated(note = "Prefer subscribe_to_signal")]
1028 pub fn signal_subscribe<
1029 P: Fn(&DBusConnection, &str, &str, &str, &str, &glib::Variant) + 'static,
1030 >(
1031 &self,
1032 sender: Option<&str>,
1033 interface_name: Option<&str>,
1034 member: Option<&str>,
1035 object_path: Option<&str>,
1036 arg0: Option<&str>,
1037 flags: DBusSignalFlags,
1038 callback: P,
1039 ) -> SignalSubscriptionId {
1040 let callback_data: Box_<P> = Box_::new(callback);
1041 unsafe extern "C" fn callback_func<
1042 P: Fn(&DBusConnection, &str, &str, &str, &str, &glib::Variant) + 'static,
1043 >(
1044 connection: *mut ffi::GDBusConnection,
1045 sender_name: *const libc::c_char,
1046 object_path: *const libc::c_char,
1047 interface_name: *const libc::c_char,
1048 signal_name: *const libc::c_char,
1049 parameters: *mut glib::ffi::GVariant,
1050 user_data: glib::ffi::gpointer,
1051 ) {
1052 unsafe {
1053 let connection = from_glib_borrow(connection);
1054 let sender_name: Borrowed<glib::GString> = from_glib_borrow(sender_name);
1055 let object_path: Borrowed<glib::GString> = from_glib_borrow(object_path);
1056 let interface_name: Borrowed<glib::GString> = from_glib_borrow(interface_name);
1057 let signal_name: Borrowed<glib::GString> = from_glib_borrow(signal_name);
1058 let parameters = from_glib_borrow(parameters);
1059 let callback: &P = &*(user_data as *mut _);
1060 (*callback)(
1061 &connection,
1062 sender_name.as_str(),
1063 object_path.as_str(),
1064 interface_name.as_str(),
1065 signal_name.as_str(),
1066 ¶meters,
1067 );
1068 }
1069 }
1070 let callback = Some(callback_func::<P> as _);
1071 unsafe extern "C" fn user_data_free_func_func<
1072 P: Fn(&DBusConnection, &str, &str, &str, &str, &glib::Variant) + 'static,
1073 >(
1074 data: glib::ffi::gpointer,
1075 ) {
1076 unsafe {
1077 let _callback: Box_<P> = Box_::from_raw(data as *mut _);
1078 }
1079 }
1080 let destroy_call9 = Some(user_data_free_func_func::<P> as _);
1081 let super_callback0: Box_<P> = callback_data;
1082 unsafe {
1083 let id = ffi::g_dbus_connection_signal_subscribe(
1084 self.to_glib_none().0,
1085 sender.to_glib_none().0,
1086 interface_name.to_glib_none().0,
1087 member.to_glib_none().0,
1088 object_path.to_glib_none().0,
1089 arg0.to_glib_none().0,
1090 flags.into_glib(),
1091 callback,
1092 Box_::into_raw(super_callback0) as *mut _,
1093 destroy_call9,
1094 );
1095 SignalSubscriptionId(NonZeroU32::new_unchecked(id))
1096 }
1097 }
1098
1099 /// Unsubscribes from signals.
1100 ///
1101 /// Note that there may still be D-Bus traffic to process (relating to this
1102 /// signal subscription) in the current thread-default #GMainContext after this
1103 /// function has returned. You should continue to iterate the #GMainContext
1104 /// until the #GDestroyNotify function passed to
1105 /// g_dbus_connection_signal_subscribe() is called, in order to avoid memory
1106 /// leaks through callbacks queued on the #GMainContext after it’s stopped being
1107 /// iterated.
1108 /// Alternatively, any idle source with a priority lower than `G_PRIORITY_DEFAULT`
1109 /// that was scheduled after unsubscription, also indicates that all resources
1110 /// of this subscription are released.
1111 /// ## `subscription_id`
1112 /// a subscription id obtained from
1113 /// g_dbus_connection_signal_subscribe()
1114 #[doc(alias = "g_dbus_connection_signal_unsubscribe")]
1115 #[deprecated(note = "Prefer subscribe_to_signal")]
1116 pub fn signal_unsubscribe(&self, subscription_id: SignalSubscriptionId) {
1117 unsafe {
1118 ffi::g_dbus_connection_signal_unsubscribe(
1119 self.to_glib_none().0,
1120 subscription_id.0.into(),
1121 );
1122 }
1123 }
1124
1125 // rustdoc-stripper-ignore-next
1126 /// Subscribe to a D-Bus signal and receive signal emissions as a stream.
1127 ///
1128 /// See [`Self::signal_subscribe`] for arguments. `map_signal` maps the
1129 /// received signal to the stream's element.
1130 ///
1131 /// The returned stream holds a strong reference to this D-Bus connection,
1132 /// and unsubscribes from the signal when dropped. To avoid reference cycles
1133 /// you may wish to downgrade the returned stream to hold only weak
1134 /// reference to the connection using [`SubscribedSignalStream::downgrade`].
1135 ///
1136 /// After invoking `map_signal` the stream threads incoming signals through
1137 /// an unbounded channel. Hence, memory consumption will keep increasing
1138 /// as long as the stream consumer does not keep up with signal emissions.
1139 /// If you need to perform expensive processing in response to signals it's
1140 /// therefore recommended to insert an extra buffering and if the buffer
1141 /// overruns, either fail drop the entire stream, or drop individual signal
1142 /// emissions until the buffer has space again.
1143 pub fn receive_signal<T: 'static, F: Fn(DBusSignalRef) -> T + 'static>(
1144 &self,
1145 sender: Option<&str>,
1146 interface_name: Option<&str>,
1147 member: Option<&str>,
1148 object_path: Option<&str>,
1149 arg0: Option<&str>,
1150 flags: DBusSignalFlags,
1151 map_signal: F,
1152 ) -> SubscribedSignalStream<SignalSubscription, impl Stream<Item = T> + use<T, F>> {
1153 let (tx, rx) = mpsc::unbounded();
1154 let subscription = self.subscribe_to_signal(
1155 sender,
1156 interface_name,
1157 member,
1158 object_path,
1159 arg0,
1160 flags,
1161 move |signal| {
1162 // Just ignore send errors: if the receiver is dropped, the
1163 // signal subscription is dropped too, so the callback won't
1164 // be invoked anymore.
1165 let _ = tx.unbounded_send(map_signal(signal));
1166 },
1167 );
1168 SubscribedSignalStream {
1169 subscription,
1170 stream: rx,
1171 }
1172 }
1173
1174 // rustdoc-stripper-ignore-next
1175 /// Subscribe to a D-Bus signal and receive signal parameters as a stream.
1176 ///
1177 /// Like [`Self::receive_signal`] (which see for more information), but
1178 /// automatically decodes the emitted signal parameters to type `T`.
1179 /// If decoding fails the corresponding variant type error is sent
1180 /// downstream.
1181 pub fn receive_signal_parameters<T>(
1182 &self,
1183 sender: Option<&str>,
1184 interface_name: Option<&str>,
1185 member: Option<&str>,
1186 object_path: Option<&str>,
1187 arg0: Option<&str>,
1188 flags: DBusSignalFlags,
1189 ) -> SubscribedSignalStream<
1190 SignalSubscription,
1191 impl Stream<Item = Result<T, VariantTypeMismatchError>> + use<T>,
1192 >
1193 where
1194 T: FromVariant + 'static,
1195 {
1196 self.receive_signal(
1197 sender,
1198 interface_name,
1199 member,
1200 object_path,
1201 arg0,
1202 flags,
1203 |signal| signal.parameters.try_get(),
1204 )
1205 }
1206}