With a Qt DBus proxy built on QDbusAbstractInterface (via qdbusxml2cpp), what's the best way to handle the service/object you want to interface to not being available when you start? Note: I'm not interested in simply knowing it (you can use BlahService.isValid() to find that out); I want to be able to know if it's valid, and know when it becomes valid so I can change state (and broadcast that state change with a signal), and on that state change do other stuff. Conversely, I want to know when it's no longer valid for similar reasons.
Without tracking the state of the service:
#define CONNECT_DBUS_SIG(x,y) connect(blah,SIGNAL(x),this,SLOT(y))
// FIX - should watch for service, and also handle it going away and
// coming back
blah = new BlahService("com.xyzzy.BlahService", "/com/xyzzy/BlahService",
QDBusConnection::sessionBus(), this);
if (!blah)
return 0;
if (blah.isValid())
{
CONNECT_DBUS_SIG(foo(),Event_foo());
}
else
{
// Since we aren't watching for registration, what can we do but exit?
}
Probably we need to watch for NameOwnerChanged on the DBus connection object - unless QT's dbus code does this for us - and then when we get that signal change state, and if needed connect or disconnect the signals from the object.
All the examples I find either ignore the issue or simply exit if the server object doesn't exist, and don't deal with it going away. The Car/Controller Qt example at least notices if the server goes away and prints "Disconnected" if isValid() becomes false during use, but it's polling isValid().
Added:
Note that QtDbusAbtractInterface registers for changes of ownership of the server (NameOwnerChanged), and updates isValid() when changes occur. So I suspect you can connect to that serverOwnerChanged signal directly to find out about changes to ownership and use that as an indicator to try again - though you won't be able to trust isValid since it may be updated before or after you get signaled.
Alternatively (ugly) you can set up a timer and poll for isValid().
Ok, since no one answered, I've found the answer in the meantime:
You want to watch NameOwnerChanged:
// subscribe to notifications about when a service is registered/unregistered
connect(QDBusConnection::sessionBus().interface(),
SIGNAL(serviceOwnerChanged(QString,QString,QString)),
this,SLOT(serviceOwnerChanged(QString,QString,QString)));
and
void
VcsApplicationController::serviceOwnerChanged(const QString &name,
const QString &oldOwner,
const QString &newOwner)
{
Q_UNUSED(oldOwner);
if (name == "com.foo.bar.FooService")
{
qLog(Whatever) << "serviceOwnerChanged" << name << oldOwner << newOwner;
if (!newOwner.isEmpty())
{
// New owner in town
emit Initialized();
// or if you control the interface and both sides, you can wait for
// a "Ready()" signal before declaring FooService ready for business.
}
else
{
// indicate we've lost connection, etc
emit Uninitialized();
}
}
}
Note that there may be race conditions with doing methods on FooService from within serviceOwnerChanged - I'm not sure yet if they're a side-effect of the binding (dbus-c++ in my test case), or inherent in the design of dbus (possible - no on on the dbus mailing list will answer the question). If there is a real race condition, you can wait on a Ready()/whatever signal, if you control the DBus API. If you don't control the other end, you can add a very short delay or you can also watch AddMatch() to make sure the new owner has added a match on the name as well.
With Qt 5.3, serviceOwnerChanged
is deprecated. Use QDBusServiceWatcher
which allows to watch for an specific service instead of all.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With