diff --git a/examples/gio_dbus_register_object/main.rs b/examples/gio_dbus_register_object/main.rs index f3422e232aeb..e5d5302aa767 100644 --- a/examples/gio_dbus_register_object/main.rs +++ b/examples/gio_dbus_register_object/main.rs @@ -40,7 +40,7 @@ enum HelloMethod { impl DBusMethodCall for HelloMethod { fn parse_call( _obj_path: &str, - _interface: &str, + _interface: Option<&str>, method: &str, params: glib::Variant, ) -> Result { @@ -65,7 +65,7 @@ fn on_startup(app: &gio::Application, tx: &Sender) { .register_object("/com/github/gtk_rs/examples/HelloWorld", &example) .typed_method_call::() .invoke_and_return_future_local(|_, sender, call| { - println!("Method call from {sender}"); + println!("Method call from {sender:?}"); async { match call { HelloMethod::Hello(Hello { name }) => { diff --git a/gio/Gir.toml b/gio/Gir.toml index 68a361347600..05c34f6f1a63 100644 --- a/gio/Gir.toml +++ b/gio/Gir.toml @@ -644,7 +644,16 @@ status = "generate" name = "return_error_literal" # glib::ErrorDomain manual = true - + [[object.function]] + name = "get_sender" + [object.function.return] + # https://gitlab.gnome.org/GNOME/glib/-/merge_requests/4414 + nullable = true + [[object.function]] + name = "get_interface_name" + [object.function.return] + # https://gitlab.gnome.org/GNOME/glib/-/merge_requests/4414 + nullable = true [[object]] name = "Gio.DBusProxy" diff --git a/gio/src/auto/dbus_method_invocation.rs b/gio/src/auto/dbus_method_invocation.rs index 4f658a22b03e..4a6c38b50e41 100644 --- a/gio/src/auto/dbus_method_invocation.rs +++ b/gio/src/auto/dbus_method_invocation.rs @@ -33,7 +33,7 @@ impl DBusMethodInvocation { #[doc(alias = "g_dbus_method_invocation_get_interface_name")] #[doc(alias = "get_interface_name")] - pub fn interface_name(&self) -> glib::GString { + pub fn interface_name(&self) -> Option { unsafe { from_glib_none(ffi::g_dbus_method_invocation_get_interface_name( self.to_glib_none().0, @@ -103,7 +103,7 @@ impl DBusMethodInvocation { #[doc(alias = "g_dbus_method_invocation_get_sender")] #[doc(alias = "get_sender")] - pub fn sender(&self) -> glib::GString { + pub fn sender(&self) -> Option { unsafe { from_glib_none(ffi::g_dbus_method_invocation_get_sender( self.to_glib_none().0, diff --git a/gio/src/dbus_connection.rs b/gio/src/dbus_connection.rs index d9b15e98cb1e..2e46f9bc3279 100644 --- a/gio/src/dbus_connection.rs +++ b/gio/src/dbus_connection.rs @@ -11,7 +11,7 @@ use glib::{prelude::*, translate::*}; pub trait DBusMethodCall: Sized { fn parse_call( obj_path: &str, - interface: &str, + interface: Option<&str>, method: &str, params: glib::Variant, ) -> Result; @@ -46,7 +46,7 @@ impl<'a, T: DBusMethodCall> MethodCallBuilder<'a, T> { /// safer interface where the callback returns a result directly. pub fn invoke(self, f: F) -> RegistrationBuilder<'a> where - F: Fn(DBusConnection, &str, T, DBusMethodInvocation) + 'static, + F: Fn(DBusConnection, Option<&str>, T, DBusMethodInvocation) + 'static, { self.registration.method_call( move |connection, sender, obj_path, interface, method, params, invocation| { @@ -74,7 +74,8 @@ impl<'a, T: DBusMethodCall> MethodCallBuilder<'a, T> { /// See [`DBusMethodInvocation::return_result`] for details. pub fn invoke_and_return(self, f: F) -> RegistrationBuilder<'a> where - F: Fn(DBusConnection, &str, T) -> Result, glib::Error> + 'static, + F: Fn(DBusConnection, Option<&str>, T) -> Result, glib::Error> + + 'static, { self.invoke(move |connection, sender, call, invocation| { invocation.return_result(f(connection, sender, call)) @@ -97,7 +98,7 @@ impl<'a, T: DBusMethodCall> MethodCallBuilder<'a, T> { /// See [`DBusMethodInvocation::return_future_local`] for details. pub fn invoke_and_return_future_local(self, f: F) -> RegistrationBuilder<'a> where - F: Fn(DBusConnection, &str, T) -> Fut + 'static, + F: Fn(DBusConnection, Option<&str>, T) -> Fut + 'static, Fut: Future, glib::Error>> + 'static, { self.invoke(move |connection, sender, call, invocation| { @@ -128,18 +129,37 @@ pub struct RegistrationBuilder<'a> { interface_info: &'a DBusInterfaceInfo, #[allow(clippy::type_complexity)] method_call: Option< - Box_, + Box_< + dyn Fn( + DBusConnection, + Option<&str>, + &str, + Option<&str>, + &str, + glib::Variant, + DBusMethodInvocation, + ), + >, >, #[allow(clippy::type_complexity)] - get_property: Option glib::Variant>>, + get_property: + Option, &str, &str, &str) -> glib::Variant>>, #[allow(clippy::type_complexity)] set_property: - Option bool>>, + Option, &str, &str, &str, glib::Variant) -> bool>>, } impl<'a> RegistrationBuilder<'a> { pub fn method_call< - F: Fn(DBusConnection, &str, &str, &str, &str, glib::Variant, DBusMethodInvocation) + 'static, + F: Fn( + DBusConnection, + Option<&str>, + &str, + Option<&str>, + &str, + glib::Variant, + DBusMethodInvocation, + ) + 'static, >( mut self, f: F, @@ -162,7 +182,9 @@ impl<'a> RegistrationBuilder<'a> { } #[doc(alias = "get_property")] - pub fn property glib::Variant + 'static>( + pub fn property< + F: Fn(DBusConnection, Option<&str>, &str, &str, &str) -> glib::Variant + 'static, + >( mut self, f: F, ) -> Self { @@ -171,7 +193,7 @@ impl<'a> RegistrationBuilder<'a> { } pub fn set_property< - F: Fn(DBusConnection, &str, &str, &str, &str, glib::Variant) -> bool + 'static, + F: Fn(DBusConnection, Option<&str>, &str, &str, &str, glib::Variant) -> bool + 'static, >( mut self, f: F, @@ -191,9 +213,9 @@ impl<'a> RegistrationBuilder<'a> { .map(|f| { glib::Closure::new_local(move |args| { let conn = args[0].get::().unwrap(); - let sender = args[1].get::<&str>().unwrap(); + let sender = args[1].get::>().unwrap(); let object_path = args[2].get::<&str>().unwrap(); - let interface_name = args[3].get::<&str>().unwrap(); + let interface_name = args[3].get::>().unwrap(); let method_name = args[4].get::<&str>().unwrap(); let parameters = args[5].get::().unwrap(); let invocation = args[6].get::().unwrap(); @@ -215,7 +237,7 @@ impl<'a> RegistrationBuilder<'a> { .map(|f| { glib::Closure::new_local(move |args| { let conn = args[0].get::().unwrap(); - let sender = args[1].get::<&str>().unwrap(); + let sender = args[1].get::>().unwrap(); let object_path = args[2].get::<&str>().unwrap(); let interface_name = args[3].get::<&str>().unwrap(); let property_name = args[4].get::<&str>().unwrap(); @@ -237,7 +259,7 @@ impl<'a> RegistrationBuilder<'a> { .map(|f| { glib::Closure::new_local(move |args| { let conn = args[0].get::().unwrap(); - let sender = args[1].get::<&str>().unwrap(); + let sender = args[1].get::>().unwrap(); let object_path = args[2].get::<&str>().unwrap(); let interface_name = args[3].get::<&str>().unwrap(); let property_name = args[4].get::<&str>().unwrap(); diff --git a/gio/tests/dbus_peer.rs b/gio/tests/dbus_peer.rs new file mode 100644 index 000000000000..67e66b35bd90 --- /dev/null +++ b/gio/tests/dbus_peer.rs @@ -0,0 +1,157 @@ +// Take a look at the license at the top of the repository in the LICENSE file. + +#[cfg(unix)] +#[test] +fn test_gdbus_peer_connection() { + use gio::{ + glib::{self, VariantTy}, + prelude::*, + DBusConnection, DBusConnectionFlags, DBusNodeInfo, Socket, + }; + use std::os::{fd::IntoRawFd, unix::net::UnixStream}; + + const EXAMPLE_XML: &str = r#" + + + + + + + + +"#; + + pub async fn spawn_server(fd: UnixStream) -> DBusConnection { + let socket = unsafe { Socket::from_fd(fd.into_raw_fd()) }.unwrap(); + let socket_connection = socket.connection_factory_create_connection(); + + let guid = gio::dbus_generate_guid(); + + dbg!("server connecting"); + + let connection = DBusConnection::new_future( + &socket_connection, + Some(&guid), + DBusConnectionFlags::AUTHENTICATION_SERVER + .union(DBusConnectionFlags::DELAY_MESSAGE_PROCESSING), + None, + ) + .await + .unwrap(); + + dbg!("server connected"); + + let interface_info = DBusNodeInfo::for_xml(EXAMPLE_XML) + .unwrap() + .lookup_interface("com.github.gtk_rs") + .unwrap(); + + let _id = connection + .register_object("/com/github/gtk_rs", &interface_info) + .method_call( + |_connection, + _sender, + _object_path, + _interface_name, + _method_name, + parameters, + invocation| { + dbg!( + _sender, + _object_path, + _interface_name, + _method_name, + ¶meters, + &invocation + ); + + let name = parameters.child_get::(0); + invocation.return_value(Some(&(format!("Hello {name}!"),).to_variant())); + }, + ) + .build() + .unwrap(); + + dbg!("server starts message processing"); + + connection.start_message_processing(); + + dbg!("server awaiting calls"); + + connection + } + + pub async fn spawn_client(fd: UnixStream) -> DBusConnection { + let socket_client = unsafe { Socket::from_fd(fd.into_raw_fd()) }.unwrap(); + let socket_connection_client = socket_client.connection_factory_create_connection(); + + dbg!("client connecting"); + + let connection = DBusConnection::new_future( + &socket_connection_client, + None, + DBusConnectionFlags::AUTHENTICATION_CLIENT, + None, + ) + .await + .unwrap(); + + dbg!("client connected"); + + connection + } + + let ctx = glib::MainContext::default(); + + let (x, y) = std::os::unix::net::UnixStream::pair().unwrap(); + + x.set_nonblocking(true).unwrap(); + y.set_nonblocking(true).unwrap(); + + ctx.block_on(async move { + let ctx = glib::MainContext::default(); + + let server = ctx.spawn_local(spawn_server(x)); + let client = ctx.spawn_local(spawn_client(y)); + + let server = server.await.unwrap(); + let client = client.await.unwrap(); + + dbg!("calling method"); + + let result = client + .call_future( + None, + "/com/github/gtk_rs", + "com.github.gtk_rs", + "Hello", + Some(&("World",).into()), + Some(VariantTy::new("(s)").unwrap()), + gio::DBusCallFlags::NONE, + 10000, + ) + .await + .unwrap(); + + dbg!("method called"); + + dbg!(&result); + + dbg!("closing client"); + client.close_future().await.unwrap(); + dbg!("closed client, closing server"); + server.close_future().await.unwrap(); + dbg!("closed server"); + + drop(client); + drop(server); + + assert_eq!(result.child_get::(0), "Hello World!"); + + glib::timeout_future_with_priority( + glib::Priority::LOW, + std::time::Duration::from_millis(50), + ) + .await; + }); +}