Migrating from dbus-glib to GDBus
Conceptual differences
The central concepts of D-Bus are modelled in a very similar way
in dbus-glib and GDBus. Both have a objects representing connections,
proxies and method invocations. But there are some important
differences:
dbus-glib uses libdbus, GDBus doesn't. Instead, it relies on GIO
streams as transport layer, and has its own implementation for the
the D-Bus connection setup and authentication. Apart from using
streams as transport, avoiding libdbus also lets GDBus avoid some
thorny multithreading issues.
dbus-glib uses the GObject type system for method arguments and
return values, including a homegrown container specialization
mechanism. GDBus relies uses the #GVariant type system which is
explicitly designed to match D-Bus types.
The typical way to export an object in dbus-glib involves generating
glue code from XML introspection data using dbus-binding-tool. GDBus does not (yet?) use code generation; you are expected to
embed the introspection data in your application code.
API comparison
dbus-glib APIs and their GDBus counterparts
dbus-glibGDBus
#DBusGConnection#GDBusConnection
#DBusGProxy#GDBusProxy
#DBusGMethodInvocation#GDBusMethodInvocation
dbus_g_bus_get()g_bus_get_sync(), also see
g_bus_get()
dbus_g_proxy_new_for_name()g_dbus_proxy_new_sync(), also see
g_dbus_proxy_new()
dbus_g_proxy_add_signal()not needed, use the generic #GDBusProxy::g-signal
dbus_g_proxy_connect_signal()use g_signal_connect() with #GDBusProxy::g-signal
dbus_g_connection_register_g_object()g_dbus_connection_register_object()
dbus_g_connection_unregister_g_object()g_dbus_connection_unregister_object()
dbus_g_object_type_install_info()introspection data is installed while registering
an object, see g_dbus_connection_register_object()
dbus_g_proxy_begin_call()g_dbus_proxy_call()
dbus_g_proxy_end_call()g_dbus_proxy_call_finish()
dbus_g_proxy_call()g_dbus_proxy_call_sync()
dbus_g_error_domain_register()g_dbus_error_register_error_domain()
dbus_g_error_has_name()no direct equivalent, see g_dbus_error_get_remote_error()
dbus_g_method_return()g_dbus_method_invocation_return_value()
dbus_g_method_return_error()g_dbus_method_invocation_return_error() and variants
dbus_g_method_get_sender()g_dbus_method_invocation_get_sender()
Owning bus names
Using dbus-glib, you typically call RequestName manually
to own a name, like in the following excerpt:
message);
g_error_free (error);
}
else
{
g_warning ("Failed to acquire %s", NAME_TO_CLAIM);
}
goto out;
}
if (result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER)
{
if (error != NULL)
{
g_warning ("Failed to acquire %s: %s",
NAME_TO_CLAIM, error->message);
g_error_free (error);
}
else
{
g_warning ("Failed to acquire %s", NAME_TO_CLAIM);
}
exit (1);
}
dbus_g_proxy_add_signal (system_bus_proxy, "NameLost",
G_TYPE_STRING, G_TYPE_INVALID);
dbus_g_proxy_connect_signal (system_bus_proxy, "NameLost",
G_CALLBACK (on_name_lost), NULL, NULL);
/* further setup ... */
]]>
While you can do things this way with GDBus too, using
g_dbus_proxy_call_sync(), it is much nicer to use the high-level API
for this:
Note that g_bus_own_name() works asynchronously and requires
you to enter your mainloop to await the on_name_aquired()
callback. Also note that in order to avoid race conditions (e.g.
when your service is activated by a method call), you have to export
your manager object before acquiring the
name. The on_bus_acquired() callback is the right place to do
such preparations.
Creating proxies for well-known names
dbus-glib lets you create proxy objects for well-known names, like the
following example:
For a #DBusGProxy constructed like this, method calls will be sent to
the current owner of the name, and that owner can change over time.
In contrast, #GDBusProxy instances are always bound to a unique name.
To get a proxy for a well-known name, you either have to call
GetNameOwner yourself and construct a proxy for the unique name
of the current name owner, or use the high-level API. The latter
option is highly recommended:
Like g_bus_own_name(), g_bus_watch_proxy() is asynchronous and
you are expected to enter your mainloop to await the on_proxy_appeared()
callback. Note that GDBus also does all the setup operations for the
proxy asynchronously, and only calls your callback when the proxy
is ready for use.
Client-side GObject bindings
dbus-glib comes with dbus-binding-tool, which
can produce somewhat nice client-side wrappers for a D-Bus interface.
GDBus does not have code-generation at this point, but #GDBusProxy
is designed to allow the creating of client-side wrappers by
subclassing #GDBusProxy.
For an example of a #GDBusProxy-derived class that wraps a D-Bus
interface in a type-safe way, see . The comparison is as
follows:
Wrapping the org.freedesktop.Accounts.User D-Bus interface in the AccountUser GObject type
D-Bus conceptGObject concept
AutomaticLogin property
AccountsUser:automatic-login GObject property
C getter: accounts_user_get_automatic_login()
Watch changes via the notify::automatic-login signal
RealName property
AccountsUser:real-name GObject property
C getter: accounts_user_get_real_name()
Watch changes via the notify::real-name signal
UserName property
AccountsUser:user-name GObject property
C getter: accounts_user_get_user_name()
Watch changes via the notify::user-name signal
Changed signal
AccountsUser::changed GObject signal
Watch via e.g. g_signal_connect()
Frobnicate method
Use accounts_user_frobnicate() + accounts_user_frobnicate_finish() or accounts_user_frobnicate_sync() to invoke
GDBusProxy subclass exampleFIXME: MISSING XINCLUDE CONTENT
Exporting objects
With dbus-glib, exporting an object over D-Bus works by generating
a bunch of glue code from your introspection XML with
dbus-binding-tool. The glue code gets included in
your source, and you need to call
dbus_g_object_type_install_info (TYPE_MYOBJECT,
&dbus_glib_myobject_object_info);
in your class_init() function to tell dbus-glib about your type.
To actually export an instance, you call
dbus_g_connection_register_g_object (system_bus_connection,
my_object_path,
G_OBJECT (my_object));
The GDBus way of exporting an object works by embedding the
introspection XML in the source, creating introspection data
structures from it with g_dbus_node_info_new_for_xml(), and
passing that along when you register the object:
"
" "
" "
" "
" "
" "
" "
"";
/* parse introspection data */
introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL);
/
id = g_dbus_connection_register_object (connection,
"/org/gtk/GDBus/TestObject",
"org.gtk.GDBus.TestPeerInterface",
introspection_data->interfaces[0],
&interface_vtable,
NULL, /* user_data */
NULL, /* user_data_free_func */
NULL); /* GError** */
]]>
The actual implementation of the exported object is done by specifying
a #GDBusInterfaceVTable that has method_call(), get_property() and
set_property() methods. There is no direct support beyond that for
exporting #GObjects, so there is quite a bit of manual work involved,
as you can see in the following example.
Since the VTable methods don't have any direct #GObject support, we
pass the exported object as @user_data. Also note that we have to handle
the emission of the PropertiesChanged signal ourselves, by connecting
to ::notify.
Exporting a GObjectFIXME: MISSING XINCLUDE CONTENT