-
-
Notifications
You must be signed in to change notification settings - Fork 119
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1634 from swsnr/dbus_register_vfunc
Expose dbus_register and dbus_unregister vfuncs
- Loading branch information
Showing
2 changed files
with
285 additions
and
107 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() | ||
} |
Oops, something went wrong.