Skip to content

Commit 5881a51

Browse files
author
Paolo Borelli
committed
gio: use a builder to register a DBus object
Make DBusConnection::register_object return a RegistrationBuilder so that one can decide which closures to set. Also add an example to demonstrate how this is used.
1 parent ec4c975 commit 5881a51

File tree

4 files changed

+223
-76
lines changed

4 files changed

+223
-76
lines changed

examples/Cargo.toml

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,15 @@ optional = true
1919
[build-dependencies.glib-build-tools]
2020
path = "../glib-build-tools"
2121

22-
2322
[[bin]]
2423
name = "gio_async_tls"
2524
path = "gio_async_tls/main.rs"
2625
required-features = ["async-tls"]
2726

27+
[[bin]]
28+
name = "gio_cancellable_future"
29+
path = "gio_cancellable_future/main.rs"
30+
2831
[[bin]]
2932
name = "gio_futures"
3033
path = "gio_futures/main.rs"
@@ -34,12 +37,12 @@ name = "gio_futures_await"
3437
path = "gio_futures_await/main.rs"
3538

3639
[[bin]]
37-
name = "gio_task"
38-
path = "gio_task/main.rs"
40+
name = "gio_dbus_register_object"
41+
path = "gio_dbus_register_object/main.rs"
3942

4043
[[bin]]
41-
name = "gio_cancellable_future"
42-
path = "gio_cancellable_future/main.rs"
44+
name = "gio_task"
45+
path = "gio_task/main.rs"
4346

4447
[[bin]]
4548
name = "object_subclass"

examples/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ Consists of various examples of how to use the `gtk-rs-core` libraries. Note tha
55
- [GIO Async TLS](./gio_async_tls/)
66
- [GIO Futures](./gio_futures/)
77
- [GIO Futures Await](./gio_futures_await/)
8+
- [GIO DBus Register Object](./gio_dbus_register_object/)
89
- [GIO Task](./gio_task/)
910
- [GIO Resources](./resources)
1011
- [Object Subclassing](./object_subclass)
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
use gio::prelude::*;
2+
use std::sync::mpsc::{channel, Receiver, Sender};
3+
4+
const EXAMPLE_XML: &str = r#"
5+
<node>
6+
<interface name='com.github.gtk_rs.examples.HelloWorld'>
7+
<method name='Hello'>
8+
<arg type='s' name='name' direction='in'/>
9+
<arg type='s' name='greet' direction='out'/>
10+
</method>
11+
</interface>
12+
</node>
13+
"#;
14+
15+
fn on_startup(app: &gio::Application, tx: &Sender<gio::RegistrationId>) {
16+
let connection = app.dbus_connection().expect("connection");
17+
18+
let example = gio::DBusNodeInfo::for_xml(EXAMPLE_XML)
19+
.ok()
20+
.and_then(|e| e.lookup_interface("com.github.gtk_rs.examples.HelloWorld"))
21+
.expect("Example interface");
22+
23+
if let Ok(id) = connection
24+
.register_object("/com/github/gtk_rs/examples/HelloWorld", &example)
25+
.method_call(
26+
glib::clone!(@strong app => move |_, _, _, _, method, params, invocation| {
27+
match method {
28+
"Hello" => {
29+
if let Some((name,)) = <(String,)>::from_variant(&params) {
30+
let greet = format!("Hello {name}!");
31+
println!("{greet}");
32+
invocation.return_value(Some(&(greet,).to_variant()));
33+
} else {
34+
invocation.return_error(gio::IOErrorEnum::Failed, "Invalid parameters");
35+
}
36+
}
37+
_ => unreachable!(),
38+
}
39+
app.quit();
40+
}),
41+
)
42+
.build()
43+
{
44+
println!("Registered object");
45+
tx.send(id).unwrap();
46+
} else {
47+
eprintln!("Could not register object");
48+
}
49+
}
50+
51+
fn on_shutdown(app: &gio::Application, rx: &Receiver<gio::RegistrationId>) {
52+
let connection = app.dbus_connection().expect("connection");
53+
if let Ok(registration_id) = rx.try_recv() {
54+
if connection.unregister_object(registration_id).is_ok() {
55+
println!("Unregistered object");
56+
} else {
57+
eprintln!("Could not unregister object");
58+
}
59+
}
60+
}
61+
62+
fn main() -> glib::ExitCode {
63+
let app = gio::Application::builder()
64+
.application_id("com.github.gtk-rs.examples.RegisterDBusObject")
65+
.build();
66+
let _guard = app.hold();
67+
let (tx, rx) = channel::<gio::RegistrationId>();
68+
69+
app.connect_startup(move |app| {
70+
on_startup(app, &tx);
71+
});
72+
73+
app.connect_activate(move |_| {
74+
println!("Waiting for DBus Hello method to be called. Call the following command from another terminal:");
75+
println!("dbus-send --print-reply --dest=com.github.gtk-rs.examples.RegisterDBusObject /com/github/gtk_rs/examples/HelloWorld com.github.gtk_rs.examples.HelloWorld.Hello string:YourName");
76+
});
77+
78+
app.connect_shutdown(move |app| {
79+
on_shutdown(app, &rx);
80+
});
81+
82+
app.run()
83+
}

