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 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                    &parameters,
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}