1use 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
23pub struct MethodCallBuilder<'a, T> {
26 registration: RegistrationBuilder<'a>,
27 capture_type: PhantomData<T>,
28}
29
30impl<'a, T: DBusMethodCall> MethodCallBuilder<'a, T> {
31 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 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 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#[derive(Debug)]
137pub struct SignalSubscription(DBusConnection, Option<SignalSubscriptionId>);
138
139impl SignalSubscription {
140 #[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#[derive(Debug)]
169pub struct WeakSignalSubscription(WeakRef<DBusConnection>, Option<SignalSubscriptionId>);
170
171impl WeakSignalSubscription {
172 #[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#[derive(Debug, Copy, Clone)]
196pub struct DBusSignalRef<'a> {
197 pub connection: &'a DBusConnection,
200 pub sender_name: &'a str,
203 pub object_path: &'a str,
206 pub interface_name: &'a str,
209 pub signal_name: &'a str,
212 pub parameters: &'a glib::Variant,
215}
216
217pin_project! {
218 #[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 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 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#[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 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 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 #[doc(alias = "g_dbus_connection_register_object")]
464 #[doc(alias = "g_dbus_connection_register_object_with_closures")]
465 pub fn register_object<'a>(
466 &'a self,
467 object_path: &'a str,
468 interface_info: &'a DBusInterfaceInfo,
469 ) -> RegistrationBuilder<'a> {
470 RegistrationBuilder {
471 connection: self,
472 object_path,
473 interface_info,
474 method_call: None,
475 get_property: None,
476 set_property: None,
477 }
478 }
479
480 #[doc(alias = "g_dbus_connection_unregister_object")]
481 pub fn unregister_object(
482 &self,
483 registration_id: RegistrationId,
484 ) -> Result<(), glib::error::BoolError> {
485 unsafe {
486 glib::result_from_gboolean!(
487 ffi::g_dbus_connection_unregister_object(
488 self.to_glib_none().0,
489 registration_id.0.into()
490 ),
491 "Failed to unregister D-Bus object"
492 )
493 }
494 }
495
496 #[doc(alias = "g_dbus_connection_export_action_group")]
497 pub fn export_action_group<P: IsA<ActionGroup>>(
498 &self,
499 object_path: &str,
500 action_group: &P,
501 ) -> Result<ActionGroupExportId, glib::Error> {
502 unsafe {
503 let mut error = std::ptr::null_mut();
504 let id = ffi::g_dbus_connection_export_action_group(
505 self.to_glib_none().0,
506 object_path.to_glib_none().0,
507 action_group.as_ref().to_glib_none().0,
508 &mut error,
509 );
510 if error.is_null() {
511 Ok(ActionGroupExportId(NonZeroU32::new_unchecked(id)))
512 } else {
513 Err(from_glib_full(error))
514 }
515 }
516 }
517
518 #[doc(alias = "g_dbus_connection_unexport_action_group")]
519 pub fn unexport_action_group(&self, export_id: ActionGroupExportId) {
520 unsafe {
521 ffi::g_dbus_connection_unexport_action_group(self.to_glib_none().0, export_id.0.into());
522 }
523 }
524
525 #[doc(alias = "g_dbus_connection_export_menu_model")]
526 pub fn export_menu_model<P: IsA<MenuModel>>(
527 &self,
528 object_path: &str,
529 menu: &P,
530 ) -> Result<MenuModelExportId, glib::Error> {
531 unsafe {
532 let mut error = std::ptr::null_mut();
533 let id = ffi::g_dbus_connection_export_menu_model(
534 self.to_glib_none().0,
535 object_path.to_glib_none().0,
536 menu.as_ref().to_glib_none().0,
537 &mut error,
538 );
539 if error.is_null() {
540 Ok(MenuModelExportId(NonZeroU32::new_unchecked(id)))
541 } else {
542 Err(from_glib_full(error))
543 }
544 }
545 }
546
547 #[doc(alias = "g_dbus_connection_unexport_menu_model")]
548 pub fn unexport_menu_model(&self, export_id: MenuModelExportId) {
549 unsafe {
550 ffi::g_dbus_connection_unexport_menu_model(self.to_glib_none().0, export_id.0.into());
551 }
552 }
553
554 #[doc(alias = "g_dbus_connection_add_filter")]
555 pub fn add_filter<
556 P: Fn(&DBusConnection, &DBusMessage, bool) -> Option<DBusMessage> + 'static,
557 >(
558 &self,
559 filter_function: P,
560 ) -> FilterId {
561 let filter_function_data: Box_<P> = Box_::new(filter_function);
562 unsafe extern "C" fn filter_function_func<
563 P: Fn(&DBusConnection, &DBusMessage, bool) -> Option<DBusMessage> + 'static,
564 >(
565 connection: *mut ffi::GDBusConnection,
566 message: *mut ffi::GDBusMessage,
567 incoming: glib::ffi::gboolean,
568 user_data: glib::ffi::gpointer,
569 ) -> *mut ffi::GDBusMessage {
570 let connection = from_glib_borrow(connection);
571 let message = from_glib_full(message);
572 let incoming = from_glib(incoming);
573 let callback: &P = &*(user_data as *mut _);
574 let res = (*callback)(&connection, &message, incoming);
575 res.into_glib_ptr()
576 }
577 let filter_function = Some(filter_function_func::<P> as _);
578 unsafe extern "C" fn user_data_free_func_func<
579 P: Fn(&DBusConnection, &DBusMessage, bool) -> Option<DBusMessage> + 'static,
580 >(
581 data: glib::ffi::gpointer,
582 ) {
583 let _callback: Box_<P> = Box_::from_raw(data as *mut _);
584 }
585 let destroy_call3 = Some(user_data_free_func_func::<P> as _);
586 let super_callback0: Box_<P> = filter_function_data;
587 unsafe {
588 let id = ffi::g_dbus_connection_add_filter(
589 self.to_glib_none().0,
590 filter_function,
591 Box_::into_raw(super_callback0) as *mut _,
592 destroy_call3,
593 );
594 FilterId(NonZeroU32::new_unchecked(id))
595 }
596 }
597
598 #[doc(alias = "g_dbus_connection_remove_filter")]
599 pub fn remove_filter(&self, filter_id: FilterId) {
600 unsafe {
601 ffi::g_dbus_connection_remove_filter(self.to_glib_none().0, filter_id.0.into());
602 }
603 }
604
605 #[must_use]
616 pub fn subscribe_to_signal<P: Fn(DBusSignalRef) + 'static>(
617 &self,
618 sender: Option<&str>,
619 interface_name: Option<&str>,
620 member: Option<&str>,
621 object_path: Option<&str>,
622 arg0: Option<&str>,
623 flags: DBusSignalFlags,
624 callback: P,
625 ) -> SignalSubscription {
626 #[allow(deprecated)]
627 let id = self.signal_subscribe(
628 sender,
629 interface_name,
630 member,
631 object_path,
632 arg0,
633 flags,
634 move |connection, sender_name, object_path, interface_name, signal_name, parameters| {
635 callback(DBusSignalRef {
636 connection,
637 sender_name,
638 object_path,
639 interface_name,
640 signal_name,
641 parameters,
642 });
643 },
644 );
645 SignalSubscription(self.clone(), Some(id))
646 }
647
648 #[doc(alias = "g_dbus_connection_signal_subscribe")]
649 #[allow(clippy::too_many_arguments)]
650 #[deprecated(note = "Prefer subscribe_to_signal")]
651 pub fn signal_subscribe<
652 P: Fn(&DBusConnection, &str, &str, &str, &str, &glib::Variant) + 'static,
653 >(
654 &self,
655 sender: Option<&str>,
656 interface_name: Option<&str>,
657 member: Option<&str>,
658 object_path: Option<&str>,
659 arg0: Option<&str>,
660 flags: DBusSignalFlags,
661 callback: P,
662 ) -> SignalSubscriptionId {
663 let callback_data: Box_<P> = Box_::new(callback);
664 unsafe extern "C" fn callback_func<
665 P: Fn(&DBusConnection, &str, &str, &str, &str, &glib::Variant) + 'static,
666 >(
667 connection: *mut ffi::GDBusConnection,
668 sender_name: *const libc::c_char,
669 object_path: *const libc::c_char,
670 interface_name: *const libc::c_char,
671 signal_name: *const libc::c_char,
672 parameters: *mut glib::ffi::GVariant,
673 user_data: glib::ffi::gpointer,
674 ) {
675 let connection = from_glib_borrow(connection);
676 let sender_name: Borrowed<glib::GString> = from_glib_borrow(sender_name);
677 let object_path: Borrowed<glib::GString> = from_glib_borrow(object_path);
678 let interface_name: Borrowed<glib::GString> = from_glib_borrow(interface_name);
679 let signal_name: Borrowed<glib::GString> = from_glib_borrow(signal_name);
680 let parameters = from_glib_borrow(parameters);
681 let callback: &P = &*(user_data as *mut _);
682 (*callback)(
683 &connection,
684 sender_name.as_str(),
685 object_path.as_str(),
686 interface_name.as_str(),
687 signal_name.as_str(),
688 ¶meters,
689 );
690 }
691 let callback = Some(callback_func::<P> as _);
692 unsafe extern "C" fn user_data_free_func_func<
693 P: Fn(&DBusConnection, &str, &str, &str, &str, &glib::Variant) + 'static,
694 >(
695 data: glib::ffi::gpointer,
696 ) {
697 let _callback: Box_<P> = Box_::from_raw(data as *mut _);
698 }
699 let destroy_call9 = Some(user_data_free_func_func::<P> as _);
700 let super_callback0: Box_<P> = callback_data;
701 unsafe {
702 let id = ffi::g_dbus_connection_signal_subscribe(
703 self.to_glib_none().0,
704 sender.to_glib_none().0,
705 interface_name.to_glib_none().0,
706 member.to_glib_none().0,
707 object_path.to_glib_none().0,
708 arg0.to_glib_none().0,
709 flags.into_glib(),
710 callback,
711 Box_::into_raw(super_callback0) as *mut _,
712 destroy_call9,
713 );
714 SignalSubscriptionId(NonZeroU32::new_unchecked(id))
715 }
716 }
717
718 #[doc(alias = "g_dbus_connection_signal_unsubscribe")]
719 #[deprecated(note = "Prefer subscribe_to_signal")]
720 pub fn signal_unsubscribe(&self, subscription_id: SignalSubscriptionId) {
721 unsafe {
722 ffi::g_dbus_connection_signal_unsubscribe(
723 self.to_glib_none().0,
724 subscription_id.0.into(),
725 );
726 }
727 }
728
729 pub fn receive_signal<T: 'static, F: Fn(DBusSignalRef) -> T + 'static>(
748 &self,
749 sender: Option<&str>,
750 interface_name: Option<&str>,
751 member: Option<&str>,
752 object_path: Option<&str>,
753 arg0: Option<&str>,
754 flags: DBusSignalFlags,
755 map_signal: F,
756 ) -> SubscribedSignalStream<SignalSubscription, impl Stream<Item = T> + use<T, F>> {
757 let (tx, rx) = mpsc::unbounded();
758 let subscription = self.subscribe_to_signal(
759 sender,
760 interface_name,
761 member,
762 object_path,
763 arg0,
764 flags,
765 move |signal| {
766 let _ = tx.unbounded_send(map_signal(signal));
770 },
771 );
772 SubscribedSignalStream {
773 subscription,
774 stream: rx,
775 }
776 }
777
778 pub fn receive_signal_parameters<T>(
786 &self,
787 sender: Option<&str>,
788 interface_name: Option<&str>,
789 member: Option<&str>,
790 object_path: Option<&str>,
791 arg0: Option<&str>,
792 flags: DBusSignalFlags,
793 ) -> SubscribedSignalStream<
794 SignalSubscription,
795 impl Stream<Item = Result<T, VariantTypeMismatchError>> + use<T>,
796 >
797 where
798 T: FromVariant + 'static,
799 {
800 self.receive_signal(
801 sender,
802 interface_name,
803 member,
804 object_path,
805 arg0,
806 flags,
807 |signal| signal.parameters.try_get(),
808 )
809 }
810}