dbus2 / zbus

Rust D-Bus crate.
Other
341 stars 75 forks source link

`interface` should allow adding an unexposed setter #898

Open zeenix opened 1 month ago

zeenix commented 1 month ago

Typically you want properties to be read-only on the bus but settable from the service process itself. When you do set them manually, you need to write a bit of a complicated code:

struct MyIface(u32);

#[interface(name = "org.myiface.MyIface")]
impl MyIface {
     #[zbus(property)]
     async fn count(&self) -> u32 {
         self.0
     }
}

let iface_ref = connection
    .object_server()
    .interface::<_, MyIface>(path).await?;
let mut iface = iface_ref.get_mut().await;
iface.0 = 42;
iface.count_changed(iface_ref.signal_context()).await?;

It would be great if allowed adding a non-exposed setter in interface that sets the value and each time it's called, it handles the signal emission for you. So the property setting code above becomes:

let iface_ref = connection
    .object_server()
    .interface::<_, MyIface>(path).await?
    .set_count(42)?;
gnulux commented 2 weeks ago

Hi ,

Does this code even works ? When i try it always have Error:InterfaceNotFound.

rgds

zeenix commented 2 weeks ago

Does this code even works ? When i try it always have Error:InterfaceNotFound.

The first code should work. If you're getting that error, you likely didn't add the interface to the ObjectServer.

zeenix commented 2 weeks ago

Does this code even works ? When i try it always have Error:InterfaceNotFound.

The first code should work. If you're getting that error, you likely didn't add the interface to the ObjectServer.

I noticed now that the sample code above, doesn't add the interface either. It's mainly for demonstrating my point. Please see our docs (or just the README) to find out how to do this.

gnulux commented 2 weeks ago

Hi , I spent time on the doc but still can see what am i doing wrong in my example below( not a Rust expert by the way). I can update propertis if i define their dbus setters and use proxy. At the end i would like to keep my properties in readOnly and update the value from the service.

use tokio::{task, time::Duration};
use zbus::names::InterfaceName;
use zbus::{connection, interface, SignalContext};
use zbus::fdo::PropertiesProxy;
use zbus::{Connection, Result, zvariant};
use zvariant::{OwnedValue};

#[derive(Clone)] 
struct Greeter {
    offset: i32,
}

#[interface(name = "org.example.TestInterface")]
impl Greeter {

    /// A "GreeterName" property.
    #[zbus(property)]
    async fn offset(&self) -> i32 {
        self.offset
    }

    //#[zbus(property)]
    async fn  set_offset(&mut self, o: i32) {
        self.offset = o;
        println!("Update offset is called with value : {o}");
    }

}

#[tokio::main]
async fn main() -> Result<(),> {

    let mut greeter = Greeter {
        offset: 23
    };

    // let mut g = greeter.clone();

    let _conn = connection::Builder::session()?
        .name("org.example.TestServer")?
        .serve_at("/org/example/TestServer", greeter.clone())?
        .build()
        .await?;

    let connect = Connection::session().await?;

    tokio::time::sleep(Duration::from_secs(4)).await;

    let path = zbus::zvariant::ObjectPath::try_from("/org/example/TestServer")?;

    connect
    .object_server()
    .at(path.clone(), greeter)
    .await?;

    let iface_ref = connect
        .object_server()
        .interface::<_, Greeter>(path).await?;

    let mut iface = iface_ref.get_mut().await;

    iface.offset = 120;
    let _ = iface.offset_changed(iface_ref.signal_context()).await;

    let _ = task::spawn(async move {
        let mut v:i32 = 0;

        loop {

            println!("offset = {v}");

            tokio::time::sleep(Duration::from_secs(2)).await;
            v+=1;
        }

    }).await;

    Ok(())
}
zeenix commented 2 weeks ago

@gnulux I'm very sorry to hear you're having trouble with zbus but this isn't the right place for getting help. Kindly join our matrix channel (#zbus:matrix.org) or create a discussion topic here. Thanks.