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 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
20pub struct MethodCallBuilder<'a, T> {
23 registration: RegistrationBuilder<'a>,
24 capture_type: PhantomData<T>,
25}
26
27impl<'a, T: DBusMethodCall> MethodCallBuilder<'a, T> {
28 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 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 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#[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 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 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 #[doc(alias = "g_dbus_connection_register_object")]
299 #[doc(alias = "g_dbus_connection_register_object_with_closures")]
300 pub fn register_object<'a>(
301 &'a self,
302 object_path: &'a str,
303 interface_info: &'a DBusInterfaceInfo,
304 ) -> RegistrationBuilder<'a> {
305 RegistrationBuilder {
306 connection: self,
307 object_path,
308 interface_info,
309 method_call: None,
310 get_property: None,
311 set_property: None,
312 }
313 }
314
315 #[doc(alias = "g_dbus_connection_unregister_object")]
316 pub fn unregister_object(
317 &self,
318 registration_id: RegistrationId,
319 ) -> Result<(), glib::error::BoolError> {
320 unsafe {
321 glib::result_from_gboolean!(
322 ffi::g_dbus_connection_unregister_object(
323 self.to_glib_none().0,
324 registration_id.0.into()
325 ),
326 "Failed to unregister D-Bus object"
327 )
328 }
329 }
330
331 #[doc(alias = "g_dbus_connection_export_action_group")]
332 pub fn export_action_group<P: IsA<ActionGroup>>(
333 &self,
334 object_path: &str,
335 action_group: &P,
336 ) -> Result<ActionGroupExportId, glib::Error> {
337 unsafe {
338 let mut error = std::ptr::null_mut();
339 let id = ffi::g_dbus_connection_export_action_group(
340 self.to_glib_none().0,
341 object_path.to_glib_none().0,
342 action_group.as_ref().to_glib_none().0,
343 &mut error,
344 );
345 if error.is_null() {
346 Ok(ActionGroupExportId(NonZeroU32::new_unchecked(id)))
347 } else {
348 Err(from_glib_full(error))
349 }
350 }
351 }
352
353 #[doc(alias = "g_dbus_connection_unexport_action_group")]
354 pub fn unexport_action_group(&self, export_id: ActionGroupExportId) {
355 unsafe {
356 ffi::g_dbus_connection_unexport_action_group(self.to_glib_none().0, export_id.0.into());
357 }
358 }
359
360 #[doc(alias = "g_dbus_connection_export_menu_model")]
361 pub fn export_menu_model<P: IsA<MenuModel>>(
362 &self,
363 object_path: &str,
364 menu: &P,
365 ) -> Result<MenuModelExportId, glib::Error> {
366 unsafe {
367 let mut error = std::ptr::null_mut();
368 let id = ffi::g_dbus_connection_export_menu_model(
369 self.to_glib_none().0,
370 object_path.to_glib_none().0,
371 menu.as_ref().to_glib_none().0,
372 &mut error,
373 );
374 if error.is_null() {
375 Ok(MenuModelExportId(NonZeroU32::new_unchecked(id)))
376 } else {
377 Err(from_glib_full(error))
378 }
379 }
380 }
381
382 #[doc(alias = "g_dbus_connection_unexport_menu_model")]
383 pub fn unexport_menu_model(&self, export_id: MenuModelExportId) {
384 unsafe {
385 ffi::g_dbus_connection_unexport_menu_model(self.to_glib_none().0, export_id.0.into());
386 }
387 }
388
389 #[doc(alias = "g_dbus_connection_add_filter")]
390 pub fn add_filter<
391 P: Fn(&DBusConnection, &DBusMessage, bool) -> Option<DBusMessage> + 'static,
392 >(
393 &self,
394 filter_function: P,
395 ) -> FilterId {
396 let filter_function_data: Box_<P> = Box_::new(filter_function);
397 unsafe extern "C" fn filter_function_func<
398 P: Fn(&DBusConnection, &DBusMessage, bool) -> Option<DBusMessage> + 'static,
399 >(
400 connection: *mut ffi::GDBusConnection,
401 message: *mut ffi::GDBusMessage,
402 incoming: glib::ffi::gboolean,
403 user_data: glib::ffi::gpointer,
404 ) -> *mut ffi::GDBusMessage {
405 let connection = from_glib_borrow(connection);
406 let message = from_glib_full(message);
407 let incoming = from_glib(incoming);
408 let callback: &P = &*(user_data as *mut _);
409 let res = (*callback)(&connection, &message, incoming);
410 res.into_glib_ptr()
411 }
412 let filter_function = Some(filter_function_func::<P> as _);
413 unsafe extern "C" fn user_data_free_func_func<
414 P: Fn(&DBusConnection, &DBusMessage, bool) -> Option<DBusMessage> + 'static,
415 >(
416 data: glib::ffi::gpointer,
417 ) {
418 let _callback: Box_<P> = Box_::from_raw(data as *mut _);
419 }
420 let destroy_call3 = Some(user_data_free_func_func::<P> as _);
421 let super_callback0: Box_<P> = filter_function_data;
422 unsafe {
423 let id = ffi::g_dbus_connection_add_filter(
424 self.to_glib_none().0,
425 filter_function,
426 Box_::into_raw(super_callback0) as *mut _,
427 destroy_call3,
428 );
429 FilterId(NonZeroU32::new_unchecked(id))
430 }
431 }
432
433 #[doc(alias = "g_dbus_connection_remove_filter")]
434 pub fn remove_filter(&self, filter_id: FilterId) {
435 unsafe {
436 ffi::g_dbus_connection_remove_filter(self.to_glib_none().0, filter_id.0.into());
437 }
438 }
439
440 #[doc(alias = "g_dbus_connection_signal_subscribe")]
441 #[allow(clippy::too_many_arguments)]
442 pub fn signal_subscribe<
443 P: Fn(&DBusConnection, &str, &str, &str, &str, &glib::Variant) + 'static,
444 >(
445 &self,
446 sender: Option<&str>,
447 interface_name: Option<&str>,
448 member: Option<&str>,
449 object_path: Option<&str>,
450 arg0: Option<&str>,
451 flags: DBusSignalFlags,
452 callback: P,
453 ) -> SignalSubscriptionId {
454 let callback_data: Box_<P> = Box_::new(callback);
455 unsafe extern "C" fn callback_func<
456 P: Fn(&DBusConnection, &str, &str, &str, &str, &glib::Variant) + 'static,
457 >(
458 connection: *mut ffi::GDBusConnection,
459 sender_name: *const libc::c_char,
460 object_path: *const libc::c_char,
461 interface_name: *const libc::c_char,
462 signal_name: *const libc::c_char,
463 parameters: *mut glib::ffi::GVariant,
464 user_data: glib::ffi::gpointer,
465 ) {
466 let connection = from_glib_borrow(connection);
467 let sender_name: Borrowed<glib::GString> = from_glib_borrow(sender_name);
468 let object_path: Borrowed<glib::GString> = from_glib_borrow(object_path);
469 let interface_name: Borrowed<glib::GString> = from_glib_borrow(interface_name);
470 let signal_name: Borrowed<glib::GString> = from_glib_borrow(signal_name);
471 let parameters = from_glib_borrow(parameters);
472 let callback: &P = &*(user_data as *mut _);
473 (*callback)(
474 &connection,
475 sender_name.as_str(),
476 object_path.as_str(),
477 interface_name.as_str(),
478 signal_name.as_str(),
479 ¶meters,
480 );
481 }
482 let callback = Some(callback_func::<P> as _);
483 unsafe extern "C" fn user_data_free_func_func<
484 P: Fn(&DBusConnection, &str, &str, &str, &str, &glib::Variant) + 'static,
485 >(
486 data: glib::ffi::gpointer,
487 ) {
488 let _callback: Box_<P> = Box_::from_raw(data as *mut _);
489 }
490 let destroy_call9 = Some(user_data_free_func_func::<P> as _);
491 let super_callback0: Box_<P> = callback_data;
492 unsafe {
493 let id = ffi::g_dbus_connection_signal_subscribe(
494 self.to_glib_none().0,
495 sender.to_glib_none().0,
496 interface_name.to_glib_none().0,
497 member.to_glib_none().0,
498 object_path.to_glib_none().0,
499 arg0.to_glib_none().0,
500 flags.into_glib(),
501 callback,
502 Box_::into_raw(super_callback0) as *mut _,
503 destroy_call9,
504 );
505 SignalSubscriptionId(NonZeroU32::new_unchecked(id))
506 }
507 }
508
509 #[doc(alias = "g_dbus_connection_signal_unsubscribe")]
510 pub fn signal_unsubscribe(&self, subscription_id: SignalSubscriptionId) {
511 unsafe {
512 ffi::g_dbus_connection_signal_unsubscribe(
513 self.to_glib_none().0,
514 subscription_id.0.into(),
515 );
516 }
517 }
518}