<chapter> <title>Migrating to GDBus</title> <section> <title>Conceptual differences</title> <para> 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: <itemizedlist> <listitem><para> 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. </para></listitem> <listitem><para> 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. </para></listitem> <listitem><para> The typical way to export an object in dbus-glib involves generating glue code from XML introspection data using <command>dbus-binding-tool</command>. GDBus does not (yet?) use code generation; you are expected to embed the introspection data in your application code. </para></listitem> </itemizedlist> </para> </section> <section> <title>API comparison</title> <table id="dbus-glib-vs-gdbus"> <title>dbus-glib APIs and their GDBus counterparts</title> <tgroup cols="2"> <thead> <row><entry>dbus-glib</entry><entry>GDBus</entry></row> </thead> <tbody> <row><entry>#DBusGConnection</entry><entry>#GDBusConnection</entry></row> <row><entry>#DBusGProxy</entry><entry>#GDBusProxy</entry></row> <row><entry>#DBusGMethodInvocation</entry><entry>#GDBusMethodInvocation</entry></row> <row><entry>dbus_g_bus_get()</entry><entry>g_bus_get_sync(), also see g_bus_get()</entry></row> <row><entry>dbus_g_proxy_new_for_name()</entry><entry>g_dbus_proxy_new_sync() and g_dbus_proxy_new_for_bus_sync(), also see g_dbus_proxy_new()</entry></row> <row><entry>dbus_g_proxy_add_signal()</entry><entry>not needed, use the generic #GDBusProxy::g-signal</entry></row> <row><entry>dbus_g_proxy_connect_signal()</entry><entry>use g_signal_connect() with #GDBusProxy::g-signal</entry></row> <row><entry>dbus_g_connection_register_g_object()</entry><entry>g_dbus_connection_register_object()</entry></row> <row><entry>dbus_g_connection_unregister_g_object()</entry><entry>g_dbus_connection_unregister_object()</entry></row> <row><entry>dbus_g_object_type_install_info()</entry><entry>introspection data is installed while registering an object, see g_dbus_connection_register_object()</entry></row> <row><entry>dbus_g_proxy_begin_call()</entry><entry>g_dbus_proxy_call()</entry></row> <row><entry>dbus_g_proxy_end_call()</entry><entry>g_dbus_proxy_call_finish()</entry></row> <row><entry>dbus_g_proxy_call()</entry><entry>g_dbus_proxy_call_sync()</entry></row> <row><entry>dbus_g_error_domain_register()</entry><entry>g_dbus_error_register_error_domain()</entry></row> <row><entry>dbus_g_error_has_name()</entry><entry>no direct equivalent, see g_dbus_error_get_remote_error()</entry></row> <row><entry>dbus_g_method_return()</entry><entry>g_dbus_method_invocation_return_value()</entry></row> <row><entry>dbus_g_method_return_error()</entry><entry>g_dbus_method_invocation_return_error() and variants</entry></row> <row><entry>dbus_g_method_get_sender()</entry><entry>g_dbus_method_invocation_get_sender()</entry></row> </tbody> </tgroup> </table> </section> <section> <title>Owning bus names</title> <para> Using dbus-glib, you typically call RequestName manually to own a name, like in the following excerpt: <informalexample><programlisting><![CDATA[ error = NULL; res = dbus_g_proxy_call (system_bus_proxy, "RequestName", &error, G_TYPE_STRING, NAME_TO_CLAIM, G_TYPE_UINT, DBUS_NAME_FLAG_ALLOW_REPLACEMENT, G_TYPE_INVALID, G_TYPE_UINT, &result, G_TYPE_INVALID); if (!res) { 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); } 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 ... */ ]]> </programlisting></informalexample> </para> <para> 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: <informalexample><programlisting><![CDATA[ static void on_name_acquired (GDBusConnection *connection, const gchar *name, gpointer user_data) { /* further setup ... */ } /* ... */ owner_id = g_bus_own_name (G_BUS_TYPE_SYSTEM, NAME_TO_CLAIM, G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT, on_bus_acquired, on_name_acquired, on_name_lost, NULL, NULL); g_main_loop_run (loop); g_bus_unown_name (owner_id); ]]> </programlisting></informalexample> 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 <emphasis>before</emphasis> acquiring the name. The on_bus_acquired() callback is the right place to do such preparations. </para> </section> <section> <title>Creating proxies for well-known names</title> <para> dbus-glib lets you create proxy objects for well-known names, like the following example: <informalexample><programlisting><![CDATA[ proxy = dbus_g_proxy_new_for_name (system_bus_connection, "org.freedesktop.Accounts", "/org/freedesktop/Accounts", "org.freedesktop.Accounts"); ]]> </programlisting></informalexample> 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. </para> <para> The same can be achieved with #GDBusProxy: <informalexample><programlisting><![CDATA[ error = NULL; proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_NONE, NULL, /* GDBusInterfaceInfo */ "org.freedesktop.Accounts", "/org/freedesktop/Accounts", "org.freedesktop.Accounts", NULL, /* GCancellable */ &error); ]]> </programlisting></informalexample> For an added layer of safety, you can specify what D-Bus interface the proxy is expected to conform to by using the #GDBusInterfaceInfo type. </para> <para> Additionally, #GDBusProxy loads, caches and tracks changes to the D-Bus properties on the remote object. It also sets up match rules so D-Bus signals from the remote object are delivered locally. </para> </section> <section> <title>Client-side GObject bindings</title> <para> dbus-glib comes with <command>dbus-binding-tool</command>, 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. </para> <para> For an example of a #GDBusProxy-derived class that wraps a D-Bus interface in a type-safe way, see <xref linkend="gdbus-example-proxy-subclass"/>. The comparison is as follows: <table id="gdbus-example-type-safe-proxy"> <title>Wrapping the org.freedesktop.Accounts.User D-Bus interface in the AccountUser GObject type</title> <tgroup cols="2"> <thead> <row><entry>D-Bus concept</entry><entry>GObject concept</entry></row> </thead> <tbody> <row> <entry>AutomaticLogin property</entry> <entry> <para><literal>AccountsUser:automatic-login</literal> GObject property</para> <para>C getter: accounts_user_get_automatic_login()</para> <para>Watch changes via the <literal>notify::automatic-login</literal> signal</para> </entry> </row> <row> <entry>RealName property</entry> <entry> <para><literal>AccountsUser:real-name</literal> GObject property</para> <para>C getter: accounts_user_get_real_name()</para> <para>Watch changes via the <literal>notify::real-name signal</literal></para> </entry> </row> <row> <entry>UserName property</entry> <entry> <para><literal>AccountsUser:user-name</literal> GObject property</para> <para>C getter: accounts_user_get_user_name()</para> <para>Watch changes via the <literal>notify::user-name</literal> signal</para> </entry> </row> <row> <entry>Changed signal</entry> <entry> <para><literal>AccountsUser::changed</literal> GObject signal</para> <para>Watch via e.g. g_signal_connect()</para> </entry> </row> <row> <entry>Frobnicate method</entry> <entry> <para>Use accounts_user_frobnicate() + accounts_user_frobnicate_finish() or accounts_user_frobnicate_sync() to invoke</para> </entry> </row> </tbody> </tgroup> </table> </para> <example id="gdbus-example-proxy-subclass"><title>GDBusProxy subclass example</title><programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" parse="text" href="../../../../gio/tests/gdbus-example-proxy-subclass.c"><xi:fallback>FIXME: MISSING XINCLUDE CONTENT</xi:fallback></xi:include></programlisting></example> </section> <section> <title>Exporting objects</title> <para> With dbus-glib, exporting an object over D-Bus works by generating a bunch of glue code from your introspection XML with <command>dbus-binding-tool</command>. The glue code gets included in your source, and you need to call <informalexample><programlisting> dbus_g_object_type_install_info (TYPE_MYOBJECT, &dbus_glib_myobject_object_info); </programlisting></informalexample> in your class_init() function to tell dbus-glib about your type. To actually export an instance, you call <informalexample><programlisting> dbus_g_connection_register_g_object (system_bus_connection, my_object_path, G_OBJECT (my_object)); </programlisting></informalexample> </para> <para> 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: <informalexample><programlisting><![CDATA[ static const gchar introspection_xml[] = "<node>" " <interface name='org.gtk.GDBus.TestPeerInterface'>" " <method name='HelloWorld'>" " <arg type='s' name='greeting' direction='in'/>" " <arg type='s' name='response' direction='out'/>" " </method>" " </interface>" "</node>"; /* 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** */ ]]> </programlisting></informalexample> </para> <para> 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. </para> <para> 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. </para> <example id="gdbus-export"><title>Exporting a GObject</title><programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" parse="text" href="../../../../gio/tests/gdbus-example-export.c"><xi:fallback>FIXME: MISSING XINCLUDE CONTENT</xi:fallback></xi:include></programlisting></example> </section> </chapter>