Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose dbus_register and dbus_unregister vfuncs #1634

Merged
merged 5 commits into from
Jan 27, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading