summaryrefslogtreecommitdiffstats
path: root/third_party/rust/dbus/examples
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /third_party/rust/dbus/examples
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/dbus/examples')
-rw-r--r--third_party/rust/dbus/examples/adv_server.rs177
-rw-r--r--third_party/rust/dbus/examples/argument_guide.md193
-rw-r--r--third_party/rust/dbus/examples/client.rs14
-rw-r--r--third_party/rust/dbus/examples/properties.rs50
-rw-r--r--third_party/rust/dbus/examples/properties_msgitem.rs10
-rw-r--r--third_party/rust/dbus/examples/rtkit.rs67
-rw-r--r--third_party/rust/dbus/examples/server.rs72
-rw-r--r--third_party/rust/dbus/examples/unity_focused_window.rs25
8 files changed, 608 insertions, 0 deletions
diff --git a/third_party/rust/dbus/examples/adv_server.rs b/third_party/rust/dbus/examples/adv_server.rs
new file mode 100644
index 0000000000..a71fc39a6a
--- /dev/null
+++ b/third_party/rust/dbus/examples/adv_server.rs
@@ -0,0 +1,177 @@
+// More advanced server example.
+
+// This is supposed to look like a D-Bus service that allows the user to manipulate storage devices.
+
+// Note: in the dbus-codegen/example directory, there is a version of this example where dbus-codegen
+// was used to create some boilerplate code - feel free to compare the two examples.
+
+extern crate dbus;
+
+use std::sync::Arc;
+use std::sync::mpsc;
+use std::cell::Cell;
+use std::thread;
+
+use dbus::{Connection, BusType, tree, Path};
+use dbus::tree::{Interface, Signal, MTFn, Access, MethodErr, EmitsChangedSignal};
+
+// Our storage device
+#[derive(Debug)]
+struct Device {
+ description: String,
+ path: Path<'static>,
+ index: i32,
+ online: Cell<bool>,
+ checking: Cell<bool>,
+}
+
+// Every storage device has its own object path.
+// We therefore create a link from the object path to the Device.
+#[derive(Copy, Clone, Default, Debug)]
+struct TData;
+impl tree::DataType for TData {
+ type Tree = ();
+ type ObjectPath = Arc<Device>;
+ type Property = ();
+ type Interface = ();
+ type Method = ();
+ type Signal = ();
+}
+
+
+impl Device {
+ // Creates a "test" device (not a real one, since this is an example).
+ fn new_bogus(index: i32) -> Device {
+ Device {
+ description: format!("This is device {}, which is {}.", index,
+ ["totally awesome", "really fancy", "still going strong"][(index as usize) % 3]),
+ path: format!("/Device{}", index).into(),
+ index: index,
+ online: Cell::new(index % 2 == 0),
+ checking: Cell::new(false),
+ }
+ }
+}
+
+// Here's where we implement the code for our interface.
+fn create_iface(check_complete_s: mpsc::Sender<i32>) -> (Interface<MTFn<TData>, TData>, Arc<Signal<TData>>) {
+ let f = tree::Factory::new_fn();
+
+ let check_complete = Arc::new(f.signal("CheckComplete", ()));
+
+ (f.interface("com.example.dbus.rs.device", ())
+ // The online property can be both set and get
+ .add_p(f.property::<bool,_>("online", ())
+ .access(Access::ReadWrite)
+ .on_get(|i, m| {
+ let dev: &Arc<Device> = m.path.get_data();
+ i.append(dev.online.get());
+ Ok(())
+ })
+ .on_set(|i, m| {
+ let dev: &Arc<Device> = m.path.get_data();
+ let b: bool = try!(i.read());
+ if b && dev.checking.get() {
+ return Err(MethodErr::failed(&"Device currently under check, cannot bring online"))
+ }
+ dev.online.set(b);
+ Ok(())
+ })
+ )
+ // The "checking" property is read only
+ .add_p(f.property::<bool,_>("checking", ())
+ .emits_changed(EmitsChangedSignal::False)
+ .on_get(|i, m| {
+ let dev: &Arc<Device> = m.path.get_data();
+ i.append(dev.checking.get());
+ Ok(())
+ })
+ )
+ // ...and so is the "description" property
+ .add_p(f.property::<&str,_>("description", ())
+ .emits_changed(EmitsChangedSignal::Const)
+ .on_get(|i, m| {
+ let dev: &Arc<Device> = m.path.get_data();
+ i.append(&dev.description);
+ Ok(())
+ })
+ )
+ // ...add a method for starting a device check...
+ .add_m(f.method("check", (), move |m| {
+ let dev: &Arc<Device> = m.path.get_data();
+ if dev.checking.get() {
+ return Err(MethodErr::failed(&"Device currently under check, cannot start another check"))
+ }
+ if dev.online.get() {
+ return Err(MethodErr::failed(&"Device is currently online, cannot start check"))
+ }
+ dev.checking.set(true);
+
+ // Start some lengthy processing in a separate thread...
+ let devindex = dev.index;
+ let ch = check_complete_s.clone();
+ thread::spawn(move || {
+
+ // Bogus check of device
+ use std::time::Duration;
+ thread::sleep(Duration::from_secs(15));
+
+ // Tell main thread that we finished
+ ch.send(devindex).unwrap();
+ });
+ Ok(vec!(m.msg.method_return()))
+ }))
+ // Indicate that we send a special signal once checking has completed.
+ .add_s(check_complete.clone())
+ , check_complete)
+}
+
+fn create_tree(devices: &[Arc<Device>], iface: &Arc<Interface<MTFn<TData>, TData>>)
+ -> tree::Tree<MTFn<TData>, TData> {
+
+ let f = tree::Factory::new_fn();
+ let mut tree = f.tree(());
+ for dev in devices {
+ tree = tree.add(f.object_path(dev.path.clone(), dev.clone())
+ .introspectable()
+ .add(iface.clone())
+ );
+ }
+ tree
+}
+
+fn run() -> Result<(), Box<std::error::Error>> {
+ // Create our bogus devices
+ let devices: Vec<Arc<Device>> = (0..10).map(|i| Arc::new(Device::new_bogus(i))).collect();
+
+ // Create tree
+ let (check_complete_s, check_complete_r) = mpsc::channel::<i32>();
+ let (iface, sig) = create_iface(check_complete_s);
+ let tree = create_tree(&devices, &Arc::new(iface));
+
+ // Setup DBus connection
+ let c = try!(Connection::get_private(BusType::Session));
+ try!(c.register_name("com.example.dbus.rs.advancedserverexample", 0));
+ try!(tree.set_registered(&c, true));
+
+ // ...and serve incoming requests.
+ c.add_handler(tree);
+ loop {
+ // Wait for incoming messages. This will block up to one second.
+ // Discard the result - relevant messages have already been handled.
+ c.incoming(1000).next();
+
+ // Do all other things we need to do in our main loop.
+ if let Ok(idx) = check_complete_r.try_recv() {
+ let dev = &devices[idx as usize];
+ dev.checking.set(false);
+ try!(c.send(sig.msg(&dev.path, &"com.example.dbus.rs.device".into())).map_err(|_| "Sending DBus signal failed"));
+ }
+ }
+}
+
+fn main() {
+ if let Err(e) = run() {
+ println!("{}", e);
+ }
+}
diff --git a/third_party/rust/dbus/examples/argument_guide.md b/third_party/rust/dbus/examples/argument_guide.md
new file mode 100644
index 0000000000..b36f2e937a
--- /dev/null
+++ b/third_party/rust/dbus/examples/argument_guide.md
@@ -0,0 +1,193 @@
+Preamble
+--------
+
+The different ways you can append and get message arguments can be a bit bewildering. I've iterated a few times on the design and didn't want to lose backwards compatibility.
+
+This guide is to help you on your way. In addition, many of the examples in the examples directory append and read arguments.
+
+Code generation
+---------------
+
+First - if you can get D-Bus introspection data, you can use the the `dbus-codegen` tool to generate some boilerplate code for you. E g, if you want to talk to NetworkManager:
+
+```rust
+cargo install dbus-codegen
+dbus-codegen-rust -s -g -m None -d org.freedesktop.NetworkManager -p /org/freedesktop/NetworkManager > networkmanager.rs
+```
+
+You would then use this code like:
+
+```rust
+// main.rs
+mod networkmanager;
+
+/* ... */
+
+// Start a connection to the system bus.
+let c = Connection::get_private(BusType::System)?;
+
+// Make a "ConnPath" struct that just contains a Connection, a destination and a path.
+let p = c.with_path("org.freedesktop.NetworkManager", "/org/freedesktop/NetworkManager", 5000);
+
+// Bring our generated code into scope.
+use networkmanager::OrgFreedesktopNetworkManager;
+
+// Now we can call methods on our connpath from the "org.freedesktop.NetworkManager" interface.
+let devices = c.get_all_devices()?;
+```
+
+There is also pre-generated code for standard D-Bus interfaces in the `stdintf` module. A similar example:
+
+```rust
+let c = Connection::get_private(BusType::Session)?;
+
+// Make a "ConnPath" struct that just contains a Connection, a destination and a path.
+let p = c.with_path("org.mpris.MediaPlayer2.rhythmbox", "/org/mpris/MediaPlayer2", 5000);
+
+// The ConnPath struct implements many traits, e g `org.freedesktop.DBus.Properties`. Bring the trait into scope.
+use stdintf::org_freedesktop_dbus::Properties;
+
+// Now we can call org.freedesktop.DBus.Properties.Get just like an ordinary method and get the result back.
+let metadata = p.get("org.mpris.MediaPlayer2.Player", "Metadata")?;
+```
+
+For more details, see `dbus-codegen-rust --help` and the `README.md` in the dbus-codegen directory.
+
+Now, if you want to make a service yourself, the generated code is more complex. And for some use cases, codegen isn't really an option, so let's move on:
+
+Append / get basic types
+------------------------
+
+If you just want to get/append simple types, just use `append1` / `append2` / `append3`, and
+`read1` / `read2` / `read3`. The imaginary method below takes one byte parameter and one string parameter, and returns one string parameter and one int parameter.
+
+```rust
+let m = Message::new_method_call(dest, path, intf, member)?.append2(5u8, "Foo");
+let r = c.send_with_reply_and_block(m, 2000)?;
+let (data1, data2): (&str, i32) = c.read2()?;
+```
+
+Arrays and dictionaries
+-----------------------
+
+D-Bus arrays and dictionaries usually correspond to `Vec` and `HashMap`. You can just append and get them like basic types:
+
+```rust
+let v = vec![3i32, 4i32, 5i32];
+let mut map = HashMap::new();
+map.insert("Funghi", 5u16);
+map.insert("Mold", 8u16);
+
+let m = Message::new_method_call(dest, path, intf, member)?.append2(v, map);
+let r = c.send_with_reply_and_block(m, 2000)?;
+let (data1, data2): (Vec<i32>, HashMap<&str, u16>) = r.read2()?;
+```
+
+Or combine them as you wish, e g, use a `Vec<Vec<u8>>`, a `HashMap<u64, Vec<String>>` or `HashMap<String, HashMap<String, i32>>` to construct more difficult types.
+
+Slices can sometimes be used as arrays - e g, `&[&str]` can be appended, but only very simple types can be used with `get` and `read`, e g `&[u8]`.
+
+This is the easiest way to get started, but in case you want to avoid the overhead of creating `Vec` or `HashMap`s, the "Array and Dict types" and "Iter / IterAppend" sections offer useful alternatives.
+
+Variants
+--------
+
+Things are getting slightly more complex with Variants, because they are not strongly typed and thus not fit as well into Rust's strongly typed as arrays and dicts.
+
+If you know the type beforehand, it's still easy:
+
+```rust
+let v = Variant("This is a variant containing a &str");
+let m = Message::new_method_call(dest, path, intf, member)?.append1(v);
+let r = c.send_with_reply_and_block(m, 2000)?;
+let z: Variant<i32> = r.read1()?;
+println!("Method returned {}", z.0);
+```
+
+The `Variant` struct is just a wrapper with a public interior, so you can easily both read from it and write to it with the `.0` accessor.
+
+Sometimes you don't know the type beforehand. We can solve this in two ways (choose whichever is more appropriate for your use case), either through the trait object `Box<RefArg>` or through `Iter` / `IterAppend` (see later sections).
+
+Through trait objects:
+
+```rust
+let x = Box::new(5000i32) as Box<RefArg>;
+let m = Message::new_method_call(dest, path, intf, member)?.append1(Variant(x));
+let r = c.send_with_reply_and_block(m, 2000)?;
+let z: Variant<Box<RefArg>> = r.read1()?;
+```
+
+Ok, so we retrieved our `Box<RefArg>`. We now need to use the `RefArg` methods to probe it, to see what's inside. Easiest is to use `as_i64` or `as_str` if you want to test for integer or string types. Use `as_iter` if the variant contains a complex type you need to iterate over.
+For floating point values, use `arg::cast` (this requires that the RefArg is `static` though, due to Rust type system limitations).
+Match over `arg_type` if you need to know the exact type.
+
+
+```rust
+let z: Variant<Box<RefArg + 'static>> = r.read1()?;
+let value = &z.0;
+
+if let Some(s) = value.as_str() { println!("It's a string: {}", s); }
+else if let Some(i) = value.as_i64() { println!("It's an integer: {}", i); }
+else if let Some(f) = arg::cast::<f64>(value) { println!("It's a float: {}", f); }
+else { println!("Don't know how to handle a {:?}", value.arg_type()) }
+```
+
+Dicts and variants are sometimes combined, e g, you might need to read a D-Bus dictionary of String to Variants. You can then read these as `HashMap<String, Variant<Box<RefArg>>>`.
+
+Structs
+-------
+
+D-Bus structs are implemented as Rust tuples. You can append and get tuples like you do with other types of arguments.
+
+TODO: Example
+
+Declare method arguments
+------------------------
+
+When you make a `Tree`, you want to declare what input and output arguments your method expects - so that correct D-Bus introspection data can be generated. You'll use the same types as you learned earlier in this guide:
+
+```rust
+factory.method( /* ... */ )
+.inarg::<HashMap<i32, Vec<(i32, bool, String)>>,_>("request")
+.outarg::<&str,_>("reply")
+```
+
+The types are just for generating a correct signature, they are never instantiated. Many different types can generate the same signature - e g, `Array<u8, _>`, `Vec<u8>` and `&[u8]` will all generate the same signature. `Variant` will generate the same type signature regardless of what's inside, so just write `Variant<()>` for simplicity.
+
+
+Iter / IterAppend
+-----------------
+
+Iter and IterAppend are more low-level, direct methods to get and append arguments. They can, e g, come handy if you have more than five arguments to read.
+
+E g, for appending a variant with IterAppend you can use `IterAppend::new(&msg).append_variant(|i| i.append(5000i32))` to append what you need to your variant inside the closure.
+To read a variant you can use `let i = msg.read1::<Variant<Iter>>::()?` and then examine the methods on `i.0` to probe the variant.
+
+Array and Dict types
+--------------------
+
+These provide slightly better flexibility than using `Vec` and `HashMap` by instead integrating with `Iterator`. Here's an example where you can append and get a dictionary without having to create a HashMap:
+
+```rust
+let x = &[("Hello", true), ("World", false)];
+let m = Message::new_method_call(dest, path, intf, member)?.append1(Dict::new(x));
+let r = c.send_with_reply_and_block(m, 2000)?;
+let z: Dict<i32, &str, _> = r.read1()?;
+for (key, value) in z { /* do something */ }
+```
+
+An edge case where this is necessary is having floating point keys in a dictionary - this is supported in D-Bus but not in Rust's `HashMap`. I have never seen this in practice, though.
+
+Unusual types
+-------------
+
+The types `Path`, `Signature` and `OwnedFd` are not often used, but they can be appended and read as other argument types. `Path` and `Signature` will return strings with a borrowed lifetime - use `.into_static()` if you want to untie that lifetime.
+
+For `OwnedFd`, which a wrapper around a file descriptor, remember that the file descriptor will be closed when it goes out of scope.
+
+MessageItem
+-----------
+
+MessageItem was the first design - an enum representing a D-Bus argument. It still works, but I doubt you'll ever need to use it. Newer methods provide better type safety, speed, and ergonomics.
+
+
diff --git a/third_party/rust/dbus/examples/client.rs b/third_party/rust/dbus/examples/client.rs
new file mode 100644
index 0000000000..a8707013ed
--- /dev/null
+++ b/third_party/rust/dbus/examples/client.rs
@@ -0,0 +1,14 @@
+extern crate dbus;
+
+use dbus::{Connection, BusType, Message};
+use dbus::arg::Array;
+
+fn main() {
+ let c = Connection::get_private(BusType::Session).unwrap();
+ let m = Message::new_method_call("org.freedesktop.DBus", "/", "org.freedesktop.DBus", "ListNames").unwrap();
+ let r = c.send_with_reply_and_block(m, 2000).unwrap();
+ // ListNames returns one argument, which is an array of strings.
+ let arr: Array<&str, _> = r.get1().unwrap();
+ for name in arr { println!("{}", name); }
+}
+
diff --git a/third_party/rust/dbus/examples/properties.rs b/third_party/rust/dbus/examples/properties.rs
new file mode 100644
index 0000000000..a410fc2ec1
--- /dev/null
+++ b/third_party/rust/dbus/examples/properties.rs
@@ -0,0 +1,50 @@
+extern crate dbus;
+
+use dbus::{Connection, BusType, stdintf, arg};
+use std::collections::HashMap;
+
+fn print_refarg(value: &arg::RefArg) {
+ // We don't know what type the value is. We'll try a few and fall back to
+ // debug printing if the value is more complex than that.
+ if let Some(s) = value.as_str() { println!("{}", s); }
+ else if let Some(i) = value.as_i64() { println!("{}", i); }
+ else { println!("{:?}", value); }
+}
+
+fn main() {
+ // Connect to server and create a ConnPath. A ConnPath implements several interfaces,
+ // in this case we'll use OrgFreedesktopDBusProperties, which allows us to call "get".
+ let c = Connection::get_private(BusType::Session).unwrap();
+ let p = c.with_path("org.mpris.MediaPlayer2.rhythmbox", "/org/mpris/MediaPlayer2", 5000);
+ use stdintf::org_freedesktop_dbus::Properties;
+
+ // The Metadata property is a Dict<String, Variant>.
+
+ // Option 1: we can get the dict straight into a hashmap, like this:
+
+ let metadata: HashMap<String, arg::Variant<Box<arg::RefArg>>> = p.get("org.mpris.MediaPlayer2.Player", "Metadata").unwrap();
+
+ println!("Option 1:");
+
+ // We now iterate over the hashmap.
+ for (key, value) in metadata.iter() {
+ print!(" {}: ", key);
+ print_refarg(&value);
+ }
+
+
+ // Option 2: we can get the entire dict as a RefArg and get the values out by iterating over it.
+
+ let metadata: Box<arg::RefArg> = p.get("org.mpris.MediaPlayer2.Player", "Metadata").unwrap();
+
+ // When using "as_iter()" for a dict, we'll get one key, it's value, next key, it's value, etc.
+ let mut iter = metadata.as_iter().unwrap();
+
+ println!("Option 2:");
+ while let Some(key) = iter.next() {
+ // Printing the key is easy, since we know it's a String.
+ print!(" {}: ", key.as_str().unwrap());
+ let value = iter.next().unwrap();
+ print_refarg(&value);
+ }
+}
diff --git a/third_party/rust/dbus/examples/properties_msgitem.rs b/third_party/rust/dbus/examples/properties_msgitem.rs
new file mode 100644
index 0000000000..86e1aa5086
--- /dev/null
+++ b/third_party/rust/dbus/examples/properties_msgitem.rs
@@ -0,0 +1,10 @@
+extern crate dbus;
+
+use dbus::{Connection, BusType, Props};
+
+fn main() {
+ let c = Connection::get_private(BusType::System).unwrap();
+ let p = Props::new(&c, "org.freedesktop.PolicyKit1", "/org/freedesktop/PolicyKit1/Authority",
+ "org.freedesktop.PolicyKit1.Authority", 10000);
+ println!("BackendVersion: {:?}", p.get("BackendVersion").unwrap())
+}
diff --git a/third_party/rust/dbus/examples/rtkit.rs b/third_party/rust/dbus/examples/rtkit.rs
new file mode 100644
index 0000000000..2717d417b9
--- /dev/null
+++ b/third_party/rust/dbus/examples/rtkit.rs
@@ -0,0 +1,67 @@
+/* This example asks the rtkit service to make our thread realtime priority.
+ Rtkit puts a few limitations on us to let us become realtime, such as setting
+ RLIMIT_RTTIME correctly, hence the syscalls. */
+
+extern crate dbus;
+extern crate libc;
+
+use std::cmp;
+
+use dbus::{Connection, BusType, Props, MessageItem, Message};
+
+fn item_as_i64(i: MessageItem) -> Result<i64, Box<std::error::Error>> {
+ match i {
+ MessageItem::Int32(i) => Ok(i as i64),
+ MessageItem::Int64(i) => Ok(i),
+ _ => Err(Box::from(&*format!("Property is not integer ({:?})", i)))
+ }
+}
+
+fn rtkit_set_realtime(c: &Connection, thread: u64, prio: u32) -> Result<(), ::dbus::Error> {
+ let mut m = Message::new_method_call("org.freedesktop.RealtimeKit1", "/org/freedesktop/RealtimeKit1",
+ "org.freedesktop.RealtimeKit1", "MakeThreadRealtime").unwrap();
+ m.append_items(&[thread.into(), prio.into()]);
+ let mut r = try!(c.send_with_reply_and_block(m, 10000));
+ r.as_result().map(|_| ())
+}
+
+fn make_realtime(prio: u32) -> Result<u32, Box<std::error::Error>> {
+ let c = try!(Connection::get_private(BusType::System));
+
+ let p = Props::new(&c, "org.freedesktop.RealtimeKit1", "/org/freedesktop/RealtimeKit1",
+ "org.freedesktop.RealtimeKit1", 10000);
+
+ // Make sure we don't fail by wanting too much
+ let max_prio = try!(item_as_i64(try!(p.get("MaxRealtimePriority")))) as u32;
+ let prio = cmp::min(prio, max_prio);
+
+ // Enforce RLIMIT_RTPRIO, also a must before asking rtkit for rtprio
+ let max_rttime = try!(item_as_i64(try!(p.get("RTTimeUSecMax")))) as u64;
+ let new_limit = libc::rlimit64 { rlim_cur: max_rttime, rlim_max: max_rttime };
+ let mut old_limit = new_limit;
+ if unsafe { libc::getrlimit64(libc::RLIMIT_RTTIME, &mut old_limit) } < 0 {
+ return Err(Box::from("getrlimit failed"));
+ }
+ if unsafe { libc::setrlimit64(libc::RLIMIT_RTTIME, &new_limit) } < 0 {
+ return Err(Box::from("setrlimit failed"));
+ }
+
+ // Finally, let's ask rtkit to make us realtime
+ let thread_id = unsafe { libc::syscall(libc::SYS_gettid) };
+ let r = rtkit_set_realtime(&c, thread_id as u64, prio);
+
+ if r.is_err() {
+ unsafe { libc::setrlimit64(libc::RLIMIT_RTTIME, &old_limit) };
+ }
+
+ try!(r);
+ Ok(prio)
+}
+
+
+fn main() {
+ match make_realtime(5) {
+ Ok(n) => println!("Got rtprio, level {}", n),
+ Err(e) => println!("No rtprio: {}", e),
+ }
+}
diff --git a/third_party/rust/dbus/examples/server.rs b/third_party/rust/dbus/examples/server.rs
new file mode 100644
index 0000000000..105593fccc
--- /dev/null
+++ b/third_party/rust/dbus/examples/server.rs
@@ -0,0 +1,72 @@
+/* This example creates a D-Bus server with the following functionality:
+ It registers the "com.example.dbustest" name, creates a "/hello" object path,
+ which has an "com.example.dbustest" interface.
+
+ The interface has a "Hello" method (which takes no arguments and returns a string),
+ and a "HelloHappened" signal (with a string argument) which is sent every time
+ someone calls the "Hello" method.
+*/
+
+
+extern crate dbus;
+
+use std::sync::Arc;
+use dbus::{Connection, BusType, NameFlag};
+use dbus::tree::Factory;
+
+fn main() {
+ // Let's start by starting up a connection to the session bus and register a name.
+ let c = Connection::get_private(BusType::Session).unwrap();
+ c.register_name("com.example.dbustest", NameFlag::ReplaceExisting as u32).unwrap();
+
+ // The choice of factory tells us what type of tree we want,
+ // and if we want any extra data inside. We pick the simplest variant.
+ let f = Factory::new_fn::<()>();
+
+ // We create the signal first, since we'll need it in both inside the method callback
+ // and when creating the tree.
+ let signal = Arc::new(f.signal("HelloHappened", ()).sarg::<&str,_>("sender"));
+ let signal2 = signal.clone();
+
+ // We create a tree with one object path inside and make that path introspectable.
+ let tree = f.tree(()).add(f.object_path("/hello", ()).introspectable().add(
+
+ // We add an interface to the object path...
+ f.interface("com.example.dbustest", ()).add_m(
+
+ // ...and a method inside the interface.
+ f.method("Hello", (), move |m| {
+
+ // This is the callback that will be called when another peer on the bus calls our method.
+ // the callback receives "MethodInfo" struct and can return either an error, or a list of
+ // messages to send back.
+
+ let name: &str = m.msg.read1()?;
+ let s = format!("Hello {}!", name);
+ let mret = m.msg.method_return().append1(s);
+
+ let sig = signal.msg(m.path.get_name(), m.iface.get_name())
+ .append1(&*name);
+
+ // Two messages will be returned - one is the method return (and should always be there),
+ // and in our case we also have a signal we want to send at the same time.
+ Ok(vec!(mret, sig))
+
+ // Our method has one output argument and one input argument.
+ }).outarg::<&str,_>("reply")
+ .inarg::<&str,_>("name")
+
+ // We also add the signal to the interface. This is mainly for introspection.
+ ).add_s(signal2)
+ ));
+
+ // We register all object paths in the tree.
+ tree.set_registered(&c, true).unwrap();
+
+ // We add the tree to the connection so that incoming method calls will be handled
+ // automatically during calls to "incoming".
+ c.add_handler(tree);
+
+ // Serve other peers forever.
+ loop { c.incoming(1000).next(); }
+}
diff --git a/third_party/rust/dbus/examples/unity_focused_window.rs b/third_party/rust/dbus/examples/unity_focused_window.rs
new file mode 100644
index 0000000000..a0e07d0372
--- /dev/null
+++ b/third_party/rust/dbus/examples/unity_focused_window.rs
@@ -0,0 +1,25 @@
+extern crate dbus;
+
+// Tracks currently focused window under the Unity desktop by listening to the
+// FocusedWindowChanged signal. The signal contains "window_id", "app_id" and "stage",
+// we print only "app_id".
+
+use dbus::{Connection, BusType, ConnectionItem};
+
+fn focus_msg(ci: &ConnectionItem) -> Option<&str> {
+ let m = if let &ConnectionItem::Signal(ref s) = ci { s } else { return None };
+ if &*m.interface().unwrap() != "com.canonical.Unity.WindowStack" { return None };
+ if &*m.member().unwrap() != "FocusedWindowChanged" { return None };
+ let (_, app) = m.get2::<u32, &str>();
+ app
+}
+
+fn main() {
+ let c = Connection::get_private(BusType::Session).unwrap();
+ c.add_match("interface='com.canonical.Unity.WindowStack',member='FocusedWindowChanged'").unwrap();
+
+ for i in c.iter(1000) {
+ if let Some(app) = focus_msg(&i) { println!("{} has now focus.", app) };
+ }
+}
+