Vala D-Bus Examples
Contents
Vala supports D-Bus inter-process communication using the GDBus API that is part of GLib/GIO since version 2.26.
Vala automatically transforms Vala style lower_case_names to D-Bus style CamelCaseNames behind the scenes. You can use methods, signals, properties in D-Bus objects as if they were Vala objects.
Using GDBus
Server
/* Note: this attribute specifies the _interface_ name. It
* is called 'name =' for historical reasons.
*/
[DBus (name = "org.example.Demo")]
public class DemoServer : Object {
private int counter;
public int ping (string msg) {
stdout.printf ("%s\n", msg);
return counter++;
}
public int ping_with_signal (string msg) {
stdout.printf ("%s\n", msg);
pong(counter, msg);
return counter++;
}
/* Including any parameter of type GLib.BusName won't be added to the
interface and will return the dbus sender name (who is calling the method) */
public int ping_with_sender (string msg, GLib.BusName sender) {
stdout.printf ("%s, from: %s\n", msg, sender);
return counter++;
}
public void ping_error () throws Error {
throw new DemoError.SOME_ERROR ("There was an error!");
}
public signal void pong (int count, string msg);
}
[DBus (name = "org.example.DemoError")]
public errordomain DemoError
{
SOME_ERROR
}
void on_bus_aquired (DBusConnection conn) {
try {
conn.register_object ("/org/example/demo", new DemoServer ());
} catch (IOError e) {
stderr.printf ("Could not register service\n");
}
}
void main () {
Bus.own_name (BusType.SESSION, "org.example.Demo", BusNameOwnerFlags.NONE,
on_bus_aquired,
() => {},
() => stderr.printf ("Could not aquire name\n"));
new MainLoop ().run ();
}
$ valac --pkg gio-2.0 gdbus-demo-server.vala
Client
The methods of the client interface must be defined with throws IOError.
[DBus (name = "org.example.Demo")]
interface Demo : Object {
public abstract int ping (string msg) throws IOError;
public abstract int ping_with_sender (string msg) throws IOError;
public abstract int ping_with_signal (string msg) throws IOError;
public signal void pong (int count, string msg);
}
void main () {
/* Needed only if your client is listening to signals; you can omit it otherwise */
var loop = new MainLoop();
/* Important: keep demo variable out of try/catch scope not lose signals! */
Demo demo = null;
try {
demo = Bus.get_proxy_sync (BusType.SESSION, "org.example.Demo",
"/org/example/demo");
/* Connecting to signal pong! */
demo.pong.connect((c, m) => {
stdout.printf ("Got pong %d for msg '%s'\n", c, m);
loop.quit ();
});
int reply = demo.ping ("Hello from Vala");
stdout.printf ("%d\n", reply);
reply = demo.ping_with_sender ("Hello from Vala with sender");
stdout.printf ("%d\n", reply);
reply = demo.ping_with_signal ("Hello from Vala with signal");
stdout.printf ("%d\n", reply);
} catch (IOError e) {
stderr.printf ("%s\n", e.message);
}
loop.run();
}
$ valac --pkg gio-2.0 gdbus-demo-client.vala
Type Table
D-Bus |
Vala |
Description |
Example |
b |
bool |
Boolean |
|
y |
uint8 |
Byte |
|
i |
int |
Integer |
|
u |
uint |
Unsigned Integer |
|
n |
int16 |
16-bit Integer |
|
q |
uint16 |
Unsigned 16-bit Integer |
|
x |
int64 |
64-bit Integer |
|
t |
uint64 |
Unsigned 64-bit Integer |
|
d |
double |
Double |
|
s |
string |
String |
|
v |
GLib.Variant |
Variant |
|
o |
GLib.ObjectPath |
Object Path |
|
a |
[] |
Array |
ai maps to int[] |
a{} |
GLib.HashTable<,> |
Dictionary |
a{sv} maps to HashTable<string, Variant> |
() |
a struct type |
Struct |
a(ii) maps to Foo[] where Foo might be defined as |
Debugging D-Bus Applications
D-Feet
D-Feet is a graphical D-Bus debugger. This is what our little D-Bus service looks like in D-Feet:
dbus-monitor
Open a terminal and enter:
$ dbus-monitor
Excerpt from the output showing a property change notification:
signal sender=:1.454 -> dest=(null destination) serial=9 path=/org/example/demo; interface=org.freedesktop.DBus.Properties; member=PropertiesChanged string "org.example.Demo" array [ dict entry( string "pubprop" variant string "1018873421" ) ] array [ ]
Service with D-Bus property change notifications
This example will setup a D-Bus service that can send notifications on the change of properties. (example code partly by Faheem)
The timeout will change the property every few seconds. The notifications can be visualized by the terminal program 'dbus-monitor' that comes with most distributions.
[DBus (name = "org.example.Demo")]
public class DemoServer : Object {
public string pubprop { owned get; set; }
private weak DBusConnection conn;
public DemoServer (DBusConnection conn) {
this.conn = conn;
this.notify.connect (send_property_change);
}
private void send_property_change (ParamSpec p) {
var builder = new VariantBuilder (VariantType.ARRAY);
var invalid_builder = new VariantBuilder (new VariantType ("as"));
if (p.name == "pubprop") {
Variant i = pubprop;
builder.add ("{sv}", "pubprop", i);
}
try {
conn.emit_signal (null,
"/org/example/demo",
"org.freedesktop.DBus.Properties",
"PropertiesChanged",
new Variant ("(sa{sv}as)",
"org.example.Demo",
builder,
invalid_builder)
);
} catch (Error e) {
stderr.printf ("%s\n", e.message);
}
}
}
public class NotificationsTest : Object {
private DemoServer dserver;
public NotificationsTest () {
Bus.own_name (BusType.SESSION, "org.example.Demo", BusNameOwnerFlags.NONE,
on_bus_acquired, on_name_acquired, on_name_lost);
}
private void on_bus_acquired (DBusConnection conn) {
print ("bus acquired\n");
try {
this.dserver = new DemoServer (conn);
conn.register_object ("/org/example/demo", this.dserver);
} catch (IOError e) {
print ("%s\n", e.message);
}
}
private void on_name_acquired () {
print ("name acquired\n");
}
private void on_name_lost () {
print ("name_lost\n");
}
public void setup_timeout () {
Timeout.add_seconds (4, () => {
dserver.pubprop = Random.next_int ().to_string ();
return true;
});
}
}
void main () {
var nt = new NotificationsTest ();
nt.setup_timeout ();
new MainLoop ().run ();
}
$ valac --pkg gio-2.0 gdbus-change-notificationst.vala