Skip to content

Commit

Permalink
Merge pull request #1634 from swsnr/dbus_register_vfunc
Browse files Browse the repository at this point in the history
Expose dbus_register and dbus_unregister vfuncs
  • Loading branch information
sdroege authored Jan 27, 2025
2 parents 79adf6c + 054d9e2 commit bb40a03
Show file tree
Hide file tree
Showing 2 changed files with 285 additions and 107 deletions.
270 changes: 165 additions & 105 deletions examples/gio_dbus_register_object/main.rs
Original file line number Diff line number Diff line change
@@ -1,126 +1,186 @@
use gio::{prelude::*, IOErrorEnum};
use std::{
sync::mpsc::{channel, Receiver, Sender},
time::Duration,
};

const EXAMPLE_XML: &str = r#"
<node>
<interface name='com.github.gtk_rs.examples.HelloWorld'>
<method name='Hello'>
<arg type='s' name='name' direction='in'/>
<arg type='s' name='greet' direction='out'/>
</method>
<method name='SlowHello'>
<arg type='s' name='name' direction='in'/>
<arg type='u' name='delay' direction='in'/>
<arg type='s' name='greet' direction='out'/>
</method>
</interface>
</node>
"#;
use gio::prelude::*;

#[derive(Debug, glib::Variant)]
struct Hello {
name: String,
glib::wrapper! {
pub struct SampleApplication(ObjectSubclass<imp::SampleApplication>)
@extends gio::Application,
@implements gio::ActionGroup, gio::ActionMap;
}

#[derive(Debug, glib::Variant)]
struct SlowHello {
name: String,
delay: u32,
impl Default for SampleApplication {
fn default() -> Self {
glib::Object::builder()
.property(
"application-id",
"com.github.gtk-rs.examples.RegisterDBusObject",
)
.build()
}
}

#[derive(Debug)]
enum HelloMethod {
Hello(Hello),
SlowHello(SlowHello),
}
mod imp {
use std::cell::RefCell;
use std::time::Duration;

use gio::prelude::*;
use gio::subclass::prelude::*;
use gio::{DBusConnection, DBusError};

const EXAMPLE_XML: &str = r#"
<node>
<interface name='com.github.gtk_rs.examples.HelloWorld'>
<method name='Hello'>
<arg type='s' name='name' direction='in'/>
<arg type='s' name='greet' direction='out'/>
</method>
<method name='SlowHello'>
<arg type='s' name='name' direction='in'/>
<arg type='u' name='delay' direction='in'/>
<arg type='s' name='greet' direction='out'/>
</method>
<method name='GoodBye'></method>
</interface>
</node>
"#;

#[derive(Debug, glib::Variant)]
struct Hello {
name: String,
}

#[derive(Debug, glib::Variant)]
struct SlowHello {
name: String,
delay: u32,
}

impl DBusMethodCall for HelloMethod {
fn parse_call(
_obj_path: &str,
_interface: Option<&str>,
method: &str,
params: glib::Variant,
) -> Result<Self, glib::Error> {
match method {
"Hello" => Ok(params.get::<Hello>().map(Self::Hello)),
"SlowHello" => Ok(params.get::<SlowHello>().map(Self::SlowHello)),
_ => Err(glib::Error::new(IOErrorEnum::Failed, "No such method")),
#[derive(Debug)]
enum HelloMethod {
Hello(Hello),
SlowHello(SlowHello),
GoodBye,
}

impl DBusMethodCall for HelloMethod {
fn parse_call(
_obj_path: &str,
_interface: Option<&str>,
method: &str,
params: glib::Variant,
) -> Result<Self, glib::Error> {
match method {
"Hello" => Ok(params.get::<Hello>().map(Self::Hello)),
"SlowHello" => Ok(params.get::<SlowHello>().map(Self::SlowHello)),
"GoodBye" => Ok(Some(Self::GoodBye)),
_ => Err(glib::Error::new(DBusError::UnknownMethod, "No such method")),
}
.and_then(|p| {
p.ok_or_else(|| glib::Error::new(DBusError::InvalidArgs, "Invalid parameters"))
})
}
.and_then(|p| p.ok_or_else(|| glib::Error::new(IOErrorEnum::Failed, "Invalid parameters")))
}
}

fn on_startup(app: &gio::Application, tx: &Sender<gio::RegistrationId>) {
let connection = app.dbus_connection().expect("connection");

let example = gio::DBusNodeInfo::for_xml(EXAMPLE_XML)
.ok()
.and_then(|e| e.lookup_interface("com.github.gtk_rs.examples.HelloWorld"))
.expect("Example interface");

if let Ok(id) = connection
.register_object("/com/github/gtk_rs/examples/HelloWorld", &example)
.typed_method_call::<HelloMethod>()
.invoke_and_return_future_local(|_, sender, call| {
println!("Method call from {sender:?}");
async {
match call {
HelloMethod::Hello(Hello { name }) => {
let greet = format!("Hello {name}!");
println!("{greet}");
Ok(Some(greet.to_variant()))
}
HelloMethod::SlowHello(SlowHello { name, delay }) => {
glib::timeout_future(Duration::from_secs(delay as u64)).await;
let greet = format!("Hello {name} after {delay} seconds!");
println!("{greet}");
Ok(Some(greet.to_variant()))
#[derive(Default)]
pub struct SampleApplication {
registration_id: RefCell<Option<gio::RegistrationId>>,
}

impl SampleApplication {
fn register_object(
&self,
connection: &DBusConnection,
) -> Result<gio::RegistrationId, glib::Error> {
let example = gio::DBusNodeInfo::for_xml(EXAMPLE_XML)
.ok()
.and_then(|e| e.lookup_interface("com.github.gtk_rs.examples.HelloWorld"))
.expect("Example interface");

connection
.register_object("/com/github/gtk_rs/examples/HelloWorld", &example)
.typed_method_call::<HelloMethod>()
.invoke_and_return_future_local(glib::clone!(
#[weak_allow_none(rename_to = app)]
self.obj(),
move |_, sender, call| {
println!("Method call from {sender:?}");
let app = app.clone();
async move {
match call {
HelloMethod::Hello(Hello { name }) => {
let greet = format!("Hello {name}!");
println!("{greet}");
Ok(Some(greet.to_variant()))
}
HelloMethod::SlowHello(SlowHello { name, delay }) => {
glib::timeout_future(Duration::from_secs(delay as u64)).await;
let greet = format!("Hello {name} after {delay} seconds!");
println!("{greet}");
Ok(Some(greet.to_variant()))
}
HelloMethod::GoodBye => {
if let Some(app) = app {
app.quit();
}
Ok(None)
}
}
}
}
))
.build()
}
}

#[glib::object_subclass]
impl ObjectSubclass for SampleApplication {
const NAME: &'static str = "SampleApplication";

type Type = super::SampleApplication;

type ParentType = gio::Application;
}

impl ObjectImpl for SampleApplication {}

impl ApplicationImpl for SampleApplication {
fn dbus_register(
&self,
connection: &DBusConnection,
object_path: &str,
) -> Result<(), glib::Error> {
self.parent_dbus_register(connection, object_path)?;
self.registration_id
.replace(Some(self.register_object(connection)?));
println!("registered object on session bus");
Ok(())
}

fn dbus_unregister(&self, connection: &DBusConnection, object_path: &str) {
self.parent_dbus_unregister(connection, object_path);
if let Some(id) = self.registration_id.take() {
if connection.unregister_object(id).is_ok() {
println!("Unregistered object");
} else {
eprintln!("Could not unregister object");
}
}
})
.build()
{
println!("Registered object");
tx.send(id).unwrap();
} else {
eprintln!("Could not register object");
}
}
}

fn shutdown(&self) {
self.parent_shutdown();
println!("Good bye!");
}

fn on_shutdown(app: &gio::Application, rx: &Receiver<gio::RegistrationId>) {
let connection = app.dbus_connection().expect("connection");
if let Ok(registration_id) = rx.try_recv() {
if connection.unregister_object(registration_id).is_ok() {
println!("Unregistered object");
} else {
eprintln!("Could not unregister object");
fn activate(&self) {
println!("Waiting for DBus Hello method to be called. Call the following command from another terminal:");
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");
println!("Quit with the following command:");
println!("dbus-send --print-reply --dest=com.github.gtk-rs.examples.RegisterDBusObject /com/github/gtk_rs/examples/HelloWorld com.github.gtk_rs.examples.HelloWorld.GoodBye");
}
}
}

fn main() -> glib::ExitCode {
let app = gio::Application::builder()
.application_id("com.github.gtk-rs.examples.RegisterDBusObject")
.build();
let app = SampleApplication::default();
let _guard = app.hold();
let (tx, rx) = channel::<gio::RegistrationId>();

app.connect_startup(move |app| {
on_startup(app, &tx);
});

app.connect_activate(move |_| {
println!("Waiting for DBus Hello method to be called. Call the following command from another terminal:");
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");
});

app.connect_shutdown(move |app| {
on_shutdown(app, &rx);
});

app.run()
}
Loading

0 comments on commit bb40a03

Please sign in to comment.