gio/src/dbus_connection.rs

Lines changed: 131 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -22,89 +22,149 @@ pub struct FilterId(NonZeroU32);
2222
#[derive(Debug, Eq, PartialEq)]
2323
pub struct SignalSubscriptionId(NonZeroU32);
2424

25-
impl DBusConnection {
26-
#[doc(alias = "g_dbus_connection_register_object_with_closures")]
27-
pub fn register_object<MethodCall, SetProperty, GetProperty>(
28-
&self,
29-
object_path: &str,
30-
interface_info: &DBusInterfaceInfo,
31-
method_call: MethodCall,
32-
get_property: GetProperty,
33-
set_property: SetProperty,
34-
) -> Result<RegistrationId, glib::Error>
35-
where
36-
MethodCall: Fn(DBusConnection, &str, &str, &str, &str, glib::Variant, DBusMethodInvocation)
37-
+ 'static,
38-
GetProperty: Fn(DBusConnection, &str, &str, &str, &str) -> glib::Variant + 'static,
39-
SetProperty: Fn(DBusConnection, &str, &str, &str, &str, glib::Variant) -> bool + 'static,
40-
{
25+
#[must_use = "The builder must be built to be used"]
26+
pub struct RegistrationBuilder<'a> {
27+
connection: &'a DBusConnection,
28+
object_path: &'a str,
29+
interface_info: &'a DBusInterfaceInfo,
30+
#[allow(clippy::type_complexity)]
31+
method_call: Option<
32+
Box_<dyn Fn(DBusConnection, &str, &str, &str, &str, glib::Variant, DBusMethodInvocation)>,
33+
>,
34+
#[allow(clippy::type_complexity)]
35+
get_property: Option<Box_<dyn Fn(DBusConnection, &str, &str, &str, &str) -> glib::Variant>>,
36+
#[allow(clippy::type_complexity)]
37+
set_property:
38+
Option<Box_<dyn Fn(DBusConnection, &str, &str, &str, &str, glib::Variant) -> bool>>,
39+
}
40+
41+
impl<'a> RegistrationBuilder<'a> {
42+
pub fn method_call<
43+
F: Fn(DBusConnection, &str, &str, &str, &str, glib::Variant, DBusMethodInvocation) + 'static,
44+
>(
45+
mut self,
46+
f: F,
47+
) -> Self {
48+
self.method_call = Some(Box_::new(f));
49+
self
50+
}
51+
52+
#[doc(alias = "get_property")]
53+
pub fn property<F: Fn(DBusConnection, &str, &str, &str, &str) -> glib::Variant + 'static>(
54+
mut self,
55+
f: F,
56+
) -> Self {
57+
self.get_property = Some(Box_::new(f));
58+
self
59+
}
60+
61+
pub fn set_property<
62+
F: Fn(DBusConnection, &str, &str, &str, &str, glib::Variant) -> bool + 'static,
63+
>(
64+
mut self,
65+
f: F,
66+
) -> Self {
67+
self.set_property = Some(Box_::new(f));
68+
self
69+
}
70+
71+
pub fn build(self) -> Result<RegistrationId, glib::Error> {
4172
unsafe {
4273
let mut error = std::ptr::null_mut();
4374
let id = ffi::g_dbus_connection_register_object_with_closures(
44-
self.to_glib_none().0,
45-
object_path.to_glib_none().0,
46-
interface_info.to_glib_none().0,
47-
glib::Closure::new_local(move |args| {
48-
let conn = args[0].get::<DBusConnection>().unwrap();
49-
let sender = args[1].get::<&str>().unwrap();
50-
let object_path = args[2].get::<&str>().unwrap();
51-
let interface_name = args[3].get::<&str>().unwrap();
52-
let method_name = args[4].get::<&str>().unwrap();
53-
let parameters = args[5].get::<glib::Variant>().unwrap();
54-
let invocation = args[6].get::<DBusMethodInvocation>().unwrap();
55-
method_call(
56-
conn,
57-
sender,
58-
object_path,
59-
interface_name,
60-
method_name,
61-
parameters,
62-
invocation,
63-
);
64-
None
65-
})
66-
.to_glib_none()
67-
.0,
68-
glib::Closure::new_local(move |args| {
69-
let conn = args[0].get::<DBusConnection>().unwrap();
70-
let sender = args[1].get::<&str>().unwrap();
71-
let object_path = args[2].get::<&str>().unwrap();
72-
let interface_name = args[3].get::<&str>().unwrap();
73-
let property_name = args[4].get::<&str>().unwrap();
74-
let result =
75-
get_property(conn, sender, object_path, interface_name, property_name);
76-
Some(result.to_value())
77-
})
78-
.to_glib_none()
79-
.0,
80-
glib::Closure::new_local(move |args| {
81-
let conn = args[0].get::<DBusConnection>().unwrap();
82-
let sender = args[1].get::<&str>().unwrap();
83-
let object_path = args[2].get::<&str>().unwrap();
84-
let interface_name = args[3].get::<&str>().unwrap();
85-
let property_name = args[4].get::<&str>().unwrap();
86-
let value = args[5].get::<glib::Variant>().unwrap();
87-
let result = set_property(
88-
conn,
89-
sender,
90-
object_path,
91-
interface_name,
92-
property_name,
93-
value,
94-
);
95-
Some(result.to_value())
96-
})
97-
.to_glib_none()
98-
.0,
75+
self.connection.to_glib_none().0,
76+
self.object_path.to_glib_none().0,
77+
self.interface_info.to_glib_none().0,
78+
self.method_call
79+
.map(|f| {
80+
glib::Closure::new_local(move |args| {
81+
let conn = args[0].get::<DBusConnection>().unwrap();
82+
let sender = args[1].get::<&str>().unwrap();
83+
let object_path = args[2].get::<&str>().unwrap();
84+
let interface_name = args[3].get::<&str>().unwrap();
85+
let method_name = args[4].get::<&str>().unwrap();
86+
let parameters = args[5].get::<glib::Variant>().unwrap();
87+
let invocation = args[6].get::<DBusMethodInvocation>().unwrap();
88+
f(
89+
conn,
90+
sender,
91+
object_path,
92+
interface_name,
93+
method_name,
94+
parameters,
95+
invocation,
96+
);
97+
None
98+
})
99+
})
100+
.to_glib_none()
101+
.0,
102+
self.set_property
103+
.map(|f| {
104+
glib::Closure::new_local(move |args| {
105+
let conn = args[0].get::<DBusConnection>().unwrap();
106+
let sender = args[1].get::<&str>().unwrap();
107+
let object_path = args[2].get::<&str>().unwrap();
108+
let interface_name = args[3].get::<&str>().unwrap();
109+
let property_name = args[4].get::<&str>().unwrap();
110+
let value = args[5].get::<glib::Variant>().unwrap();
111+
let result = f(
112+
conn,
113+
sender,
114+
object_path,
115+
interface_name,
116+
property_name,
117+
value,
118+
);
119+
Some(result.to_value())
120+
})
121+
})
122+
.to_glib_none()
123+
.0,
124+
self.get_property
125+
.map(|f| {
126+
glib::Closure::new_local(move |args| {
127+
let conn = args[0].get::<DBusConnection>().unwrap();
128+
let sender = args[1].get::<&str>().unwrap();
129+
let object_path = args[2].get::<&str>().unwrap();
130+
let interface_name = args[3].get::<&str>().unwrap();
131+
let property_name = args[4].get::<&str>().unwrap();
132+
let result =
133+
f(conn, sender, object_path, interface_name, property_name);
134+
Some(result.to_value())
135+
})
136+
})
137+
.to_glib_none()
138+
.0,
99139
&mut error,
100140
);
141+
101142
if error.is_null() {
102143
Ok(RegistrationId(NonZeroU32::new_unchecked(id)))
103144
} else {
104145
Err(from_glib_full(error))
105146
}
106147
}
107148
}
149+
}
150+
151+
impl DBusConnection {
152+
#[doc(alias = "g_dbus_connection_register_object")]
153+
#[doc(alias = "g_dbus_connection_register_object_with_closures")]
154+
pub fn register_object<'a>(
155+
&'a self,
156+
object_path: &'a str,
157+
interface_info: &'a DBusInterfaceInfo,
158+
) -> RegistrationBuilder<'a> {
159+
RegistrationBuilder {
160+
connection: self,
161+
object_path,
162+
interface_info,
163+
method_call: None,
164+
get_property: None,
165+
set_property: None,
166+
}
167+
}
108168

109169
#[doc(alias = "g_dbus_connection_unregister_object")]
110170
pub fn unregister_object(

0 commit comments

Comments
 (0)