mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-12-28 16:36:14 +01:00
293 lines
12 KiB
XML
293 lines
12 KiB
XML
<chapter>
|
|
<title>Migrating from dbus-glib 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(), 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>
|
|
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:
|
|
<informalexample><programlisting><![CDATA[
|
|
static void
|
|
on_proxy_appeared (GDBusConnection *connection,
|
|
const gchar *name,
|
|
const gchar *name_owner,
|
|
GDBusProxy *proxy,
|
|
gpointer user_data)
|
|
{
|
|
/* start to use proxy */
|
|
}
|
|
|
|
/* ... */
|
|
|
|
watcher_id = g_bus_watch_proxy (G_BUS_TYPE_SYSTEM,
|
|
"org.freedesktop.Accounts",
|
|
G_BUS_NAME_WATCHER_FLAGS_NONE,
|
|
"/org/freedesktop/Accounts",
|
|
"org.freedesktop.Accounts",
|
|
G_TYPE_DBUS_PROXY,
|
|
G_BUS_PROXY_FLAGS_NONE,
|
|
on_proxy_appeared,
|
|
on_proxy_vanished,
|
|
NULL,
|
|
NULL);
|
|
|
|
g_main_loop_run (loop);
|
|
|
|
g_bus_unwatch_proxy (watcher_id);
|
|
]]>
|
|
</programlisting></informalexample>
|
|
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.
|
|
</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>
|
|
</section>
|
|
|
|
</chapter>
|