Skip to content

Commit b3d6427

Browse files
committed
gio-macros: Implement derive for DBusInterfaceSkeleton (methods only)
1 parent dd29a20 commit b3d6427

19 files changed

Lines changed: 1623 additions & 120 deletions

File tree

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
use crate::SampleApplication;
2+
use gio::prelude::*;
3+
use gio::subclass::prelude::*;
4+
5+
glib::wrapper! {
6+
pub struct HelloWorldSkeleton(ObjectSubclass<imp::HelloWorldSkeleton>)
7+
@extends gio::DBusInterfaceSkeleton;
8+
}
9+
10+
impl Default for HelloWorldSkeleton {
11+
fn default() -> Self {
12+
glib::Object::new()
13+
}
14+
}
15+
16+
impl HelloWorldSkeleton {
17+
pub(crate) fn set_application(&self, app: &SampleApplication) {
18+
self.imp().set_application(app);
19+
}
20+
}
21+
22+
mod imp {
23+
use super::*;
24+
use glib::WeakRef;
25+
use std::cell::RefCell;
26+
use std::time::{Duration, Instant};
27+
28+
#[derive(Default, gio::DBusInterfaceSkeleton)]
29+
#[dbus_interface(name = "com.github.gtk_rs.examples.HelloWorld")]
30+
pub struct HelloWorldSkeleton {
31+
application: RefCell<WeakRef<SampleApplication>>,
32+
}
33+
34+
#[glib::object_subclass]
35+
impl ObjectSubclass for HelloWorldSkeleton {
36+
const NAME: &'static str = "HelloWorldSkeleton";
37+
type Type = super::HelloWorldSkeleton;
38+
type ParentType = gio::DBusInterfaceSkeleton;
39+
}
40+
41+
impl ObjectImpl for HelloWorldSkeleton {}
42+
43+
impl HelloWorldSkeleton {
44+
pub(super) fn set_application(&self, app: &SampleApplication) {
45+
self.application.replace(app.downgrade());
46+
}
47+
}
48+
49+
#[gio::dbus_methods]
50+
impl HelloWorldSkeleton {
51+
#[deprecated]
52+
fn hello(&self, name: String) -> String {
53+
let greet = format!("Hello {name}!");
54+
println!("{greet}");
55+
greet
56+
}
57+
58+
#[dbus(out_args("name", "effective_delay"))]
59+
async fn slow_hello(
60+
&self,
61+
#[dbus(connection)] connection: gio::DBusConnection,
62+
#[dbus(invocation)] invocation: gio::DBusMethodInvocation,
63+
name: String,
64+
delay: u32,
65+
) -> Result<(String, f64), glib::Error> {
66+
if delay > 4 {
67+
return Err(glib::Error::new(
68+
gio::DBusError::InvalidArgs,
69+
"delay must not be greater than 4 seconds",
70+
));
71+
}
72+
73+
let instant = Instant::now();
74+
glib::timeout_future(Duration::from_secs(delay as u64)).await;
75+
let greet = format!("Hello {name} after {delay} seconds!");
76+
println!("{greet}");
77+
Ok((greet, instant.elapsed().as_secs_f64()))
78+
}
79+
80+
#[allow(clippy::manual_async_fn)]
81+
fn goodbye(&self) -> impl Future<Output = ()> {
82+
async {
83+
if let Some(app) = self.application.borrow().upgrade() {
84+
app.quit();
85+
}
86+
}
87+
}
88+
89+
fn r#raw_ident(&self) {}
90+
}
91+
}

examples/gio_dbus_register_object/main.rs

Lines changed: 18 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
1+
use crate::hello_world::HelloWorldSkeleton;
12
use gio::prelude::*;
3+
use gio::subclass::prelude::*;
24

35
glib::wrapper! {
46
pub struct SampleApplication(ObjectSubclass<imp::SampleApplication>)
57
@extends gio::Application,
68
@implements gio::ActionGroup, gio::ActionMap;
79
}
810

11+
mod hello_world;
12+
913
impl Default for SampleApplication {
1014
fn default() -> Self {
1115
glib::Object::builder()
@@ -18,116 +22,11 @@ impl Default for SampleApplication {
1822
}
1923

2024
mod imp {
21-
use std::cell::RefCell;
22-
use std::time::Duration;
23-
24-
use gio::prelude::*;
25-
use gio::subclass::prelude::*;
26-
use gio::{DBusConnection, DBusError};
27-
28-
const EXAMPLE_XML: &str = r#"
29-
<node>
30-
<interface name='com.github.gtk_rs.examples.HelloWorld'>
31-
<method name='Hello'>
32-
<arg type='s' name='name' direction='in'/>
33-
<arg type='s' name='greet' direction='out'/>
34-
</method>
35-
<method name='SlowHello'>
36-
<arg type='s' name='name' direction='in'/>
37-
<arg type='u' name='delay' direction='in'/>
38-
<arg type='s' name='greet' direction='out'/>
39-
</method>
40-
<method name='GoodBye'></method>
41-
</interface>
42-
</node>
43-
"#;
44-
45-
#[derive(Debug, glib::Variant)]
46-
struct Hello {
47-
name: String,
48-
}
49-
50-
#[derive(Debug, glib::Variant)]
51-
struct SlowHello {
52-
name: String,
53-
delay: u32,
54-
}
55-
56-
#[derive(Debug)]
57-
enum HelloMethod {
58-
Hello(Hello),
59-
SlowHello(SlowHello),
60-
GoodBye,
61-
}
62-
63-
impl DBusMethodCall for HelloMethod {
64-
fn parse_call(
65-
_obj_path: &str,
66-
_interface: Option<&str>,
67-
method: &str,
68-
params: glib::Variant,
69-
) -> Result<Self, glib::Error> {
70-
match method {
71-
"Hello" => Ok(params.get::<Hello>().map(Self::Hello)),
72-
"SlowHello" => Ok(params.get::<SlowHello>().map(Self::SlowHello)),
73-
"GoodBye" => Ok(Some(Self::GoodBye)),
74-
_ => Err(glib::Error::new(DBusError::UnknownMethod, "No such method")),
75-
}
76-
.and_then(|p| {
77-
p.ok_or_else(|| glib::Error::new(DBusError::InvalidArgs, "Invalid parameters"))
78-
})
79-
}
80-
}
25+
use super::*;
8126

8227
#[derive(Default)]
8328
pub struct SampleApplication {
84-
registration_id: RefCell<Option<gio::RegistrationId>>,
85-
}
86-
87-
impl SampleApplication {
88-
fn register_object(
89-
&self,
90-
connection: &DBusConnection,
91-
) -> Result<gio::RegistrationId, glib::Error> {
92-
let example = gio::DBusNodeInfo::for_xml(EXAMPLE_XML)
93-
.ok()
94-
.and_then(|e| e.lookup_interface("com.github.gtk_rs.examples.HelloWorld"))
95-
.expect("Example interface");
96-
97-
connection
98-
.register_object("/com/github/gtk_rs/examples/HelloWorld", &example)
99-
.typed_method_call::<HelloMethod>()
100-
.invoke_and_return_future_local(glib::clone!(
101-
#[weak_allow_none(rename_to = app)]
102-
self.obj(),
103-
move |_, sender, call| {
104-
println!("Method call from {sender:?}");
105-
let app = app.clone();
106-
async move {
107-
match call {
108-
HelloMethod::Hello(Hello { name }) => {
109-
let greet = format!("Hello {name}!");
110-
println!("{greet}");
111-
Ok(Some(greet.to_variant()))
112-
}
113-
HelloMethod::SlowHello(SlowHello { name, delay }) => {
114-
glib::timeout_future(Duration::from_secs(delay as u64)).await;
115-
let greet = format!("Hello {name} after {delay} seconds!");
116-
println!("{greet}");
117-
Ok(Some(greet.to_variant()))
118-
}
119-
HelloMethod::GoodBye => {
120-
if let Some(app) = app {
121-
app.quit();
122-
}
123-
Ok(None)
124-
}
125-
}
126-
}
127-
}
128-
))
129-
.build()
130-
}
29+
hello_world_skeleton: HelloWorldSkeleton,
13130
}
13231

13332
#[glib::object_subclass]
@@ -139,30 +38,30 @@ mod imp {
13938
type ParentType = gio::Application;
14039
}
14140

142-
impl ObjectImpl for SampleApplication {}
41+
impl ObjectImpl for SampleApplication {
42+
fn constructed(&self) {
43+
self.hello_world_skeleton.set_application(&self.obj());
44+
}
45+
}
14346

14447
impl ApplicationImpl for SampleApplication {
14548
fn dbus_register(
14649
&self,
147-
connection: &DBusConnection,
50+
connection: &gio::DBusConnection,
14851
object_path: &str,
14952
) -> Result<(), glib::Error> {
15053
self.parent_dbus_register(connection, object_path)?;
151-
self.registration_id
152-
.replace(Some(self.register_object(connection)?));
54+
self.hello_world_skeleton
55+
.export(connection, "/com/github/gtk_rs/examples/HelloWorld")?;
15356
println!("registered object on session bus");
15457
Ok(())
15558
}
15659

157-
fn dbus_unregister(&self, connection: &DBusConnection, object_path: &str) {
60+
fn dbus_unregister(&self, connection: &gio::DBusConnection, object_path: &str) {
15861
self.parent_dbus_unregister(connection, object_path);
159-
if let Some(id) = self.registration_id.take() {
160-
if connection.unregister_object(id).is_ok() {
161-
println!("Unregistered object");
162-
} else {
163-
eprintln!("Could not unregister object");
164-
}
165-
}
62+
self.hello_world_skeleton
63+
.unexport_from_connection(connection);
64+
println!("Unregistered object");
16665
}
16766

16867
fn shutdown(&self) {
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// Take a look at the license at the top of the repository in the LICENSE file.
2+
3+
use crate::dbus_interface::attributes::ATTRIBUTE_NAME;
4+
use crate::dbus_interface::parse::{DBusMethodArgumentProvider, DBusPropertyArgumentProvider};
5+
use syn::{Attribute, LitStr};
6+
7+
pub(crate) struct DBusMethodArgumentAttribute {
8+
pub(crate) name: Option<LitStr>,
9+
pub(crate) provider: Option<DBusMethodArgumentProvider>,
10+
}
11+
12+
impl DBusMethodArgumentAttribute {
13+
pub(crate) fn parse(attrs: &[Attribute]) -> syn::Result<Self> {
14+
let mut provider = None;
15+
let mut name: Option<LitStr> = None;
16+
for attr in attrs {
17+
if attr.path().is_ident(ATTRIBUTE_NAME) {
18+
attr.parse_nested_meta(|meta| {
19+
if meta.path.is_ident("connection") {
20+
provider = Some(DBusMethodArgumentProvider::Connection);
21+
Ok(())
22+
}
23+
else if meta.path.is_ident("signal_emitter") {
24+
provider = Some(DBusMethodArgumentProvider::SignalEmitter);
25+
Ok(())
26+
}
27+
else if meta.path.is_ident("invocation") {
28+
provider = Some(DBusMethodArgumentProvider::Invocation);
29+
Ok(())
30+
}
31+
else if meta.path.is_ident("name") {
32+
name = Some(meta.value()?.parse()?);
33+
Ok(())
34+
} else {
35+
Err(meta.error(format!(
36+
"unknown attribute `{}`. Possible attributes are `connection`, `invocation`, `signal_emitter`, `name`",
37+
meta.path.get_ident().unwrap(),
38+
)))
39+
}
40+
})?;
41+
}
42+
}
43+
Ok(Self { name, provider })
44+
}
45+
}
46+
47+
pub(crate) struct DBusPropertyArgumentAttribute {
48+
pub(crate) provider: Option<DBusPropertyArgumentProvider>,
49+
}
50+
51+
impl DBusPropertyArgumentAttribute {
52+
pub(crate) fn parse(attrs: &[Attribute]) -> syn::Result<Self> {
53+
let mut provider = None;
54+
for attr in attrs {
55+
if attr.path().is_ident(ATTRIBUTE_NAME) {
56+
attr.parse_nested_meta(|meta| {
57+
if meta.path.is_ident("connection") {
58+
provider = Some(DBusPropertyArgumentProvider::Connection);
59+
Ok(())
60+
} else if meta.path.is_ident("signal_emitter") {
61+
provider = Some(DBusPropertyArgumentProvider::SignalEmitter);
62+
Ok(())
63+
} else {
64+
Err(meta.error(format!(
65+
"unknown attribute `{}`. Possible attributes are `connection`",
66+
meta.path.get_ident().unwrap(),
67+
)))
68+
}
69+
})?;
70+
}
71+
}
72+
Ok(Self { provider })
73+
}
74+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Take a look at the license at the top of the repository in the LICENSE file.
2+
3+
use proc_macro2::Span;
4+
use syn::LitStr;
5+
use syn::parse::Parse;
6+
7+
pub(crate) struct EmitsChangedSignal {
8+
pub(crate) kind: EmitsChangedSignalKind,
9+
pub(crate) span: Span,
10+
}
11+
12+
pub(crate) enum EmitsChangedSignalKind {
13+
True,
14+
Invalidates,
15+
Const,
16+
False,
17+
}
18+
19+
impl Parse for EmitsChangedSignal {
20+
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
21+
use EmitsChangedSignalKind::*;
22+
let lit: LitStr = input.parse()?;
23+
let value = lit.value();
24+
let kind = match value.as_str() {
25+
"true" => True,
26+
"invalidates" => Invalidates,
27+
"const" => Const,
28+
"false" => False,
29+
_ => {
30+
return Err(syn::Error::new(
31+
lit.span(),
32+
format!(
33+
"unknown value {value:?}. Possible values are \"true\", \"invalidates\", \"const\", \"false\""
34+
),
35+
));
36+
}
37+
};
38+
Ok(EmitsChangedSignal {
39+
kind,
40+
span: lit.span(),
41+
})
42+
}
43+
}

0 commit comments

Comments
 (0)