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 futures_channel::mpsc;
10use futures_core::{FusedStream, Stream};
11use glib::{prelude::*, translate::*, variant::VariantTypeMismatchError, WeakRef};
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            if let Some(connection) = self.0.upgrade() {
186                #[allow(deprecated)]
187                connection.signal_unsubscribe(id);
188            }
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:
311        Option<Box_<dyn Fn(DBusConnection, Option<&str>, &str, &str, &str) -> glib::Variant>>,
312    #[allow(clippy::type_complexity)]
313    set_property:
314        Option<Box_<dyn Fn(DBusConnection, Option<&str>, &str, &str, &str, glib::Variant) -> bool>>,
315}
316
317impl<'a> RegistrationBuilder<'a> {
318    pub fn method_call<
319        F: Fn(
320                DBusConnection,
321                Option<&str>,
322                &str,
323                Option<&str>,
324                &str,
325                glib::Variant,
326                DBusMethodInvocation,
327            ) + 'static,
328    >(
329        mut self,
330        f: F,
331    ) -> Self {
332        self.method_call = Some(Box_::new(f));
333        self
334    }
335
336    // rustdoc-stripper-ignore-next
337    /// Handle method calls on this object.
338    ///
339    /// Return a builder for method calls which parses method names and
340    /// parameters with the given [`DBusMethodCall`] and then allows to dispatch
341    /// the parsed call either synchronously or asynchronously.
342    pub fn typed_method_call<T: DBusMethodCall>(self) -> MethodCallBuilder<'a, T> {
343        MethodCallBuilder {
344            registration: self,
345            capture_type: Default::default(),
346        }
347    }
348
349    #[doc(alias = "get_property")]
350    pub fn property<
351        F: Fn(DBusConnection, Option<&str>, &str, &str, &str) -> glib::Variant + 'static,
352    >(
353        mut self,
354        f: F,
355    ) -> Self {
356        self.get_property = Some(Box_::new(f));
357        self
358    }
359
360    pub fn set_property<
361        F: Fn(DBusConnection, Option<&str>, &str, &str, &str, glib::Variant) -> bool + 'static,
362    >(
363        mut self,
364        f: F,
365    ) -> Self {
366        self.set_property = Some(Box_::new(f));
367        self
368    }
369
370    pub fn build(self) -> Result<RegistrationId, glib::Error> {
371        unsafe {
372            let mut error = std::ptr::null_mut();
373            let id = ffi::g_dbus_connection_register_object_with_closures(
374                self.connection.to_glib_none().0,
375                self.object_path.to_glib_none().0,
376                self.interface_info.to_glib_none().0,
377                self.method_call
378                    .map(|f| {
379                        glib::Closure::new_local(move |args| {
380                            let conn = args[0].get::<DBusConnection>().unwrap();
381                            let sender = args[1].get::<Option<&str>>().unwrap();
382                            let object_path = args[2].get::<&str>().unwrap();
383                            let interface_name = args[3].get::<Option<&str>>().unwrap();
384                            let method_name = args[4].get::<&str>().unwrap();
385                            let parameters = args[5].get::<glib::Variant>().unwrap();
386
387                            // Work around GLib memory leak: Assume that the invocation is passed
388                            // as `transfer full` into the closure.
389                            //
390                            // This workaround is not going to break with future versions of
391                            // GLib as fixing the bug was considered a breaking API change.
392                            //
393                            // See https://gitlab.gnome.org/GNOME/glib/-/merge_requests/4427
394                            let invocation = from_glib_full(glib::gobject_ffi::g_value_get_object(
395                                args[6].as_ptr(),
396                            )
397                                as *mut ffi::GDBusMethodInvocation);
398
399                            f(
400                                conn,
401                                sender,
402                                object_path,
403                                interface_name,
404                                method_name,
405                                parameters,
406                                invocation,
407                            );
408                            None
409                        })
410                    })
411                    .to_glib_none()
412                    .0,
413                self.get_property
414                    .map(|f| {
415                        glib::Closure::new_local(move |args| {
416                            let conn = args[0].get::<DBusConnection>().unwrap();
417                            let sender = args[1].get::<Option<&str>>().unwrap();
418                            let object_path = args[2].get::<&str>().unwrap();
419                            let interface_name = args[3].get::<&str>().unwrap();
420                            let property_name = args[4].get::<&str>().unwrap();
421                            let result =
422                                f(conn, sender, object_path, interface_name, property_name);
423                            Some(result.to_value())
424                        })
425                    })
426                    .to_glib_none()
427                    .0,
428                self.set_property
429                    .map(|f| {
430                        glib::Closure::new_local(move |args| {
431                            let conn = args[0].get::<DBusConnection>().unwrap();
432                            let sender = args[1].get::<Option<&str>>().unwrap();
433                            let object_path = args[2].get::<&str>().unwrap();
434                            let interface_name = args[3].get::<&str>().unwrap();
435                            let property_name = args[4].get::<&str>().unwrap();
436                            let value = args[5].get::<glib::Variant>().unwrap();
437                            let result = f(
438                                conn,
439                                sender,
440                                object_path,
441                                interface_name,
442                                property_name,
443                                value,
444                            );
445                            Some(result.to_value())
446                        })
447                    })
448                    .to_glib_none()
449                    .0,
450                &mut error,
451            );
452
453            if error.is_null() {
454                Ok(RegistrationId(NonZeroU32::new_unchecked(id)))
455            } else {
456                Err(from_glib_full(error))
457            }
458        }
459    }
460}
461
462impl DBusConnection {
463    /// Registers callbacks for exported objects at @object_path with the
464    /// D-Bus interface that is described in @interface_info.
465    ///
466    /// Calls to functions in @vtable (and @user_data_free_func) will happen
467    /// in the thread-default main context
468    /// (see [`glib::MainContext::push_thread_default()`][crate::glib::MainContext::push_thread_default()])
469    /// of the thread you are calling this method from.
470    ///
471    /// Note that all #GVariant values passed to functions in @vtable will match
472    /// the signature given in @interface_info - if a remote caller passes
473    /// incorrect values, the `org.freedesktop.DBus.Error.InvalidArgs`
474    /// is returned to the remote caller.
475    ///
476    /// Additionally, if the remote caller attempts to invoke methods or
477    /// access properties not mentioned in @interface_info the
478    /// `org.freedesktop.DBus.Error.UnknownMethod` resp.
479    /// `org.freedesktop.DBus.Error.InvalidArgs` errors
480    /// are returned to the caller.
481    ///
482    /// It is considered a programming error if the
483    /// #GDBusInterfaceGetPropertyFunc function in @vtable returns a
484    /// #GVariant of incorrect type.
485    ///
486    /// If an existing callback is already registered at @object_path and
487    /// @interface_name, then @error is set to [`IOErrorEnum::Exists`][crate::IOErrorEnum::Exists].
488    ///
489    /// GDBus automatically implements the standard D-Bus interfaces
490    /// org.freedesktop.DBus.Properties, org.freedesktop.DBus.Introspectable
491    /// and org.freedesktop.Peer, so you don't have to implement those for the
492    /// objects you export. You can implement org.freedesktop.DBus.Properties
493    /// yourself, e.g. to handle getting and setting of properties asynchronously.
494    ///
495    /// Note that the reference count on @interface_info will be
496    /// incremented by 1 (unless allocated statically, e.g. if the
497    /// reference count is -1, see g_dbus_interface_info_ref()) for as long
498    /// as the object is exported. Also note that @vtable will be copied.
499    ///
500    /// See this [server][`DBusConnection`][crate::DBusConnection]#an-example-d-bus-server]
501    /// for an example of how to use this method.
502    /// ## `object_path`
503    /// the object path to register at
504    /// ## `interface_info`
505    /// introspection data for the interface
506    /// ## `vtable`
507    /// a #GDBusInterfaceVTable to call into or [`None`]
508    ///
509    /// # Returns
510    ///
511    /// 0 if @error is set, otherwise a registration id (never 0)
512    ///     that can be used with g_dbus_connection_unregister_object()
513    #[doc(alias = "g_dbus_connection_register_object")]
514    #[doc(alias = "g_dbus_connection_register_object_with_closures")]
515    pub fn register_object<'a>(
516        &'a self,
517        object_path: &'a str,
518        interface_info: &'a DBusInterfaceInfo,
519    ) -> RegistrationBuilder<'a> {
520        RegistrationBuilder {
521            connection: self,
522            object_path,
523            interface_info,
524            method_call: None,
525            get_property: None,
526            set_property: None,
527        }
528    }
529
530    /// Unregisters an object.
531    /// ## `registration_id`
532    /// a registration id obtained from
533    ///     g_dbus_connection_register_object()
534    ///
535    /// # Returns
536    ///
537    /// [`true`] if the object was unregistered, [`false`] otherwise
538    #[doc(alias = "g_dbus_connection_unregister_object")]
539    pub fn unregister_object(
540        &self,
541        registration_id: RegistrationId,
542    ) -> Result<(), glib::error::BoolError> {
543        unsafe {
544            glib::result_from_gboolean!(
545                ffi::g_dbus_connection_unregister_object(
546                    self.to_glib_none().0,
547                    registration_id.0.into()
548                ),
549                "Failed to unregister D-Bus object"
550            )
551        }
552    }
553
554    /// Exports @action_group on @self at @object_path.
555    ///
556    /// The implemented D-Bus API should be considered private.  It is
557    /// subject to change in the future.
558    ///
559    /// A given object path can only have one action group exported on it.
560    /// If this constraint is violated, the export will fail and 0 will be
561    /// returned (with @error set accordingly).
562    ///
563    /// You can unexport the action group using
564    /// [`unexport_action_group()`][Self::unexport_action_group()] with the return value of
565    /// this function.
566    ///
567    /// The thread default main context is taken at the time of this call.
568    /// All incoming action activations and state change requests are
569    /// reported from this context.  Any changes on the action group that
570    /// cause it to emit signals must also come from this same context.
571    /// Since incoming action activations and state change requests are
572    /// rather likely to cause changes on the action group, this effectively
573    /// limits a given action group to being exported from only one main
574    /// context.
575    /// ## `object_path`
576    /// a D-Bus object path
577    /// ## `action_group`
578    /// an action group
579    ///
580    /// # Returns
581    ///
582    /// the ID of the export (never zero), or 0 in case of failure
583    #[doc(alias = "g_dbus_connection_export_action_group")]
584    pub fn export_action_group<P: IsA<ActionGroup>>(
585        &self,
586        object_path: &str,
587        action_group: &P,
588    ) -> Result<ActionGroupExportId, glib::Error> {
589        unsafe {
590            let mut error = std::ptr::null_mut();
591            let id = ffi::g_dbus_connection_export_action_group(
592                self.to_glib_none().0,
593                object_path.to_glib_none().0,
594                action_group.as_ref().to_glib_none().0,
595                &mut error,
596            );
597            if error.is_null() {
598                Ok(ActionGroupExportId(NonZeroU32::new_unchecked(id)))
599            } else {
600                Err(from_glib_full(error))
601            }
602        }
603    }
604
605    /// Reverses the effect of a previous call to
606    /// [`export_action_group()`][Self::export_action_group()].
607    ///
608    /// It is an error to call this function with an ID that wasn’t returned from
609    /// [`export_action_group()`][Self::export_action_group()] or to call it with the same
610    /// ID more than once.
611    /// ## `export_id`
612    /// the ID from [`export_action_group()`][Self::export_action_group()]
613    #[doc(alias = "g_dbus_connection_unexport_action_group")]
614    pub fn unexport_action_group(&self, export_id: ActionGroupExportId) {
615        unsafe {
616            ffi::g_dbus_connection_unexport_action_group(self.to_glib_none().0, export_id.0.into());
617        }
618    }
619
620    /// Exports @menu on @self at @object_path.
621    ///
622    /// The implemented D-Bus API should be considered private.
623    /// It is subject to change in the future.
624    ///
625    /// An object path can only have one menu model exported on it. If this
626    /// constraint is violated, the export will fail and 0 will be
627    /// returned (with @error set accordingly).
628    ///
629    /// Exporting menus with sections containing more than
630    /// `G_MENU_EXPORTER_MAX_SECTION_SIZE` items is not supported and results in
631    /// undefined behavior.
632    ///
633    /// You can unexport the menu model using
634    /// g_dbus_connection_unexport_menu_model() with the return value of
635    /// this function.
636    /// ## `object_path`
637    /// a D-Bus object path
638    /// ## `menu`
639    /// a #GMenuModel
640    ///
641    /// # Returns
642    ///
643    /// the ID of the export (never zero), or 0 in case of failure
644    #[doc(alias = "g_dbus_connection_export_menu_model")]
645    pub fn export_menu_model<P: IsA<MenuModel>>(
646        &self,
647        object_path: &str,
648        menu: &P,
649    ) -> Result<MenuModelExportId, glib::Error> {
650        unsafe {
651            let mut error = std::ptr::null_mut();
652            let id = ffi::g_dbus_connection_export_menu_model(
653                self.to_glib_none().0,
654                object_path.to_glib_none().0,
655                menu.as_ref().to_glib_none().0,
656                &mut error,
657            );
658            if error.is_null() {
659                Ok(MenuModelExportId(NonZeroU32::new_unchecked(id)))
660            } else {
661                Err(from_glib_full(error))
662            }
663        }
664    }
665
666    /// Reverses the effect of a previous call to
667    /// g_dbus_connection_export_menu_model().
668    ///
669    /// It is an error to call this function with an ID that wasn't returned
670    /// from g_dbus_connection_export_menu_model() or to call it with the
671    /// same ID more than once.
672    /// ## `export_id`
673    /// the ID from g_dbus_connection_export_menu_model()
674    #[doc(alias = "g_dbus_connection_unexport_menu_model")]
675    pub fn unexport_menu_model(&self, export_id: MenuModelExportId) {
676        unsafe {
677            ffi::g_dbus_connection_unexport_menu_model(self.to_glib_none().0, export_id.0.into());
678        }
679    }
680
681    /// Adds a message filter. Filters are handlers that are run on all
682    /// incoming and outgoing messages, prior to standard dispatch. Filters
683    /// are run in the order that they were added.  The same handler can be
684    /// added as a filter more than once, in which case it will be run more
685    /// than once.  Filters added during a filter callback won't be run on
686    /// the message being processed. Filter functions are allowed to modify
687    /// and even drop messages.
688    ///
689    /// Note that filters are run in a dedicated message handling thread so
690    /// they can't block and, generally, can't do anything but signal a
691    /// worker thread. Also note that filters are rarely needed - use API
692    /// such as g_dbus_connection_send_message_with_reply(),
693    /// g_dbus_connection_signal_subscribe() or g_dbus_connection_call() instead.
694    ///
695    /// If a filter consumes an incoming message the message is not
696    /// dispatched anywhere else - not even the standard dispatch machinery
697    /// (that API such as g_dbus_connection_signal_subscribe() and
698    /// g_dbus_connection_send_message_with_reply() relies on) will see the
699    /// message. Similarly, if a filter consumes an outgoing message, the
700    /// message will not be sent to the other peer.
701    ///
702    /// If @user_data_free_func is non-[`None`], it will be called (in the
703    /// thread-default main context of the thread you are calling this
704    /// method from) at some point after @user_data is no longer
705    /// needed. (It is not guaranteed to be called synchronously when the
706    /// filter is removed, and may be called after @self has been
707    /// destroyed.)
708    /// ## `filter_function`
709    /// a filter function
710    ///
711    /// # Returns
712    ///
713    /// a filter identifier that can be used with
714    ///     g_dbus_connection_remove_filter()
715    #[doc(alias = "g_dbus_connection_add_filter")]
716    pub fn add_filter<
717        P: Fn(&DBusConnection, &DBusMessage, bool) -> Option<DBusMessage> + 'static,
718    >(
719        &self,
720        filter_function: P,
721    ) -> FilterId {
722        let filter_function_data: Box_<P> = Box_::new(filter_function);
723        unsafe extern "C" fn filter_function_func<
724            P: Fn(&DBusConnection, &DBusMessage, bool) -> Option<DBusMessage> + 'static,
725        >(
726            connection: *mut ffi::GDBusConnection,
727            message: *mut ffi::GDBusMessage,
728            incoming: glib::ffi::gboolean,
729            user_data: glib::ffi::gpointer,
730        ) -> *mut ffi::GDBusMessage {
731            let connection = from_glib_borrow(connection);
732            let message = from_glib_full(message);
733            let incoming = from_glib(incoming);
734            let callback: &P = &*(user_data as *mut _);
735            let res = (*callback)(&connection, &message, incoming);
736            res.into_glib_ptr()
737        }
738        let filter_function = Some(filter_function_func::<P> as _);
739        unsafe extern "C" fn user_data_free_func_func<
740            P: Fn(&DBusConnection, &DBusMessage, bool) -> Option<DBusMessage> + 'static,
741        >(
742            data: glib::ffi::gpointer,
743        ) {
744            let _callback: Box_<P> = Box_::from_raw(data as *mut _);
745        }
746        let destroy_call3 = Some(user_data_free_func_func::<P> as _);
747        let super_callback0: Box_<P> = filter_function_data;
748        unsafe {
749            let id = ffi::g_dbus_connection_add_filter(
750                self.to_glib_none().0,
751                filter_function,
752                Box_::into_raw(super_callback0) as *mut _,
753                destroy_call3,
754            );
755            FilterId(NonZeroU32::new_unchecked(id))
756        }
757    }
758
759    /// Removes a filter.
760    ///
761    /// Note that since filters run in a different thread, there is a race
762    /// condition where it is possible that the filter will be running even
763    /// after calling g_dbus_connection_remove_filter(), so you cannot just
764    /// free data that the filter might be using. Instead, you should pass
765    /// a #GDestroyNotify to g_dbus_connection_add_filter(), which will be
766    /// called when it is guaranteed that the data is no longer needed.
767    /// ## `filter_id`
768    /// an identifier obtained from g_dbus_connection_add_filter()
769    #[doc(alias = "g_dbus_connection_remove_filter")]
770    pub fn remove_filter(&self, filter_id: FilterId) {
771        unsafe {
772            ffi::g_dbus_connection_remove_filter(self.to_glib_none().0, filter_id.0.into());
773        }
774    }
775
776    // rustdoc-stripper-ignore-next
777    /// Subscribe to a D-Bus signal.
778    ///
779    /// See [`Self::signal_subscribe`] for arguments.
780    ///
781    /// Return a signal subscription which keeps a reference to this D-Bus
782    /// connection and unsubscribes from the signal when dropped.
783    ///
784    /// To avoid reference cycles you may wish to downgrade the returned
785    /// subscription to a weak one with [`SignalSubscription::downgrade`].
786    #[must_use]
787    pub fn subscribe_to_signal<P: Fn(DBusSignalRef) + 'static>(
788        &self,
789        sender: Option<&str>,
790        interface_name: Option<&str>,
791        member: Option<&str>,
792        object_path: Option<&str>,
793        arg0: Option<&str>,
794        flags: DBusSignalFlags,
795        callback: P,
796    ) -> SignalSubscription {
797        #[allow(deprecated)]
798        let id = self.signal_subscribe(
799            sender,
800            interface_name,
801            member,
802            object_path,
803            arg0,
804            flags,
805            move |connection, sender_name, object_path, interface_name, signal_name, parameters| {
806                callback(DBusSignalRef {
807                    connection,
808                    sender_name,
809                    object_path,
810                    interface_name,
811                    signal_name,
812                    parameters,
813                });
814            },
815        );
816        SignalSubscription(self.clone(), Some(id))
817    }
818
819    /// Subscribes to signals on @self and invokes @callback whenever
820    /// the signal is received. Note that @callback will be invoked in the
821    /// thread-default main context (see [`glib::MainContext::push_thread_default()`][crate::glib::MainContext::push_thread_default()])
822    /// of the thread you are calling this method from.
823    ///
824    /// If @self is not a message bus connection, @sender must be
825    /// [`None`].
826    ///
827    /// If @sender is a well-known name note that @callback is invoked with
828    /// the unique name for the owner of @sender, not the well-known name
829    /// as one would expect. This is because the message bus rewrites the
830    /// name. As such, to avoid certain race conditions, users should be
831    /// tracking the name owner of the well-known name and use that when
832    /// processing the received signal.
833    ///
834    /// If one of [`DBusSignalFlags::MATCH_ARG0_NAMESPACE`][crate::DBusSignalFlags::MATCH_ARG0_NAMESPACE] or
835    /// [`DBusSignalFlags::MATCH_ARG0_PATH`][crate::DBusSignalFlags::MATCH_ARG0_PATH] are given, @arg0 is
836    /// interpreted as part of a namespace or path.  The first argument
837    /// of a signal is matched against that part as specified by D-Bus.
838    ///
839    /// If @user_data_free_func is non-[`None`], it will be called (in the
840    /// thread-default main context of the thread you are calling this
841    /// method from) at some point after @user_data is no longer
842    /// needed. (It is not guaranteed to be called synchronously when the
843    /// signal is unsubscribed from, and may be called after @self
844    /// has been destroyed.)
845    ///
846    /// As @callback is potentially invoked in a different thread from where it’s
847    /// emitted, it’s possible for this to happen after
848    /// g_dbus_connection_signal_unsubscribe() has been called in another thread.
849    /// Due to this, @user_data should have a strong reference which is freed with
850    /// @user_data_free_func, rather than pointing to data whose lifecycle is tied
851    /// to the signal subscription. For example, if a #GObject is used to store the
852    /// subscription ID from g_dbus_connection_signal_subscribe(), a strong reference
853    /// to that #GObject must be passed to @user_data, and g_object_unref() passed to
854    /// @user_data_free_func. You are responsible for breaking the resulting
855    /// reference count cycle by explicitly unsubscribing from the signal when
856    /// dropping the last external reference to the #GObject. Alternatively, a weak
857    /// reference may be used.
858    ///
859    /// It is guaranteed that if you unsubscribe from a signal using
860    /// g_dbus_connection_signal_unsubscribe() from the same thread which made the
861    /// corresponding g_dbus_connection_signal_subscribe() call, @callback will not
862    /// be invoked after g_dbus_connection_signal_unsubscribe() returns.
863    ///
864    /// The returned subscription identifier is an opaque value which is guaranteed
865    /// to never be zero.
866    ///
867    /// This function can never fail.
868    /// ## `sender`
869    /// sender name to match on (unique or well-known name)
870    ///     or [`None`] to listen from all senders
871    /// ## `interface_name`
872    /// D-Bus interface name to match on or [`None`] to
873    ///     match on all interfaces
874    /// ## `member`
875    /// D-Bus signal name to match on or [`None`] to match on
876    ///     all signals
877    /// ## `object_path`
878    /// object path to match on or [`None`] to match on
879    ///     all object paths
880    /// ## `arg0`
881    /// contents of first string argument to match on or [`None`]
882    ///     to match on all kinds of arguments
883    /// ## `flags`
884    /// #GDBusSignalFlags describing how arg0 is used in subscribing to the
885    ///     signal
886    /// ## `callback`
887    /// callback to invoke when there is a signal matching the requested data
888    ///
889    /// # Returns
890    ///
891    /// a subscription identifier that can be used with g_dbus_connection_signal_unsubscribe()
892    #[doc(alias = "g_dbus_connection_signal_subscribe")]
893    #[allow(clippy::too_many_arguments)]
894    #[deprecated(note = "Prefer subscribe_to_signal")]
895    pub fn signal_subscribe<
896        P: Fn(&DBusConnection, &str, &str, &str, &str, &glib::Variant) + 'static,
897    >(
898        &self,
899        sender: Option<&str>,
900        interface_name: Option<&str>,
901        member: Option<&str>,
902        object_path: Option<&str>,
903        arg0: Option<&str>,
904        flags: DBusSignalFlags,
905        callback: P,
906    ) -> SignalSubscriptionId {
907        let callback_data: Box_<P> = Box_::new(callback);
908        unsafe extern "C" fn callback_func<
909            P: Fn(&DBusConnection, &str, &str, &str, &str, &glib::Variant) + 'static,
910        >(
911            connection: *mut ffi::GDBusConnection,
912            sender_name: *const libc::c_char,
913            object_path: *const libc::c_char,
914            interface_name: *const libc::c_char,
915            signal_name: *const libc::c_char,
916            parameters: *mut glib::ffi::GVariant,
917            user_data: glib::ffi::gpointer,
918        ) {
919            let connection = from_glib_borrow(connection);
920            let sender_name: Borrowed<glib::GString> = from_glib_borrow(sender_name);
921            let object_path: Borrowed<glib::GString> = from_glib_borrow(object_path);
922            let interface_name: Borrowed<glib::GString> = from_glib_borrow(interface_name);
923            let signal_name: Borrowed<glib::GString> = from_glib_borrow(signal_name);
924            let parameters = from_glib_borrow(parameters);
925            let callback: &P = &*(user_data as *mut _);
926            (*callback)(
927                &connection,
928                sender_name.as_str(),
929                object_path.as_str(),
930                interface_name.as_str(),
931                signal_name.as_str(),
932                &parameters,
933            );
934        }
935        let callback = Some(callback_func::<P> as _);
936        unsafe extern "C" fn user_data_free_func_func<
937            P: Fn(&DBusConnection, &str, &str, &str, &str, &glib::Variant) + 'static,
938        >(
939            data: glib::ffi::gpointer,
940        ) {
941            let _callback: Box_<P> = Box_::from_raw(data as *mut _);
942        }
943        let destroy_call9 = Some(user_data_free_func_func::<P> as _);
944        let super_callback0: Box_<P> = callback_data;
945        unsafe {
946            let id = ffi::g_dbus_connection_signal_subscribe(
947                self.to_glib_none().0,
948                sender.to_glib_none().0,
949                interface_name.to_glib_none().0,
950                member.to_glib_none().0,
951                object_path.to_glib_none().0,
952                arg0.to_glib_none().0,
953                flags.into_glib(),
954                callback,
955                Box_::into_raw(super_callback0) as *mut _,
956                destroy_call9,
957            );
958            SignalSubscriptionId(NonZeroU32::new_unchecked(id))
959        }
960    }
961
962    /// Unsubscribes from signals.
963    ///
964    /// Note that there may still be D-Bus traffic to process (relating to this
965    /// signal subscription) in the current thread-default #GMainContext after this
966    /// function has returned. You should continue to iterate the #GMainContext
967    /// until the #GDestroyNotify function passed to
968    /// g_dbus_connection_signal_subscribe() is called, in order to avoid memory
969    /// leaks through callbacks queued on the #GMainContext after it’s stopped being
970    /// iterated.
971    /// Alternatively, any idle source with a priority lower than `G_PRIORITY_DEFAULT`
972    /// that was scheduled after unsubscription, also indicates that all resources
973    /// of this subscription are released.
974    /// ## `subscription_id`
975    /// a subscription id obtained from
976    ///     g_dbus_connection_signal_subscribe()
977    #[doc(alias = "g_dbus_connection_signal_unsubscribe")]
978    #[deprecated(note = "Prefer subscribe_to_signal")]
979    pub fn signal_unsubscribe(&self, subscription_id: SignalSubscriptionId) {
980        unsafe {
981            ffi::g_dbus_connection_signal_unsubscribe(
982                self.to_glib_none().0,
983                subscription_id.0.into(),
984            );
985        }
986    }
987
988    // rustdoc-stripper-ignore-next
989    /// Subscribe to a D-Bus signal and receive signal emissions as a stream.
990    ///
991    /// See [`Self::signal_subscribe`] for arguments.  `map_signal` maps the
992    /// received signal to the stream's element.
993    ///
994    /// The returned stream holds a strong reference to this D-Bus connection,
995    /// and unsubscribes from the signal when dropped. To avoid reference cycles
996    /// you may wish to downgrade the returned stream to hold only weak
997    /// reference to the connection using [`SubscribedSignalStream::downgrade`].
998    ///
999    /// After invoking `map_signal` the stream threads incoming signals through
1000    /// an unbounded channel.  Hence, memory consumption will keep increasing
1001    /// as long as the stream consumer does not keep up with signal emissions.
1002    /// If you need to perform expensive processing in response to signals it's
1003    /// therefore recommended to insert an extra buffering and if the buffer
1004    /// overruns, either fail drop the entire stream, or drop individual signal
1005    /// emissions until the buffer has space again.
1006    pub fn receive_signal<T: 'static, F: Fn(DBusSignalRef) -> T + 'static>(
1007        &self,
1008        sender: Option<&str>,
1009        interface_name: Option<&str>,
1010        member: Option<&str>,
1011        object_path: Option<&str>,
1012        arg0: Option<&str>,
1013        flags: DBusSignalFlags,
1014        map_signal: F,
1015    ) -> SubscribedSignalStream<SignalSubscription, impl Stream<Item = T> + use<T, F>> {
1016        let (tx, rx) = mpsc::unbounded();
1017        let subscription = self.subscribe_to_signal(
1018            sender,
1019            interface_name,
1020            member,
1021            object_path,
1022            arg0,
1023            flags,
1024            move |signal| {
1025                // Just ignore send errors: if the receiver is dropped, the
1026                // signal subscription is dropped too, so the callback won't
1027                // be invoked anymore.
1028                let _ = tx.unbounded_send(map_signal(signal));
1029            },
1030        );
1031        SubscribedSignalStream {
1032            subscription,
1033            stream: rx,
1034        }
1035    }
1036
1037    // rustdoc-stripper-ignore-next
1038    /// Subscribe to a D-Bus signal and receive signal parameters as a stream.
1039    ///
1040    /// Like [`Self::receive_signal`] (which see for more information), but
1041    /// automatically decodes the emitted signal parameters to type `T`.
1042    /// If decoding fails the corresponding variant type error is sent
1043    /// downstream.
1044    pub fn receive_signal_parameters<T>(
1045        &self,
1046        sender: Option<&str>,
1047        interface_name: Option<&str>,
1048        member: Option<&str>,
1049        object_path: Option<&str>,
1050        arg0: Option<&str>,
1051        flags: DBusSignalFlags,
1052    ) -> SubscribedSignalStream<
1053        SignalSubscription,
1054        impl Stream<Item = Result<T, VariantTypeMismatchError>> + use<T>,
1055    >
1056    where
1057        T: FromVariant + 'static,
1058    {
1059        self.receive_signal(
1060            sender,
1061            interface_name,
1062            member,
1063            object_path,
1064            arg0,
1065            flags,
1066            |signal| signal.parameters.try_get(),
1067        )
1068    }
1069}