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    ffi, ActionGroup, DBusConnection, DBusInterfaceInfo, DBusMessage, DBusMethodInvocation,
7    DBusSignalFlags, MenuModel,
8};
9use glib::{prelude::*, translate::*};
10
11pub trait DBusMethodCall: Sized {
12    fn parse_call(
13        obj_path: &str,
14        interface: Option<&str>,
15        method: &str,
16        params: glib::Variant,
17    ) -> Result<Self, glib::Error>;
18}
19
20// rustdoc-stripper-ignore-next
21/// Handle method invocations.
22pub struct MethodCallBuilder<'a, T> {
23    registration: RegistrationBuilder<'a>,
24    capture_type: PhantomData<T>,
25}
26
27impl<'a, T: DBusMethodCall> MethodCallBuilder<'a, T> {
28    // rustdoc-stripper-ignore-next
29    /// Handle invocation of a parsed method call.
30    ///
31    /// For each DBus method call parse the call, and then invoke the given closure
32    /// with
33    ///
34    /// 1. the DBus connection object,
35    /// 2. the name of the sender of the method call,
36    /// 3. the parsed call, and
37    /// 4. the method invocation object.
38    ///
39    /// The closure **must** return a value through the invocation object in all
40    /// code paths, using any of its `return_` functions, such as
41    /// [`DBusMethodInvocation::return_result`] or
42    /// [`DBusMethodInvocation::return_future_local`], to finish the call.
43    ///
44    /// If direct access to the invocation object is not needed,
45    /// [`invoke_and_return`] and [`invoke_and_return_future_local`] provide a
46    /// safer interface where the callback returns a result directly.
47    pub fn invoke<F>(self, f: F) -> RegistrationBuilder<'a>
48    where
49        F: Fn(DBusConnection, Option<&str>, T, DBusMethodInvocation) + 'static,
50    {
51        self.registration.method_call(
52            move |connection, sender, obj_path, interface, method, params, invocation| {
53                match T::parse_call(obj_path, interface, method, params) {
54                    Ok(call) => f(connection, sender, call, invocation),
55                    Err(error) => invocation.return_gerror(error),
56                }
57            },
58        )
59    }
60
61    // rustdoc-stripper-ignore-next
62    /// Handle invocation of a parsed method call.
63    ///
64    /// For each DBus method call parse the call, and then invoke the given closure
65    /// with
66    ///
67    /// 1. the DBus connection object,
68    /// 2. the name of the sender of the method call, and
69    /// 3. the parsed call.
70    ///
71    /// The return value of the closure is then returned on the method call.
72    /// If the returned variant value is not a tuple, it is automatically wrapped
73    /// in a single element tuple, as DBus methods must always return tuples.
74    /// See [`DBusMethodInvocation::return_result`] for details.
75    pub fn invoke_and_return<F>(self, f: F) -> RegistrationBuilder<'a>
76    where
77        F: Fn(DBusConnection, Option<&str>, T) -> Result<Option<glib::Variant>, glib::Error>
78            + 'static,
79    {
80        self.invoke(move |connection, sender, call, invocation| {
81            invocation.return_result(f(connection, sender, call))
82        })
83    }
84
85    // rustdoc-stripper-ignore-next
86    /// Handle an async invocation of a parsed method call.
87    ///
88    /// For each DBus method call parse the call, and then invoke the given closure
89    /// with
90    ///
91    /// 1. the DBus connection object,
92    /// 2. the name of the sender of the method call, and
93    /// 3. the parsed call.
94    ///
95    /// The output of the future is then returned on the method call.
96    /// If the returned variant value is not a tuple, it is automatically wrapped
97    /// in a single element tuple, as DBus methods must always return tuples.
98    /// See [`DBusMethodInvocation::return_future_local`] for details.
99    pub fn invoke_and_return_future_local<F, Fut>(self, f: F) -> RegistrationBuilder<'a>
100    where
101        F: Fn(DBusConnection, Option<&str>, T) -> Fut + 'static,
102        Fut: Future<Output = Result<Option<glib::Variant>, glib::Error>> + 'static,
103    {
104        self.invoke(move |connection, sender, call, invocation| {
105            invocation.return_future_local(f(connection, sender, call));
106        })
107    }
108}
109
110#[derive(Debug, Eq, PartialEq)]
111pub struct RegistrationId(NonZeroU32);
112#[derive(Debug, Eq, PartialEq)]
113pub struct WatcherId(NonZeroU32);
114#[derive(Debug, Eq, PartialEq)]
115pub struct ActionGroupExportId(NonZeroU32);
116#[derive(Debug, Eq, PartialEq)]
117pub struct MenuModelExportId(NonZeroU32);
118#[derive(Debug, Eq, PartialEq)]
119pub struct FilterId(NonZeroU32);
120#[derive(Debug, Eq, PartialEq)]
121pub struct SignalSubscriptionId(NonZeroU32);
122
123// rustdoc-stripper-ignore-next
124/// Build a registered DBus object, by handling different parts of DBus.
125#[must_use = "The builder must be built to be used"]
126pub struct RegistrationBuilder<'a> {
127    connection: &'a DBusConnection,
128    object_path: &'a str,
129    interface_info: &'a DBusInterfaceInfo,
130    #[allow(clippy::type_complexity)]
131    method_call: Option<
132        Box_<
133            dyn Fn(
134                DBusConnection,
135                Option<&str>,
136                &str,
137                Option<&str>,
138                &str,
139                glib::Variant,
140                DBusMethodInvocation,
141            ),
142        >,
143    >,
144    #[allow(clippy::type_complexity)]
145    get_property:
146        Option<Box_<dyn Fn(DBusConnection, Option<&str>, &str, &str, &str) -> glib::Variant>>,
147    #[allow(clippy::type_complexity)]
148    set_property:
149        Option<Box_<dyn Fn(DBusConnection, Option<&str>, &str, &str, &str, glib::Variant) -> bool>>,
150}
151
152impl<'a> RegistrationBuilder<'a> {
153    pub fn method_call<
154        F: Fn(
155                DBusConnection,
156                Option<&str>,
157                &str,
158                Option<&str>,
159                &str,
160                glib::Variant,
161                DBusMethodInvocation,
162            ) + 'static,
163    >(
164        mut self,
165        f: F,
166    ) -> Self {
167        self.method_call = Some(Box_::new(f));
168        self
169    }
170
171    // rustdoc-stripper-ignore-next
172    /// Handle method calls on this object.
173    ///
174    /// Return a builder for method calls which parses method names and
175    /// parameters with the given [`DBusMethodCall`] and then allows to dispatch
176    /// the parsed call either synchronously or asynchronously.
177    pub fn typed_method_call<T: DBusMethodCall>(self) -> MethodCallBuilder<'a, T> {
178        MethodCallBuilder {
179            registration: self,
180            capture_type: Default::default(),
181        }
182    }
183
184    #[doc(alias = "get_property")]
185    pub fn property<
186        F: Fn(DBusConnection, Option<&str>, &str, &str, &str) -> glib::Variant + 'static,
187    >(
188        mut self,
189        f: F,
190    ) -> Self {
191        self.get_property = Some(Box_::new(f));
192        self
193    }
194
195    pub fn set_property<
196        F: Fn(DBusConnection, Option<&str>, &str, &str, &str, glib::Variant) -> bool + 'static,
197    >(
198        mut self,
199        f: F,
200    ) -> Self {
201        self.set_property = Some(Box_::new(f));
202        self
203    }
204
205    pub fn build(self) -> Result<RegistrationId, glib::Error> {
206        unsafe {
207            let mut error = std::ptr::null_mut();
208            let id = ffi::g_dbus_connection_register_object_with_closures(
209                self.connection.to_glib_none().0,
210                self.object_path.to_glib_none().0,
211                self.interface_info.to_glib_none().0,
212                self.method_call
213                    .map(|f| {
214                        glib::Closure::new_local(move |args| {
215                            let conn = args[0].get::<DBusConnection>().unwrap();
216                            let sender = args[1].get::<Option<&str>>().unwrap();
217                            let object_path = args[2].get::<&str>().unwrap();
218                            let interface_name = args[3].get::<Option<&str>>().unwrap();
219                            let method_name = args[4].get::<&str>().unwrap();
220                            let parameters = args[5].get::<glib::Variant>().unwrap();
221
222                            // Work around GLib memory leak: Assume that the invocation is passed
223                            // as `transfer full` into the closure.
224                            //
225                            // This workaround is not going to break with future versions of
226                            // GLib as fixing the bug was considered a breaking API change.
227                            //
228                            // See https://gitlab.gnome.org/GNOME/glib/-/merge_requests/4427
229                            let invocation = from_glib_full(glib::gobject_ffi::g_value_get_object(
230                                args[6].as_ptr(),
231                            )
232                                as *mut ffi::GDBusMethodInvocation);
233
234                            f(
235                                conn,
236                                sender,
237                                object_path,
238                                interface_name,
239                                method_name,
240                                parameters,
241                                invocation,
242                            );
243                            None
244                        })
245                    })
246                    .to_glib_none()
247                    .0,
248                self.get_property
249                    .map(|f| {
250                        glib::Closure::new_local(move |args| {
251                            let conn = args[0].get::<DBusConnection>().unwrap();
252                            let sender = args[1].get::<Option<&str>>().unwrap();
253                            let object_path = args[2].get::<&str>().unwrap();
254                            let interface_name = args[3].get::<&str>().unwrap();
255                            let property_name = args[4].get::<&str>().unwrap();
256                            let result =
257                                f(conn, sender, object_path, interface_name, property_name);
258                            Some(result.to_value())
259                        })
260                    })
261                    .to_glib_none()
262                    .0,
263                self.set_property
264                    .map(|f| {
265                        glib::Closure::new_local(move |args| {
266                            let conn = args[0].get::<DBusConnection>().unwrap();
267                            let sender = args[1].get::<Option<&str>>().unwrap();
268                            let object_path = args[2].get::<&str>().unwrap();
269                            let interface_name = args[3].get::<&str>().unwrap();
270                            let property_name = args[4].get::<&str>().unwrap();
271                            let value = args[5].get::<glib::Variant>().unwrap();
272                            let result = f(
273                                conn,
274                                sender,
275                                object_path,
276                                interface_name,
277                                property_name,
278                                value,
279                            );
280                            Some(result.to_value())
281                        })
282                    })
283                    .to_glib_none()
284                    .0,
285                &mut error,
286            );
287
288            if error.is_null() {
289                Ok(RegistrationId(NonZeroU32::new_unchecked(id)))
290            } else {
291                Err(from_glib_full(error))
292            }
293        }
294    }
295}
296
297impl DBusConnection {
298    /// Registers callbacks for exported objects at @object_path with the
299    /// D-Bus interface that is described in @interface_info.
300    ///
301    /// Calls to functions in @vtable (and @user_data_free_func) will happen
302    /// in the thread-default main context
303    /// (see [`glib::MainContext::push_thread_default()`][crate::glib::MainContext::push_thread_default()])
304    /// of the thread you are calling this method from.
305    ///
306    /// Note that all #GVariant values passed to functions in @vtable will match
307    /// the signature given in @interface_info - if a remote caller passes
308    /// incorrect values, the `org.freedesktop.DBus.Error.InvalidArgs`
309    /// is returned to the remote caller.
310    ///
311    /// Additionally, if the remote caller attempts to invoke methods or
312    /// access properties not mentioned in @interface_info the
313    /// `org.freedesktop.DBus.Error.UnknownMethod` resp.
314    /// `org.freedesktop.DBus.Error.InvalidArgs` errors
315    /// are returned to the caller.
316    ///
317    /// It is considered a programming error if the
318    /// #GDBusInterfaceGetPropertyFunc function in @vtable returns a
319    /// #GVariant of incorrect type.
320    ///
321    /// If an existing callback is already registered at @object_path and
322    /// @interface_name, then @error is set to [`IOErrorEnum::Exists`][crate::IOErrorEnum::Exists].
323    ///
324    /// GDBus automatically implements the standard D-Bus interfaces
325    /// org.freedesktop.DBus.Properties, org.freedesktop.DBus.Introspectable
326    /// and org.freedesktop.Peer, so you don't have to implement those for the
327    /// objects you export. You can implement org.freedesktop.DBus.Properties
328    /// yourself, e.g. to handle getting and setting of properties asynchronously.
329    ///
330    /// Note that the reference count on @interface_info will be
331    /// incremented by 1 (unless allocated statically, e.g. if the
332    /// reference count is -1, see g_dbus_interface_info_ref()) for as long
333    /// as the object is exported. Also note that @vtable will be copied.
334    ///
335    /// See this [server][`DBusConnection`][crate::DBusConnection]#an-example-d-bus-server]
336    /// for an example of how to use this method.
337    /// ## `object_path`
338    /// the object path to register at
339    /// ## `interface_info`
340    /// introspection data for the interface
341    /// ## `vtable`
342    /// a #GDBusInterfaceVTable to call into or [`None`]
343    ///
344    /// # Returns
345    ///
346    /// 0 if @error is set, otherwise a registration id (never 0)
347    ///     that can be used with g_dbus_connection_unregister_object()
348    #[doc(alias = "g_dbus_connection_register_object")]
349    #[doc(alias = "g_dbus_connection_register_object_with_closures")]
350    pub fn register_object<'a>(
351        &'a self,
352        object_path: &'a str,
353        interface_info: &'a DBusInterfaceInfo,
354    ) -> RegistrationBuilder<'a> {
355        RegistrationBuilder {
356            connection: self,
357            object_path,
358            interface_info,
359            method_call: None,
360            get_property: None,
361            set_property: None,
362        }
363    }
364
365    /// Unregisters an object.
366    /// ## `registration_id`
367    /// a registration id obtained from
368    ///     g_dbus_connection_register_object()
369    ///
370    /// # Returns
371    ///
372    /// [`true`] if the object was unregistered, [`false`] otherwise
373    #[doc(alias = "g_dbus_connection_unregister_object")]
374    pub fn unregister_object(
375        &self,
376        registration_id: RegistrationId,
377    ) -> Result<(), glib::error::BoolError> {
378        unsafe {
379            glib::result_from_gboolean!(
380                ffi::g_dbus_connection_unregister_object(
381                    self.to_glib_none().0,
382                    registration_id.0.into()
383                ),
384                "Failed to unregister D-Bus object"
385            )
386        }
387    }
388
389    /// Exports @action_group on @self at @object_path.
390    ///
391    /// The implemented D-Bus API should be considered private.  It is
392    /// subject to change in the future.
393    ///
394    /// A given object path can only have one action group exported on it.
395    /// If this constraint is violated, the export will fail and 0 will be
396    /// returned (with @error set accordingly).
397    ///
398    /// You can unexport the action group using
399    /// [`unexport_action_group()`][Self::unexport_action_group()] with the return value of
400    /// this function.
401    ///
402    /// The thread default main context is taken at the time of this call.
403    /// All incoming action activations and state change requests are
404    /// reported from this context.  Any changes on the action group that
405    /// cause it to emit signals must also come from this same context.
406    /// Since incoming action activations and state change requests are
407    /// rather likely to cause changes on the action group, this effectively
408    /// limits a given action group to being exported from only one main
409    /// context.
410    /// ## `object_path`
411    /// a D-Bus object path
412    /// ## `action_group`
413    /// an action group
414    ///
415    /// # Returns
416    ///
417    /// the ID of the export (never zero), or 0 in case of failure
418    #[doc(alias = "g_dbus_connection_export_action_group")]
419    pub fn export_action_group<P: IsA<ActionGroup>>(
420        &self,
421        object_path: &str,
422        action_group: &P,
423    ) -> Result<ActionGroupExportId, glib::Error> {
424        unsafe {
425            let mut error = std::ptr::null_mut();
426            let id = ffi::g_dbus_connection_export_action_group(
427                self.to_glib_none().0,
428                object_path.to_glib_none().0,
429                action_group.as_ref().to_glib_none().0,
430                &mut error,
431            );
432            if error.is_null() {
433                Ok(ActionGroupExportId(NonZeroU32::new_unchecked(id)))
434            } else {
435                Err(from_glib_full(error))
436            }
437        }
438    }
439
440    /// Reverses the effect of a previous call to
441    /// [`export_action_group()`][Self::export_action_group()].
442    ///
443    /// It is an error to call this function with an ID that wasn’t returned from
444    /// [`export_action_group()`][Self::export_action_group()] or to call it with the same
445    /// ID more than once.
446    /// ## `export_id`
447    /// the ID from [`export_action_group()`][Self::export_action_group()]
448    #[doc(alias = "g_dbus_connection_unexport_action_group")]
449    pub fn unexport_action_group(&self, export_id: ActionGroupExportId) {
450        unsafe {
451            ffi::g_dbus_connection_unexport_action_group(self.to_glib_none().0, export_id.0.into());
452        }
453    }
454
455    /// Exports @menu on @self at @object_path.
456    ///
457    /// The implemented D-Bus API should be considered private.
458    /// It is subject to change in the future.
459    ///
460    /// An object path can only have one menu model exported on it. If this
461    /// constraint is violated, the export will fail and 0 will be
462    /// returned (with @error set accordingly).
463    ///
464    /// Exporting menus with sections containing more than
465    /// `G_MENU_EXPORTER_MAX_SECTION_SIZE` items is not supported and results in
466    /// undefined behavior.
467    ///
468    /// You can unexport the menu model using
469    /// g_dbus_connection_unexport_menu_model() with the return value of
470    /// this function.
471    /// ## `object_path`
472    /// a D-Bus object path
473    /// ## `menu`
474    /// a #GMenuModel
475    ///
476    /// # Returns
477    ///
478    /// the ID of the export (never zero), or 0 in case of failure
479    #[doc(alias = "g_dbus_connection_export_menu_model")]
480    pub fn export_menu_model<P: IsA<MenuModel>>(
481        &self,
482        object_path: &str,
483        menu: &P,
484    ) -> Result<MenuModelExportId, glib::Error> {
485        unsafe {
486            let mut error = std::ptr::null_mut();
487            let id = ffi::g_dbus_connection_export_menu_model(
488                self.to_glib_none().0,
489                object_path.to_glib_none().0,
490                menu.as_ref().to_glib_none().0,
491                &mut error,
492            );
493            if error.is_null() {
494                Ok(MenuModelExportId(NonZeroU32::new_unchecked(id)))
495            } else {
496                Err(from_glib_full(error))
497            }
498        }
499    }
500
501    /// Reverses the effect of a previous call to
502    /// g_dbus_connection_export_menu_model().
503    ///
504    /// It is an error to call this function with an ID that wasn't returned
505    /// from g_dbus_connection_export_menu_model() or to call it with the
506    /// same ID more than once.
507    /// ## `export_id`
508    /// the ID from g_dbus_connection_export_menu_model()
509    #[doc(alias = "g_dbus_connection_unexport_menu_model")]
510    pub fn unexport_menu_model(&self, export_id: MenuModelExportId) {
511        unsafe {
512            ffi::g_dbus_connection_unexport_menu_model(self.to_glib_none().0, export_id.0.into());
513        }
514    }
515
516    /// Adds a message filter. Filters are handlers that are run on all
517    /// incoming and outgoing messages, prior to standard dispatch. Filters
518    /// are run in the order that they were added.  The same handler can be
519    /// added as a filter more than once, in which case it will be run more
520    /// than once.  Filters added during a filter callback won't be run on
521    /// the message being processed. Filter functions are allowed to modify
522    /// and even drop messages.
523    ///
524    /// Note that filters are run in a dedicated message handling thread so
525    /// they can't block and, generally, can't do anything but signal a
526    /// worker thread. Also note that filters are rarely needed - use API
527    /// such as g_dbus_connection_send_message_with_reply(),
528    /// g_dbus_connection_signal_subscribe() or g_dbus_connection_call() instead.
529    ///
530    /// If a filter consumes an incoming message the message is not
531    /// dispatched anywhere else - not even the standard dispatch machinery
532    /// (that API such as g_dbus_connection_signal_subscribe() and
533    /// g_dbus_connection_send_message_with_reply() relies on) will see the
534    /// message. Similarly, if a filter consumes an outgoing message, the
535    /// message will not be sent to the other peer.
536    ///
537    /// If @user_data_free_func is non-[`None`], it will be called (in the
538    /// thread-default main context of the thread you are calling this
539    /// method from) at some point after @user_data is no longer
540    /// needed. (It is not guaranteed to be called synchronously when the
541    /// filter is removed, and may be called after @self has been
542    /// destroyed.)
543    /// ## `filter_function`
544    /// a filter function
545    ///
546    /// # Returns
547    ///
548    /// a filter identifier that can be used with
549    ///     g_dbus_connection_remove_filter()
550    #[doc(alias = "g_dbus_connection_add_filter")]
551    pub fn add_filter<
552        P: Fn(&DBusConnection, &DBusMessage, bool) -> Option<DBusMessage> + 'static,
553    >(
554        &self,
555        filter_function: P,
556    ) -> FilterId {
557        let filter_function_data: Box_<P> = Box_::new(filter_function);
558        unsafe extern "C" fn filter_function_func<
559            P: Fn(&DBusConnection, &DBusMessage, bool) -> Option<DBusMessage> + 'static,
560        >(
561            connection: *mut ffi::GDBusConnection,
562            message: *mut ffi::GDBusMessage,
563            incoming: glib::ffi::gboolean,
564            user_data: glib::ffi::gpointer,
565        ) -> *mut ffi::GDBusMessage {
566            let connection = from_glib_borrow(connection);
567            let message = from_glib_full(message);
568            let incoming = from_glib(incoming);
569            let callback: &P = &*(user_data as *mut _);
570            let res = (*callback)(&connection, &message, incoming);
571            res.into_glib_ptr()
572        }
573        let filter_function = Some(filter_function_func::<P> as _);
574        unsafe extern "C" fn user_data_free_func_func<
575            P: Fn(&DBusConnection, &DBusMessage, bool) -> Option<DBusMessage> + 'static,
576        >(
577            data: glib::ffi::gpointer,
578        ) {
579            let _callback: Box_<P> = Box_::from_raw(data as *mut _);
580        }
581        let destroy_call3 = Some(user_data_free_func_func::<P> as _);
582        let super_callback0: Box_<P> = filter_function_data;
583        unsafe {
584            let id = ffi::g_dbus_connection_add_filter(
585                self.to_glib_none().0,
586                filter_function,
587                Box_::into_raw(super_callback0) as *mut _,
588                destroy_call3,
589            );
590            FilterId(NonZeroU32::new_unchecked(id))
591        }
592    }
593
594    /// Removes a filter.
595    ///
596    /// Note that since filters run in a different thread, there is a race
597    /// condition where it is possible that the filter will be running even
598    /// after calling g_dbus_connection_remove_filter(), so you cannot just
599    /// free data that the filter might be using. Instead, you should pass
600    /// a #GDestroyNotify to g_dbus_connection_add_filter(), which will be
601    /// called when it is guaranteed that the data is no longer needed.
602    /// ## `filter_id`
603    /// an identifier obtained from g_dbus_connection_add_filter()
604    #[doc(alias = "g_dbus_connection_remove_filter")]
605    pub fn remove_filter(&self, filter_id: FilterId) {
606        unsafe {
607            ffi::g_dbus_connection_remove_filter(self.to_glib_none().0, filter_id.0.into());
608        }
609    }
610
611    /// Subscribes to signals on @self and invokes @callback whenever
612    /// the signal is received. Note that @callback will be invoked in the
613    /// thread-default main context (see [`glib::MainContext::push_thread_default()`][crate::glib::MainContext::push_thread_default()])
614    /// of the thread you are calling this method from.
615    ///
616    /// If @self is not a message bus connection, @sender must be
617    /// [`None`].
618    ///
619    /// If @sender is a well-known name note that @callback is invoked with
620    /// the unique name for the owner of @sender, not the well-known name
621    /// as one would expect. This is because the message bus rewrites the
622    /// name. As such, to avoid certain race conditions, users should be
623    /// tracking the name owner of the well-known name and use that when
624    /// processing the received signal.
625    ///
626    /// If one of [`DBusSignalFlags::MATCH_ARG0_NAMESPACE`][crate::DBusSignalFlags::MATCH_ARG0_NAMESPACE] or
627    /// [`DBusSignalFlags::MATCH_ARG0_PATH`][crate::DBusSignalFlags::MATCH_ARG0_PATH] are given, @arg0 is
628    /// interpreted as part of a namespace or path.  The first argument
629    /// of a signal is matched against that part as specified by D-Bus.
630    ///
631    /// If @user_data_free_func is non-[`None`], it will be called (in the
632    /// thread-default main context of the thread you are calling this
633    /// method from) at some point after @user_data is no longer
634    /// needed. (It is not guaranteed to be called synchronously when the
635    /// signal is unsubscribed from, and may be called after @self
636    /// has been destroyed.)
637    ///
638    /// As @callback is potentially invoked in a different thread from where it’s
639    /// emitted, it’s possible for this to happen after
640    /// g_dbus_connection_signal_unsubscribe() has been called in another thread.
641    /// Due to this, @user_data should have a strong reference which is freed with
642    /// @user_data_free_func, rather than pointing to data whose lifecycle is tied
643    /// to the signal subscription. For example, if a #GObject is used to store the
644    /// subscription ID from g_dbus_connection_signal_subscribe(), a strong reference
645    /// to that #GObject must be passed to @user_data, and g_object_unref() passed to
646    /// @user_data_free_func. You are responsible for breaking the resulting
647    /// reference count cycle by explicitly unsubscribing from the signal when
648    /// dropping the last external reference to the #GObject. Alternatively, a weak
649    /// reference may be used.
650    ///
651    /// It is guaranteed that if you unsubscribe from a signal using
652    /// g_dbus_connection_signal_unsubscribe() from the same thread which made the
653    /// corresponding g_dbus_connection_signal_subscribe() call, @callback will not
654    /// be invoked after g_dbus_connection_signal_unsubscribe() returns.
655    ///
656    /// The returned subscription identifier is an opaque value which is guaranteed
657    /// to never be zero.
658    ///
659    /// This function can never fail.
660    /// ## `sender`
661    /// sender name to match on (unique or well-known name)
662    ///     or [`None`] to listen from all senders
663    /// ## `interface_name`
664    /// D-Bus interface name to match on or [`None`] to
665    ///     match on all interfaces
666    /// ## `member`
667    /// D-Bus signal name to match on or [`None`] to match on
668    ///     all signals
669    /// ## `object_path`
670    /// object path to match on or [`None`] to match on
671    ///     all object paths
672    /// ## `arg0`
673    /// contents of first string argument to match on or [`None`]
674    ///     to match on all kinds of arguments
675    /// ## `flags`
676    /// #GDBusSignalFlags describing how arg0 is used in subscribing to the
677    ///     signal
678    /// ## `callback`
679    /// callback to invoke when there is a signal matching the requested data
680    ///
681    /// # Returns
682    ///
683    /// a subscription identifier that can be used with g_dbus_connection_signal_unsubscribe()
684    #[doc(alias = "g_dbus_connection_signal_subscribe")]
685    #[allow(clippy::too_many_arguments)]
686    pub fn signal_subscribe<
687        P: Fn(&DBusConnection, &str, &str, &str, &str, &glib::Variant) + 'static,
688    >(
689        &self,
690        sender: Option<&str>,
691        interface_name: Option<&str>,
692        member: Option<&str>,
693        object_path: Option<&str>,
694        arg0: Option<&str>,
695        flags: DBusSignalFlags,
696        callback: P,
697    ) -> SignalSubscriptionId {
698        let callback_data: Box_<P> = Box_::new(callback);
699        unsafe extern "C" fn callback_func<
700            P: Fn(&DBusConnection, &str, &str, &str, &str, &glib::Variant) + 'static,
701        >(
702            connection: *mut ffi::GDBusConnection,
703            sender_name: *const libc::c_char,
704            object_path: *const libc::c_char,
705            interface_name: *const libc::c_char,
706            signal_name: *const libc::c_char,
707            parameters: *mut glib::ffi::GVariant,
708            user_data: glib::ffi::gpointer,
709        ) {
710            let connection = from_glib_borrow(connection);
711            let sender_name: Borrowed<glib::GString> = from_glib_borrow(sender_name);
712            let object_path: Borrowed<glib::GString> = from_glib_borrow(object_path);
713            let interface_name: Borrowed<glib::GString> = from_glib_borrow(interface_name);
714            let signal_name: Borrowed<glib::GString> = from_glib_borrow(signal_name);
715            let parameters = from_glib_borrow(parameters);
716            let callback: &P = &*(user_data as *mut _);
717            (*callback)(
718                &connection,
719                sender_name.as_str(),
720                object_path.as_str(),
721                interface_name.as_str(),
722                signal_name.as_str(),
723                &parameters,
724            );
725        }
726        let callback = Some(callback_func::<P> as _);
727        unsafe extern "C" fn user_data_free_func_func<
728            P: Fn(&DBusConnection, &str, &str, &str, &str, &glib::Variant) + 'static,
729        >(
730            data: glib::ffi::gpointer,
731        ) {
732            let _callback: Box_<P> = Box_::from_raw(data as *mut _);
733        }
734        let destroy_call9 = Some(user_data_free_func_func::<P> as _);
735        let super_callback0: Box_<P> = callback_data;
736        unsafe {
737            let id = ffi::g_dbus_connection_signal_subscribe(
738                self.to_glib_none().0,
739                sender.to_glib_none().0,
740                interface_name.to_glib_none().0,
741                member.to_glib_none().0,
742                object_path.to_glib_none().0,
743                arg0.to_glib_none().0,
744                flags.into_glib(),
745                callback,
746                Box_::into_raw(super_callback0) as *mut _,
747                destroy_call9,
748            );
749            SignalSubscriptionId(NonZeroU32::new_unchecked(id))
750        }
751    }
752
753    /// Unsubscribes from signals.
754    ///
755    /// Note that there may still be D-Bus traffic to process (relating to this
756    /// signal subscription) in the current thread-default #GMainContext after this
757    /// function has returned. You should continue to iterate the #GMainContext
758    /// until the #GDestroyNotify function passed to
759    /// g_dbus_connection_signal_subscribe() is called, in order to avoid memory
760    /// leaks through callbacks queued on the #GMainContext after it’s stopped being
761    /// iterated.
762    /// Alternatively, any idle source with a priority lower than `G_PRIORITY_DEFAULT`
763    /// that was scheduled after unsubscription, also indicates that all resources
764    /// of this subscription are released.
765    /// ## `subscription_id`
766    /// a subscription id obtained from
767    ///     g_dbus_connection_signal_subscribe()
768    #[doc(alias = "g_dbus_connection_signal_unsubscribe")]
769    pub fn signal_unsubscribe(&self, subscription_id: SignalSubscriptionId) {
770        unsafe {
771            ffi::g_dbus_connection_signal_unsubscribe(
772                self.to_glib_none().0,
773                subscription_id.0.into(),
774            );
775        }
776    }
777}