Skip to main content

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