mirror of
https://gitlab.freedesktop.org/dbus/dbus.git
synced 2026-01-04 01:30:22 +01:00
dbus-tutorial: replace the entire GLib section with "use GDBus"
Also provide links to relevant GLib and Qt documentation. Bug: https://bugs.freedesktop.org/show_bug.cgi?id=25140
This commit is contained in:
parent
2e4d069ac4
commit
acec88f32a
1 changed files with 15 additions and 941 deletions
|
|
@ -704,939 +704,19 @@
|
|||
</sect1>
|
||||
|
||||
<sect1 id="glib-client">
|
||||
<title>GLib API: Using Remote Objects</title>
|
||||
<title>GLib APIs</title>
|
||||
<para>
|
||||
The recommended GLib API for D-Bus is GDBus, which has been
|
||||
distributed with GLib since version 2.26. It is not documented here.
|
||||
See <ulink url="https://developer.gnome.org/gio/stable/gdbus-convenience.html">the
|
||||
GLib documentation</ulink> for details of how to use GDBus.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The GLib binding is defined in the header file
|
||||
<literal><dbus/dbus-glib.h></literal>.
|
||||
An older API, dbus-glib, also exists. It is deprecated and should
|
||||
not be used in new code. Whenever possible, porting existing code
|
||||
from dbus-glib to GDBus is also recommended.
|
||||
</para>
|
||||
|
||||
<sect2 id="glib-typemappings">
|
||||
<title>D-Bus - GLib type mappings</title>
|
||||
<para>
|
||||
The heart of the GLib bindings for D-Bus is the mapping it
|
||||
provides between D-Bus "type signatures" and GLib types
|
||||
(<literal>GType</literal>). The D-Bus type system is composed of
|
||||
a number of "basic" types, along with several "container" types.
|
||||
</para>
|
||||
<sect3 id="glib-basic-typemappings">
|
||||
<title>Basic type mappings</title>
|
||||
<para>
|
||||
Below is a list of the basic types, along with their associated
|
||||
mapping to a <literal>GType</literal>.
|
||||
<informaltable>
|
||||
<tgroup cols="4">
|
||||
<thead>
|
||||
<row>
|
||||
<entry>D-Bus basic type</entry>
|
||||
<entry>GType</entry>
|
||||
<entry>Free function</entry>
|
||||
<entry>Notes</entry>
|
||||
</row>
|
||||
</thead>
|
||||
<tbody>
|
||||
<row>
|
||||
<entry><literal>BYTE</literal></entry>
|
||||
<entry><literal>G_TYPE_UCHAR</literal></entry>
|
||||
<entry></entry>
|
||||
<entry></entry>
|
||||
</row><row>
|
||||
<entry><literal>BOOLEAN</literal></entry>
|
||||
<entry><literal>G_TYPE_BOOLEAN</literal></entry>
|
||||
<entry></entry>
|
||||
<entry></entry>
|
||||
</row><row>
|
||||
<entry><literal>INT16</literal></entry>
|
||||
<entry><literal>G_TYPE_INT</literal></entry>
|
||||
<entry></entry>
|
||||
<entry>Will be changed to a <literal>G_TYPE_INT16</literal> once GLib has it</entry>
|
||||
</row><row>
|
||||
<entry><literal>UINT16</literal></entry>
|
||||
<entry><literal>G_TYPE_UINT</literal></entry>
|
||||
<entry></entry>
|
||||
<entry>Will be changed to a <literal>G_TYPE_UINT16</literal> once GLib has it</entry>
|
||||
</row><row>
|
||||
<entry><literal>INT32</literal></entry>
|
||||
<entry><literal>G_TYPE_INT</literal></entry>
|
||||
<entry></entry>
|
||||
<entry>Will be changed to a <literal>G_TYPE_INT32</literal> once GLib has it</entry>
|
||||
</row><row>
|
||||
<entry><literal>UINT32</literal></entry>
|
||||
<entry><literal>G_TYPE_UINT</literal></entry>
|
||||
<entry></entry>
|
||||
<entry>Will be changed to a <literal>G_TYPE_UINT32</literal> once GLib has it</entry>
|
||||
</row><row>
|
||||
<entry><literal>INT64</literal></entry>
|
||||
<entry><literal>G_TYPE_GINT64</literal></entry>
|
||||
<entry></entry>
|
||||
<entry></entry>
|
||||
</row><row>
|
||||
<entry><literal>UINT64</literal></entry>
|
||||
<entry><literal>G_TYPE_GUINT64</literal></entry>
|
||||
<entry></entry>
|
||||
<entry></entry>
|
||||
</row><row>
|
||||
<entry><literal>DOUBLE</literal></entry>
|
||||
<entry><literal>G_TYPE_DOUBLE</literal></entry>
|
||||
<entry></entry>
|
||||
<entry></entry>
|
||||
</row><row>
|
||||
<entry><literal>STRING</literal></entry>
|
||||
<entry><literal>G_TYPE_STRING</literal></entry>
|
||||
<entry><literal>g_free</literal></entry>
|
||||
<entry></entry>
|
||||
</row><row>
|
||||
<entry><literal>OBJECT_PATH</literal></entry>
|
||||
<entry><literal>DBUS_TYPE_G_PROXY</literal></entry>
|
||||
<entry><literal>g_object_unref</literal></entry>
|
||||
<entry>The returned proxy does not have an interface set; use <literal>dbus_g_proxy_set_interface</literal> to invoke methods</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</informaltable>
|
||||
As you can see, the basic mapping is fairly straightforward.
|
||||
</para>
|
||||
</sect3>
|
||||
<sect3 id="glib-container-typemappings">
|
||||
<title>Container type mappings</title>
|
||||
<para>
|
||||
The D-Bus type system also has a number of "container"
|
||||
types, such as <literal>DBUS_TYPE_ARRAY</literal> and
|
||||
<literal>DBUS_TYPE_STRUCT</literal>. The D-Bus type system
|
||||
is fully recursive, so one can for example have an array of
|
||||
array of strings (i.e. type signature
|
||||
<literal>aas</literal>).
|
||||
</para>
|
||||
<para>
|
||||
However, not all of these types are in common use; for
|
||||
example, at the time of this writing the author knows of no
|
||||
one using <literal>DBUS_TYPE_STRUCT</literal>, or a
|
||||
<literal>DBUS_TYPE_ARRAY</literal> containing any non-basic
|
||||
type. The approach the GLib bindings take is pragmatic; try
|
||||
to map the most common types in the most obvious way, and
|
||||
let using less common and more complex types be less
|
||||
"natural".
|
||||
</para>
|
||||
<para>
|
||||
First, D-Bus type signatures which have an "obvious"
|
||||
corresponding built-in GLib type are mapped using that type:
|
||||
<informaltable>
|
||||
<tgroup cols="6">
|
||||
<thead>
|
||||
<row>
|
||||
<entry>D-Bus type signature</entry>
|
||||
<entry>Description</entry>
|
||||
<entry>GType</entry>
|
||||
<entry>C typedef</entry>
|
||||
<entry>Free function</entry>
|
||||
<entry>Notes</entry>
|
||||
</row>
|
||||
</thead>
|
||||
<tbody>
|
||||
<row>
|
||||
<entry><literal>as</literal></entry>
|
||||
<entry>Array of strings</entry>
|
||||
<entry><literal>G_TYPE_STRV</literal></entry>
|
||||
<entry><literal>char **</literal></entry>
|
||||
<entry><literal>g_strfreev</literal></entry>
|
||||
<entry></entry>
|
||||
</row><row>
|
||||
<entry><literal>v</literal></entry>
|
||||
<entry>Generic value container</entry>
|
||||
<entry><literal>G_TYPE_VALUE</literal></entry>
|
||||
<entry><literal>GValue *</literal></entry>
|
||||
<entry><literal>g_value_unset</literal></entry>
|
||||
<entry>The calling conventions for values expect that method callers have allocated return values; see below.</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</informaltable>
|
||||
</para>
|
||||
<para>
|
||||
The next most common recursive type signatures are arrays of
|
||||
basic values. The most obvious mapping for arrays of basic
|
||||
types is a <literal>GArray</literal>. Now, GLib does not
|
||||
provide a builtin <literal>GType</literal> for
|
||||
<literal>GArray</literal>. However, we actually need more than
|
||||
that - we need a "parameterized" type which includes the
|
||||
contained type. Why we need this we will see below.
|
||||
</para>
|
||||
<para>
|
||||
The approach taken is to create these types in the D-Bus GLib
|
||||
bindings; however, there is nothing D-Bus specific about them.
|
||||
In the future, we hope to include such "fundamental" types in GLib
|
||||
itself.
|
||||
<informaltable>
|
||||
<tgroup cols="6">
|
||||
<thead>
|
||||
<row>
|
||||
<entry>D-Bus type signature</entry>
|
||||
<entry>Description</entry>
|
||||
<entry>GType</entry>
|
||||
<entry>C typedef</entry>
|
||||
<entry>Free function</entry>
|
||||
<entry>Notes</entry>
|
||||
</row>
|
||||
</thead>
|
||||
<tbody>
|
||||
<row>
|
||||
<entry><literal>ay</literal></entry>
|
||||
<entry>Array of bytes</entry>
|
||||
<entry><literal>DBUS_TYPE_G_UCHAR_ARRAY</literal></entry>
|
||||
<entry><literal>GArray *</literal></entry>
|
||||
<entry>g_array_free</entry>
|
||||
<entry></entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><literal>au</literal></entry>
|
||||
<entry>Array of uint</entry>
|
||||
<entry><literal>DBUS_TYPE_G_UINT_ARRAY</literal></entry>
|
||||
<entry><literal>GArray *</literal></entry>
|
||||
<entry>g_array_free</entry>
|
||||
<entry></entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><literal>ai</literal></entry>
|
||||
<entry>Array of int</entry>
|
||||
<entry><literal>DBUS_TYPE_G_INT_ARRAY</literal></entry>
|
||||
<entry><literal>GArray *</literal></entry>
|
||||
<entry>g_array_free</entry>
|
||||
<entry></entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><literal>ax</literal></entry>
|
||||
<entry>Array of int64</entry>
|
||||
<entry><literal>DBUS_TYPE_G_INT64_ARRAY</literal></entry>
|
||||
<entry><literal>GArray *</literal></entry>
|
||||
<entry>g_array_free</entry>
|
||||
<entry></entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><literal>at</literal></entry>
|
||||
<entry>Array of uint64</entry>
|
||||
<entry><literal>DBUS_TYPE_G_UINT64_ARRAY</literal></entry>
|
||||
<entry><literal>GArray *</literal></entry>
|
||||
<entry>g_array_free</entry>
|
||||
<entry></entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><literal>ad</literal></entry>
|
||||
<entry>Array of double</entry>
|
||||
<entry><literal>DBUS_TYPE_G_DOUBLE_ARRAY</literal></entry>
|
||||
<entry><literal>GArray *</literal></entry>
|
||||
<entry>g_array_free</entry>
|
||||
<entry></entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><literal>ab</literal></entry>
|
||||
<entry>Array of boolean</entry>
|
||||
<entry><literal>DBUS_TYPE_G_BOOLEAN_ARRAY</literal></entry>
|
||||
<entry><literal>GArray *</literal></entry>
|
||||
<entry>g_array_free</entry>
|
||||
<entry></entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</informaltable>
|
||||
</para>
|
||||
<para>
|
||||
D-Bus also includes a special type DBUS_TYPE_DICT_ENTRY which
|
||||
is only valid in arrays. It's intended to be mapped to a "dictionary"
|
||||
type by bindings. The obvious GLib mapping here is GHashTable. Again,
|
||||
however, there is no builtin <literal>GType</literal> for a GHashTable.
|
||||
Moreover, just like for arrays, we need a parameterized type so that
|
||||
the bindings can communiate which types are contained in the hash table.
|
||||
</para>
|
||||
<para>
|
||||
At present, only strings are supported. Work is in progress to
|
||||
include more types.
|
||||
<informaltable>
|
||||
<tgroup cols="6">
|
||||
<thead>
|
||||
<row>
|
||||
<entry>D-Bus type signature</entry>
|
||||
<entry>Description</entry>
|
||||
<entry>GType</entry>
|
||||
<entry>C typedef</entry>
|
||||
<entry>Free function</entry>
|
||||
<entry>Notes</entry>
|
||||
</row>
|
||||
</thead>
|
||||
<tbody>
|
||||
<row>
|
||||
<entry><literal>a{ss}</literal></entry>
|
||||
<entry>Dictionary mapping strings to strings</entry>
|
||||
<entry><literal>DBUS_TYPE_G_STRING_STRING_HASHTABLE</literal></entry>
|
||||
<entry><literal>GHashTable *</literal></entry>
|
||||
<entry>g_hash_table_destroy</entry>
|
||||
<entry></entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</informaltable>
|
||||
</para>
|
||||
</sect3>
|
||||
<sect3 id="glib-generic-typemappings">
|
||||
<title>Arbitrarily recursive type mappings</title>
|
||||
<para>
|
||||
Finally, it is possible users will want to write or invoke D-Bus
|
||||
methods which have arbitrarily complex type signatures not
|
||||
directly supported by these bindings. For this case, we have a
|
||||
<literal>DBusGValue</literal> which acts as a kind of special
|
||||
variant value which may be iterated over manually. The
|
||||
<literal>GType</literal> associated is
|
||||
<literal>DBUS_TYPE_G_VALUE</literal>.
|
||||
</para>
|
||||
<para>
|
||||
TODO insert usage of <literal>DBUS_TYPE_G_VALUE</literal> here.
|
||||
</para>
|
||||
</sect3>
|
||||
</sect2>
|
||||
<sect2 id="sample-program-1">
|
||||
<title>A sample program</title>
|
||||
<para>Here is a D-Bus program using the GLib bindings.
|
||||
<programlisting>
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
DBusGConnection *connection;
|
||||
GError *error;
|
||||
DBusGProxy *proxy;
|
||||
char **name_list;
|
||||
char **name_list_ptr;
|
||||
|
||||
g_type_init ();
|
||||
|
||||
error = NULL;
|
||||
connection = dbus_g_bus_get (DBUS_BUS_SESSION,
|
||||
&error);
|
||||
if (connection == NULL)
|
||||
{
|
||||
g_printerr ("Failed to open connection to bus: %s\n",
|
||||
error->message);
|
||||
g_error_free (error);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
/* Create a proxy object for the "bus driver" (name "org.freedesktop.DBus") */
|
||||
|
||||
proxy = dbus_g_proxy_new_for_name (connection,
|
||||
DBUS_SERVICE_DBUS,
|
||||
DBUS_PATH_DBUS,
|
||||
DBUS_INTERFACE_DBUS);
|
||||
|
||||
/* Call ListNames method, wait for reply */
|
||||
error = NULL;
|
||||
if (!dbus_g_proxy_call (proxy, "ListNames", &error, G_TYPE_INVALID,
|
||||
G_TYPE_STRV, &name_list, G_TYPE_INVALID))
|
||||
{
|
||||
/* Just do demonstrate remote exceptions versus regular GError */
|
||||
if (error->domain == DBUS_GERROR && error->code == DBUS_GERROR_REMOTE_EXCEPTION)
|
||||
g_printerr ("Caught remote method exception %s: %s",
|
||||
dbus_g_error_get_name (error),
|
||||
error->message);
|
||||
else
|
||||
g_printerr ("Error: %s\n", error->message);
|
||||
g_error_free (error);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
/* Print the results */
|
||||
|
||||
g_print ("Names on the message bus:\n");
|
||||
|
||||
for (name_list_ptr = name_list; *name_list_ptr; name_list_ptr++)
|
||||
{
|
||||
g_print (" %s\n", *name_list_ptr);
|
||||
}
|
||||
g_strfreev (name_list);
|
||||
|
||||
g_object_unref (proxy);
|
||||
|
||||
return 0;
|
||||
}
|
||||
</programlisting>
|
||||
</para>
|
||||
</sect2>
|
||||
<sect2 id="glib-program-setup">
|
||||
<title>Program initalization</title>
|
||||
<para>
|
||||
A connection to the bus is acquired using
|
||||
<literal>dbus_g_bus_get</literal>. Next, a proxy
|
||||
is created for the object "/org/freedesktop/DBus" with
|
||||
interface <literal>org.freedesktop.DBus</literal>
|
||||
on the service <literal>org.freedesktop.DBus</literal>.
|
||||
This is a proxy for the message bus itself.
|
||||
</para>
|
||||
</sect2>
|
||||
<sect2 id="glib-method-invocation">
|
||||
<title>Understanding method invocation</title>
|
||||
<para>
|
||||
You have a number of choices for method invocation. First, as
|
||||
used above, <literal>dbus_g_proxy_call</literal> sends a
|
||||
method call to the remote object, and blocks until a reply is
|
||||
recieved. The outgoing arguments are specified in the varargs
|
||||
array, terminated with <literal>G_TYPE_INVALID</literal>.
|
||||
Next, pointers to return values are specified, followed again
|
||||
by <literal>G_TYPE_INVALID</literal>.
|
||||
</para>
|
||||
<para>
|
||||
To invoke a method asynchronously, use
|
||||
<literal>dbus_g_proxy_begin_call</literal>. This returns a
|
||||
<literal>DBusGPendingCall</literal> object; you may then set a
|
||||
notification function using
|
||||
<literal>dbus_g_pending_call_set_notify</literal>.
|
||||
</para>
|
||||
</sect2>
|
||||
<sect2 id="glib-signal-connection">
|
||||
<title>Connecting to object signals</title>
|
||||
<para>
|
||||
You may connect to signals using
|
||||
<literal>dbus_g_proxy_add_signal</literal> and
|
||||
<literal>dbus_g_proxy_connect_signal</literal>. You must
|
||||
invoke <literal>dbus_g_proxy_add_signal</literal> to specify
|
||||
the signature of your signal handlers; you may then invoke
|
||||
<literal>dbus_g_proxy_connect_signal</literal> multiple times.
|
||||
</para>
|
||||
<para>
|
||||
Note that it will often be the case that there is no builtin
|
||||
marshaller for the type signature of a remote signal. In that
|
||||
case, you must generate a marshaller yourself by using
|
||||
<application>glib-genmarshal</application>, and then register
|
||||
it using <literal>dbus_g_object_register_marshaller</literal>.
|
||||
</para>
|
||||
</sect2>
|
||||
<sect2 id="glib-error-handling">
|
||||
<title>Error handling and remote exceptions</title>
|
||||
<para>
|
||||
All of the GLib binding methods such as
|
||||
<literal>dbus_g_proxy_end_call</literal> return a
|
||||
<literal>GError</literal>. This <literal>GError</literal> can
|
||||
represent two different things:
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
An internal D-Bus error, such as an out-of-memory
|
||||
condition, an I/O error, or a network timeout. Errors
|
||||
generated by the D-Bus library itself have the domain
|
||||
<literal>DBUS_GERROR</literal>, and a corresponding code
|
||||
such as <literal>DBUS_GERROR_NO_MEMORY</literal>. It will
|
||||
not be typical for applications to handle these errors
|
||||
specifically.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
A remote D-Bus exception, thrown by the peer, bus, or
|
||||
service. D-Bus remote exceptions have both a textual
|
||||
"name" and a "message". The GLib bindings store this
|
||||
information in the <literal>GError</literal>, but some
|
||||
special rules apply.
|
||||
</para>
|
||||
<para>
|
||||
The set error will have the domain
|
||||
<literal>DBUS_GERROR</literal> as above, and will also
|
||||
have the code
|
||||
<literal>DBUS_GERROR_REMOTE_EXCEPTION</literal>. In order
|
||||
to access the remote exception name, you must use a
|
||||
special accessor, such as
|
||||
<literal>dbus_g_error_has_name</literal> or
|
||||
<literal>dbus_g_error_get_name</literal>. The remote
|
||||
exception detailed message is accessible via the regular
|
||||
GError <literal>message</literal> member.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
</sect2>
|
||||
<sect2 id="glib-more-examples">
|
||||
<title>More examples of method invocation</title>
|
||||
<sect3 id="glib-sending-stuff">
|
||||
<title>Sending an integer and string, receiving an array of bytes</title>
|
||||
<para>
|
||||
<programlisting>
|
||||
GArray *arr;
|
||||
|
||||
error = NULL;
|
||||
if (!dbus_g_proxy_call (proxy, "Foobar", &error,
|
||||
G_TYPE_INT, 42, G_TYPE_STRING, "hello",
|
||||
G_TYPE_INVALID,
|
||||
DBUS_TYPE_G_UCHAR_ARRAY, &arr, G_TYPE_INVALID))
|
||||
{
|
||||
/* Handle error */
|
||||
}
|
||||
g_assert (arr != NULL);
|
||||
printf ("got back %u values", arr->len);
|
||||
</programlisting>
|
||||
</para>
|
||||
</sect3>
|
||||
<sect3 id="glib-sending-hash">
|
||||
<title>Sending a GHashTable</title>
|
||||
<para>
|
||||
<programlisting>
|
||||
GHashTable *hash = g_hash_table_new (g_str_hash, g_str_equal);
|
||||
guint32 ret;
|
||||
|
||||
g_hash_table_insert (hash, "foo", "bar");
|
||||
g_hash_table_insert (hash, "baz", "whee");
|
||||
|
||||
error = NULL;
|
||||
if (!dbus_g_proxy_call (proxy, "HashSize", &error,
|
||||
DBUS_TYPE_G_STRING_STRING_HASH, hash, G_TYPE_INVALID,
|
||||
G_TYPE_UINT, &ret, G_TYPE_INVALID))
|
||||
{
|
||||
/* Handle error */
|
||||
}
|
||||
g_assert (ret == 2);
|
||||
g_hash_table_destroy (hash);
|
||||
</programlisting>
|
||||
</para>
|
||||
</sect3>
|
||||
<sect3 id="glib-receiving-bool-int">
|
||||
<title>Receiving a boolean and a string</title>
|
||||
<para>
|
||||
<programlisting>
|
||||
gboolean boolret;
|
||||
char *strret;
|
||||
|
||||
error = NULL;
|
||||
if (!dbus_g_proxy_call (proxy, "GetStuff", &error,
|
||||
G_TYPE_INVALID,
|
||||
G_TYPE_BOOLEAN, &boolret,
|
||||
G_TYPE_STRING, &strret,
|
||||
G_TYPE_INVALID))
|
||||
{
|
||||
/* Handle error */
|
||||
}
|
||||
printf ("%s %s", boolret ? "TRUE" : "FALSE", strret);
|
||||
g_free (strret);
|
||||
</programlisting>
|
||||
</para>
|
||||
</sect3>
|
||||
<sect3 id="glib-sending-str-arrays">
|
||||
<title>Sending two arrays of strings</title>
|
||||
<para>
|
||||
<programlisting>
|
||||
/* NULL terminate */
|
||||
char *strs_static[] = {"foo", "bar", "baz", NULL};
|
||||
/* Take pointer to array; cannot pass array directly */
|
||||
char **strs_static_p = strs_static;
|
||||
char **strs_dynamic;
|
||||
|
||||
strs_dynamic = g_new (char *, 4);
|
||||
strs_dynamic[0] = g_strdup ("hello");
|
||||
strs_dynamic[1] = g_strdup ("world");
|
||||
strs_dynamic[2] = g_strdup ("!");
|
||||
/* NULL terminate */
|
||||
strs_dynamic[3] = NULL;
|
||||
|
||||
error = NULL;
|
||||
if (!dbus_g_proxy_call (proxy, "TwoStrArrays", &error,
|
||||
G_TYPE_STRV, strs_static_p,
|
||||
G_TYPE_STRV, strs_dynamic,
|
||||
G_TYPE_INVALID,
|
||||
G_TYPE_INVALID))
|
||||
{
|
||||
/* Handle error */
|
||||
}
|
||||
g_strfreev (strs_dynamic);
|
||||
</programlisting>
|
||||
</para>
|
||||
</sect3>
|
||||
<sect3 id="glib-getting-str-array">
|
||||
<title>Sending a boolean, receiving an array of strings</title>
|
||||
<para>
|
||||
<programlisting>
|
||||
char **strs;
|
||||
char **strs_p;
|
||||
gboolean blah;
|
||||
|
||||
error = NULL;
|
||||
blah = TRUE;
|
||||
if (!dbus_g_proxy_call (proxy, "GetStrs", &error,
|
||||
G_TYPE_BOOLEAN, blah,
|
||||
G_TYPE_INVALID,
|
||||
G_TYPE_STRV, &strs,
|
||||
G_TYPE_INVALID))
|
||||
{
|
||||
/* Handle error */
|
||||
}
|
||||
for (strs_p = strs; *strs_p; strs_p++)
|
||||
printf ("got string: \"%s\"", *strs_p);
|
||||
g_strfreev (strs);
|
||||
</programlisting>
|
||||
</para>
|
||||
</sect3>
|
||||
<sect3 id="glib-sending-variant">
|
||||
<title>Sending a variant</title>
|
||||
<para>
|
||||
<programlisting>
|
||||
GValue val = {0, };
|
||||
|
||||
g_value_init (&val, G_TYPE_STRING);
|
||||
g_value_set_string (&val, "hello world");
|
||||
|
||||
error = NULL;
|
||||
if (!dbus_g_proxy_call (proxy, "SendVariant", &error,
|
||||
G_TYPE_VALUE, &val, G_TYPE_INVALID,
|
||||
G_TYPE_INVALID))
|
||||
{
|
||||
/* Handle error */
|
||||
}
|
||||
g_assert (ret == 2);
|
||||
g_value_unset (&val);
|
||||
</programlisting>
|
||||
</para>
|
||||
</sect3>
|
||||
<sect3 id="glib-receiving-variant">
|
||||
<title>Receiving a variant</title>
|
||||
<para>
|
||||
<programlisting>
|
||||
GValue val = {0, };
|
||||
|
||||
error = NULL;
|
||||
if (!dbus_g_proxy_call (proxy, "GetVariant", &error, G_TYPE_INVALID,
|
||||
G_TYPE_VALUE, &val, G_TYPE_INVALID))
|
||||
{
|
||||
/* Handle error */
|
||||
}
|
||||
if (G_VALUE_TYPE (&val) == G_TYPE_STRING)
|
||||
printf ("%s\n", g_value_get_string (&val));
|
||||
else if (G_VALUE_TYPE (&val) == G_TYPE_INT)
|
||||
printf ("%d\n", g_value_get_int (&val));
|
||||
else
|
||||
...
|
||||
g_value_unset (&val);
|
||||
</programlisting>
|
||||
</para>
|
||||
</sect3>
|
||||
</sect2>
|
||||
|
||||
<sect2 id="glib-generated-bindings">
|
||||
<title>Generated Bindings</title>
|
||||
<para>
|
||||
By using the Introspection XML files, convenient client-side bindings
|
||||
can be automatically created to ease the use of a remote DBus object.
|
||||
</para>
|
||||
<para>
|
||||
Here is a sample XML file which describes an object that exposes
|
||||
one method, named <literal>ManyArgs</literal>.
|
||||
<programlisting>
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<node name="/com/example/MyObject">
|
||||
<interface name="com.example.MyObject">
|
||||
<method name="ManyArgs">
|
||||
<arg type="u" name="x" direction="in" />
|
||||
<arg type="s" name="str" direction="in" />
|
||||
<arg type="d" name="trouble" direction="in" />
|
||||
<arg type="d" name="d_ret" direction="out" />
|
||||
<arg type="s" name="str_ret" direction="out" />
|
||||
</method>
|
||||
</interface>
|
||||
</node>
|
||||
</programlisting>
|
||||
</para>
|
||||
<para>
|
||||
Run <literal>dbus-binding-tool --mode=glib-client
|
||||
<replaceable>FILENAME</replaceable> >
|
||||
<replaceable>HEADER_NAME</replaceable></literal> to generate the header
|
||||
file. For example: <command>dbus-binding-tool --mode=glib-client
|
||||
my-object.xml > my-object-bindings.h</command>. This will generate
|
||||
inline functions with the following prototypes:
|
||||
<programlisting>
|
||||
/* This is a blocking call */
|
||||
gboolean
|
||||
com_example_MyObject_many_args (DBusGProxy *proxy, const guint IN_x,
|
||||
const char * IN_str, const gdouble IN_trouble,
|
||||
gdouble* OUT_d_ret, char ** OUT_str_ret,
|
||||
GError **error);
|
||||
|
||||
/* This is a non-blocking call */
|
||||
DBusGProxyCall*
|
||||
com_example_MyObject_many_args_async (DBusGProxy *proxy, const guint IN_x,
|
||||
const char * IN_str, const gdouble IN_trouble,
|
||||
com_example_MyObject_many_args_reply callback,
|
||||
gpointer userdata);
|
||||
|
||||
/* This is the typedef for the non-blocking callback */
|
||||
typedef void
|
||||
(*com_example_MyObject_many_args_reply)
|
||||
(DBusGProxy *proxy, gdouble OUT_d_ret, char * OUT_str_ret,
|
||||
GError *error, gpointer userdata);
|
||||
</programlisting>
|
||||
The first argument in all functions is a <literal>DBusGProxy
|
||||
*</literal>, which you should create with the usual
|
||||
<literal>dbus_g_proxy_new_*</literal> functions. Following that are the
|
||||
"in" arguments, and then either the "out" arguments and a
|
||||
<literal>GError *</literal> for the synchronous (blocking) function, or
|
||||
callback and user data arguments for the asynchronous (non-blocking)
|
||||
function. The callback in the asynchronous function passes the
|
||||
<literal>DBusGProxy *</literal>, the returned "out" arguments, an
|
||||
<literal>GError *</literal> which is set if there was an error otherwise
|
||||
<literal>NULL</literal>, and the user data.
|
||||
</para>
|
||||
<para>
|
||||
As with the server-side bindings support (see <xref
|
||||
linkend="glib-server"/>), the exact behaviour of the client-side
|
||||
bindings can be manipulated using "annotations". Currently the only
|
||||
annotation used by the client bindings is
|
||||
<literal>org.freedesktop.DBus.GLib.NoReply</literal>, which sets the
|
||||
flag indicating that the client isn't expecting a reply to the method
|
||||
call, so a reply shouldn't be sent. This is often used to speed up
|
||||
rapid method calls where there are no "out" arguments, and not knowing
|
||||
if the method succeeded is an acceptable compromise to half the traffic
|
||||
on the bus.
|
||||
</para>
|
||||
</sect2>
|
||||
</sect1>
|
||||
|
||||
<sect1 id="glib-server">
|
||||
<title>GLib API: Implementing Objects</title>
|
||||
<para>
|
||||
At the moment, to expose a GObject via D-Bus, you must
|
||||
write XML by hand which describes the methods exported
|
||||
by the object. In the future, this manual step will
|
||||
be obviated by the upcoming GLib introspection support.
|
||||
</para>
|
||||
<para>
|
||||
Here is a sample XML file which describes an object that exposes
|
||||
one method, named <literal>ManyArgs</literal>.
|
||||
<programlisting>
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<node name="/com/example/MyObject">
|
||||
|
||||
<interface name="com.example.MyObject">
|
||||
<annotation name="org.freedesktop.DBus.GLib.CSymbol" value="my_object"/>
|
||||
<method name="ManyArgs">
|
||||
<!-- This is optional, and in this case is redunundant -->
|
||||
<annotation name="org.freedesktop.DBus.GLib.CSymbol" value="my_object_many_args"/>
|
||||
<arg type="u" name="x" direction="in" />
|
||||
<arg type="s" name="str" direction="in" />
|
||||
<arg type="d" name="trouble" direction="in" />
|
||||
<arg type="d" name="d_ret" direction="out" />
|
||||
<arg type="s" name="str_ret" direction="out" />
|
||||
</method>
|
||||
</interface>
|
||||
</node>
|
||||
</programlisting>
|
||||
</para>
|
||||
<para>
|
||||
This XML is in the same format as the D-Bus introspection XML
|
||||
format. Except we must include an "annotation" which give the C
|
||||
symbols corresponding to the object implementation prefix
|
||||
(<literal>my_object</literal>). In addition, if particular
|
||||
methods symbol names deviate from C convention
|
||||
(i.e. <literal>ManyArgs</literal> ->
|
||||
<literal>many_args</literal>), you may specify an annotation
|
||||
giving the C symbol.
|
||||
</para>
|
||||
<para>
|
||||
Once you have written this XML, run <literal>dbus-binding-tool --mode=glib-server <replaceable>FILENAME</replaceable> > <replaceable>HEADER_NAME</replaceable>.</literal> to
|
||||
generate a header file. For example: <command>dbus-binding-tool --mode=glib-server my-object.xml > my-object-glue.h</command>.
|
||||
</para>
|
||||
<para>
|
||||
Next, include the generated header in your program, and invoke
|
||||
<literal>dbus_g_object_class_install_info</literal> in the class
|
||||
initializer, passing the object class and "object info" included in the
|
||||
header. For example:
|
||||
<programlisting>
|
||||
dbus_g_object_type_install_info (COM_FOO_TYPE_MY_OBJECT, &com_foo_my_object_info);
|
||||
</programlisting>
|
||||
This should be done exactly once per object class.
|
||||
</para>
|
||||
<para>
|
||||
To actually implement the method, just define a C function named e.g.
|
||||
<literal>my_object_many_args</literal> in the same file as the info
|
||||
header is included. At the moment, it is required that this function
|
||||
conform to the following rules:
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
The function must return a value of type <literal>gboolean</literal>;
|
||||
<literal>TRUE</literal> on success, and <literal>FALSE</literal>
|
||||
otherwise.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
The first parameter is a pointer to an instance of the object.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
Following the object instance pointer are the method
|
||||
input values.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
Following the input values are pointers to return values.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
The final parameter must be a <literal>GError **</literal>.
|
||||
If the function returns <literal>FALSE</literal> for an
|
||||
error, the error parameter must be initalized with
|
||||
<literal>g_set_error</literal>.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
<para>
|
||||
Finally, you can export an object using <literal>dbus_g_connection_register_g_object</literal>. For example:
|
||||
<programlisting>
|
||||
dbus_g_connection_register_g_object (connection,
|
||||
"/com/foo/MyObject",
|
||||
obj);
|
||||
</programlisting>
|
||||
</para>
|
||||
|
||||
<sect2 id="glib-annotations">
|
||||
<title>Server-side Annotations</title>
|
||||
<para>
|
||||
There are several annotations that are used when generating the
|
||||
server-side bindings. The most common annotation is
|
||||
<literal>org.freedesktop.DBus.GLib.CSymbol</literal> but there are other
|
||||
annotations which are often useful.
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term><literal>org.freedesktop.DBus.GLib.CSymbol</literal></term>
|
||||
<listitem>
|
||||
<para>
|
||||
This annotation is used to specify the C symbol names for
|
||||
the various types (interface, method, etc), if it differs from the
|
||||
name DBus generates.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><literal>org.freedesktop.DBus.GLib.Async</literal></term>
|
||||
<listitem>
|
||||
<para>
|
||||
This annotation marks the method implementation as an
|
||||
asynchronous function, which doesn't return a response straight
|
||||
away but will send the response at some later point to complete
|
||||
the call. This is used to implement non-blocking services where
|
||||
method calls can take time.
|
||||
</para>
|
||||
<para>
|
||||
When a method is asynchronous, the function prototype is
|
||||
different. It is required that the function conform to the
|
||||
following rules:
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
The function must return a value of type <literal>gboolean</literal>;
|
||||
<literal>TRUE</literal> on success, and <literal>FALSE</literal>
|
||||
otherwise. TODO: the return value is currently ignored.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
The first parameter is a pointer to an instance of the object.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
Following the object instance pointer are the method
|
||||
input values.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
The final parameter must be a
|
||||
<literal>DBusGMethodInvocation *</literal>. This is used
|
||||
when sending the response message back to the client, by
|
||||
calling <literal>dbus_g_method_return</literal> or
|
||||
<literal>dbus_g_method_return_error</literal>.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><literal>org.freedesktop.DBus.GLib.Const</literal></term>
|
||||
<listitem>
|
||||
<para>This attribute can only be applied to "out"
|
||||
<literal><arg></literal> nodes, and specifies that the
|
||||
parameter isn't being copied when returned. For example, this
|
||||
turns a 's' argument from a <literal>char **</literal> to a
|
||||
<literal>const char **</literal>, and results in the argument not
|
||||
being freed by DBus after the message is sent.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><literal>org.freedesktop.DBus.GLib.ReturnVal</literal></term>
|
||||
<listitem>
|
||||
<para>
|
||||
This attribute can only be applied to "out"
|
||||
<literal><arg></literal> nodes, and alters the expected
|
||||
function signature. It currently can be set to two values:
|
||||
<literal>""</literal> or <literal>"error"</literal>. The
|
||||
argument marked with this attribute is not returned via a
|
||||
pointer argument, but by the function's return value. If the
|
||||
attribute's value is the empty string, the <literal>GError
|
||||
*</literal> argument is also omitted so there is no standard way
|
||||
to return an error value. This is very useful for interfacing
|
||||
with existing code, as it is possible to match existing APIs.
|
||||
If the attribute's value is <literal>"error"</literal>, then the
|
||||
final argument is a <literal>GError *</literal> as usual.
|
||||
</para>
|
||||
<para>
|
||||
Some examples to demonstrate the usage. This introspection XML:
|
||||
<programlisting>
|
||||
<method name="Increment">
|
||||
<arg type="u" name="x" />
|
||||
<arg type="u" direction="out" />
|
||||
</method>
|
||||
</programlisting>
|
||||
Expects the following function declaration:
|
||||
<programlisting>
|
||||
gboolean
|
||||
my_object_increment (MyObject *obj, gint32 x, gint32 *ret, GError **error);
|
||||
</programlisting>
|
||||
</para>
|
||||
<para>
|
||||
This introspection XML:
|
||||
<programlisting>
|
||||
<method name="IncrementRetval">
|
||||
<arg type="u" name="x" />
|
||||
<arg type="u" direction="out" >
|
||||
<annotation name="org.freedesktop.DBus.GLib.ReturnVal" value=""/>
|
||||
</arg>
|
||||
</method>
|
||||
</programlisting>
|
||||
Expects the following function declaration:
|
||||
<programlisting>
|
||||
gint32
|
||||
my_object_increment_retval (MyObject *obj, gint32 x)
|
||||
</programlisting>
|
||||
</para>
|
||||
<para>
|
||||
This introspection XML:
|
||||
<programlisting>
|
||||
<method name="IncrementRetvalError">
|
||||
<arg type="u" name="x" />
|
||||
<arg type="u" direction="out" >
|
||||
<annotation name="org.freedesktop.DBus.GLib.ReturnVal" value="error"/>
|
||||
</arg>
|
||||
</method>
|
||||
</programlisting>
|
||||
Expects the following function declaration:
|
||||
<programlisting>
|
||||
gint32
|
||||
my_object_increment_retval_error (MyObject *obj, gint32 x, GError **error)
|
||||
</programlisting>
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</para>
|
||||
</sect2>
|
||||
</sect1>
|
||||
|
||||
<sect1 id="python-client">
|
||||
|
|
@ -1650,18 +730,12 @@ my_object_increment_retval_error (MyObject *obj, gint32 x, GError **error)
|
|||
</sect1>
|
||||
|
||||
<sect1 id="qt-client">
|
||||
<title>Qt API: Using Remote Objects</title>
|
||||
<title>Qt API</title>
|
||||
<para>
|
||||
|
||||
The Qt bindings are not yet documented.
|
||||
|
||||
</para>
|
||||
</sect1>
|
||||
|
||||
<sect1 id="qt-server">
|
||||
<title>Qt API: Implementing Objects</title>
|
||||
<para>
|
||||
The Qt bindings are not yet documented.
|
||||
The Qt binding for libdbus, QtDBus, has been distributed with Qt
|
||||
since version 4.2. It is not documented here. See
|
||||
<ulink url="http://qt-project.org/doc/qt-5/qtdbus-index.html">the Qt
|
||||
documentation</ulink> for details of how to use QtDBus.
|
||||
</para>
|
||||
</sect1>
|
||||
</article>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue