diff options
author | Colin Walters <walters@verbum.org> | 2005-06-13 03:01:30 +0000 |
---|---|---|
committer | Colin Walters <walters@verbum.org> | 2005-06-13 03:01:30 +0000 |
commit | beb9cd2eb219e04f9872c6a4dd743d5d1c36b4b1 (patch) | |
tree | 392eb655fe68d80363169bf170c9a123430a4058 /doc | |
parent | 982de71850996f01c244429809ba23f715353ea3 (diff) |
2005-06-12 Colin Walters <walters@verbum.org>
Async signals and various bugfixes and testing by
Ross Burton <ross@burtonini.com>.
* glib/dbus-gvalue.h: (struct DBusBasicGValue): Delete.
(dbus_gvalue_genmarshal_name_from_type)
(dbus_gvalue_ctype_from_type): Moved to dbus-binding-tool-glib.c.
(dbus_gtype_to_dbus_type): Renamed to dbus_gtype_from_signature.
(dbus_g_value_types_init, dbus_gtype_from_signature)
(dbus_gtype_from_signature_iter, dbus_gtype_to_signature)
(dbus_gtypes_from_arg_signature): New function prototypes.
(dbus_gvalue_demarshal): Take context and error arguments.
(dbus_gvalue_demarshal_variant): New function.
(dbus_gvalue_demarshal_message): New function.
(dbus_gvalue_store): Delete.
* glib/dbus-gvalue.c:
File has been almost entirely rewritten; now we special-case
more types such as DBUS_TYPE_SIGNATURE, handle arrays and
hash tables correctly, etc. Full support for recursive values
is not yet complete.
* glib/dbus-gproxy.c (dbus_g_proxy_class_init): Change last
argument of signal to G_TYPE_POINTER since we now pass a
structure.
(lookup_g_marshaller): Delete in favor of
_dbus_gobject_lookup_marshaller.
(marshal_dbus_message_to_g_marshaller): Use
_dbus_gobject_lookup_marshaller and dbus_gvalue_demarshal_message
to handle remote signal callbacks.
(dbus_g_proxy_new_from_proxy): New function; creates a new
DBusGProxy by copying an existing one.
(dbus_g_proxy_get_interface, dbus_g_proxy_set_interface)
(dbus_g_proxy_get_path): New functions.
(dbus_g_proxy_marshal_args_to_message): New function;
factored out of existing code.
(DBUS_G_VALUE_ARRAY_COLLECT_ALL): Collect all arguments
from a varargs array.
(dbus_g_proxy_begin_call_internal): New function.
(dbus_g_proxy_end_call_internal): New function.
(dbus_g_proxy_begin_call): Take GTypes instead of DBus types
as arguments; simply invoke dbus_g_proxy_begin_call_internal
after collecting args into value array.
(dbus_g_proxy_end_call): Take GTypes instead of DBus types;
invoke dbus_g_proxy_end_call_internal.
(dbus_g_proxy_invoke): Simply invoke begin_call_interanl and
end_call_internal.
(dbus_g_proxy_call_no_reply): Take GTypes instead of DBus
types.
(array_free_all): New function.
(dbus_g_proxy_add_signal): Take GTypes.
* glib/dbus-gobject.h:
(_dbus_glib_marshal_dbus_message_to_gvalue_array): Delete.
(_dbus_gobject_get_path, _dbus_gobject_lookup_marshaller):
Prototype.
* glib/dbus-gobject.c: Add a global marshal_table hash which
stores mappings from type signatures to marshallers. Change lots
of invocations of dbus_gtype_to_dbus_type to
dbus_gtype_to_signature.
(_dbus_glib_marshal_dbus_message_to_gvalue_array): Delete.
(introspect_signals): Fix test for query.return_type.
(set_object_property): Update invocation of dbus_gvalue_demarshal.
(invoke_object_method): Many changes. Handle asynchronous
invocations. Convert arguments with
dbus_gvalue_demarshal_message. Handle errors. Use
DBusSignatureIter instead of strlen on args. Handle all arguments
generically. Special-case variants.
(dbus_g_method_return, dbus_g_method_return_error): New function.
(DBusGSignalClosure): New structure, closes over signal
information.
(dbus_g_signal_closure_new): New function.
(dbus_g_signal_closure_finalize): New function.
(signal_emitter_marshaller): New function; is special marshaller
which emits signals on bus.
(export_signals): New function; introspects object signals and
connects to them.
(dbus_g_object_type_install_info): Take GType instead of
GObjectClass.
(dbus_g_connection_register_g_object): Invoke export_signals.
(dbus_g_connection_lookup_g_object): New function.
(DBusGFuncSignature) New structure; used for mapping type
signatures to marshallers.
(funcsig_hash): New function; hashes DBusGFuncSignature.
(funcsig_equal): New function; compares DBusGFuncSignature.
(_dbus_gobject_lookup_marshaller): New function.
(dbus_g_object_register_marshaller): New function; used to
register a marshaller at runtime for a particular signature.
* glib/dbus-gmain.c (_dbus_gmain_test): Add various tests.
* glib/dbus-binding-tool-glib.h: Add DBUS_GLIB_ANNOTATION_ASYNC
which notes a server method implementation should be
asynchronous.
* glib/dbus-binding-tool-glib.c
(dbus_binding_tool_output_glib_server): Call
dbus_g_value_types_init.
(write_formal_parameters): Use dbus_gtype_from_signature. Handle
variants specially.
(dbus_g_type_get_lookup_function): Turn GType into an invocation
of a lookup function.
(write_args_for_direction): Use dbus_g_type_get_lookup_function.
(write_untyped_out_args): New method; write output arguments.
(write_formal_declarations_for_direction): Function for
writing prototypes.
(write_formal_parameters_for_direction): Function for
writing implementations.
(write_typed_args_for_direction): Function for writing
arguments prefixed with GTypes.
(write_async_method_client): Write out async version
of method.
* glib/dbus-binding-tool-glib.c: Include dbus-gvalue-utils.h.
(dbus_g_type_get_marshal_name): Move mapping from GType
to marshal name into here.
(dbus_g_type_get_c_name): Move into here.
(compute_marshaller): Convert signature to type with
dbus_gtype_from_signature, use dbus_g_type_get_marshal_name.
(compute_marshaller_name): Ditto.
(compute_marshaller): Handle async signal annotations.
(gather_marshallers): Return if we don't have a known
prefix.
(generate_glue): Collect introspection blob here, and
write all of the blob at the end. This allows an object
with multiple interfaces to work.
Mark async methods in introspection blob.
* glib/Makefile.am (libdbus_glib_1_la_SOURCES): Add
dbus-gtype-specialized.c, dbus-gtype-specialized.h,
dbus-gvalue-utils.h, dbus-gvalue-utils.c.
* dbus/dbus-glib.h: Don't include dbus-protocol.h; this
avoids people accidentally using DBUS_TYPE_* which should
not be necessary anymore.
Do include dbus-gtype-specialized.h, which are utilities
for GLib container types.
Add various #defines for types such as
DBUS_TYPE_G_BOOLEAN_ARRAY.
(DBusGValueIterator, DBusGValue): Define, not fully used
yet.
(dbus_g_value_get_g_type): Type for recursive value.
(dbus_g_value_open, dbus_g_value_iterator_get_value)
(dbus_g_value_iterator_get_values, dbus_g_value_iterator_recurse)
(dbus_g_value_free): Prototypes.
(dbus_g_object_register_marshaller, dbus_g_proxy_new_from_proxy): Prototype.
(dbus_g_proxy_set_interface): Prototype.
(dbus_g_proxy_begin_call, dbus_g_proxy_end_call)
(dbus_g_proxy_call_no_reply): Take GLib types instead of DBus
types.
(dbus_g_proxy_get_path, dbus_g_proxy_get_interface):
Accessors.
(DBusGAsyncData, DBusGMethodInvocation): Structures for
doing async invocations.
(dbus_g_method_return, dbus_g_method_return_error):
Prototypes.
* doc/dbus-tutorial.xml: Update GLib section.
* tools/dbus-viewer.c (load_child_nodes): Update
for new invocation type of dbus_g_proxy_end_call.
(load_from_service_thread_func): Ditto.
* tools/print-introspect.c (main): Ditto.
* tools/dbus-names-model.c (have_names_notify)
(names_model_reload, names_model_set_connection)
Use GTypes.
* python/Makefile.am (INCLUDES): Define DBUS_COMPILATION,
needed since Python bindings use GLib bindings.
* test/glib/Makefile.am (INCLUDES): Define DBUS_COMPILATION.
Add --prefix argument.
* tools/Makefile.am: Define DBUS_COMPILATION. Remove
unneeded --ignore-unsupported arg.
* test/glib/test-service-glib.c:
* test/glib/test-service-glib.xml:
* test/glib/test-dbus-glib.c: Add many more tests.
Diffstat (limited to 'doc')
-rw-r--r-- | doc/TODO | 9 | ||||
-rw-r--r-- | doc/dbus-tutorial.xml | 680 |
2 files changed, 613 insertions, 76 deletions
@@ -39,12 +39,11 @@ Important for 1.0 Important for 1.0 GLib Bindings === - - finish dbus-glib-tool support for adding introspection - data to GObject and autoexporting GObject using same + - Annotations for "do not take ownership of this return value" on server - - Need to make sure that dbus-glib.h never returns any - dbus_malloc() memory, only g_malloc(). - dbus_g_proxy_end_call() is the major offender. + - Fix signals + + - Fix errors - need to get specific error back, not UnmappedError crap - DBusGProxy doesn't emit "destroy" when it should diff --git a/doc/dbus-tutorial.xml b/doc/dbus-tutorial.xml index 22906ac9..6ee32927 100644 --- a/doc/dbus-tutorial.xml +++ b/doc/dbus-tutorial.xml @@ -449,20 +449,294 @@ <title>GLib API: Using Remote Objects</title> <para> - The GLib binding is defined in the header file - <dbus/dbus-glib.h>. The API is very small, in sharp contrast to the - low-level <dbus/dbus.h>. - + <dbus/dbus-glib.h>. </para> - <para> - The GLib bindings are incomplete, see the TODO file and comments in the - source code. - </para> - - <para> -Here is a D-BUS program using the GLib bindings. + <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 G_TYPE_INT16 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 G_TYPE_UINT16 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 G_TYPE_INT32 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 G_TYPE_UINT32 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>g_free</entry> + <entry></entry> + </row><row> + <entry><literal>OBJECT_PATH</literal></entry> + <entry><literal>DBUS_TYPE_G_PROXY</literal></entry> + <entry>g_object_unref</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 builtin 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>g_strfreev</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>g_value_unset</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_BYTE_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) @@ -470,10 +744,8 @@ main (int argc, char **argv) DBusGConnection *connection; GError *error; DBusGProxy *proxy; - DBusGPendingCall *call; char **name_list; - int name_list_len; - int i; + char **name_list_ptr; g_type_init (); @@ -495,15 +767,10 @@ main (int argc, char **argv) DBUS_PATH_ORG_FREEDESKTOP_DBUS, DBUS_INTERFACE_ORG_FREEDESKTOP_DBUS); - /* Call ListNames method */ - - call = dbus_g_proxy_begin_call (proxy, "ListNames", DBUS_TYPE_INVALID); - + /* Call ListNames method, wait for reply */ error = NULL; - if (!dbus_g_proxy_end_call (proxy, call, &error, - DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, - &name_list, &name_list_len, - DBUS_TYPE_INVALID)) + if (!dbus_g_proxy_invoke (proxy, "ListNames", &error, G_TYPE_INVALID, + G_TYPE_STRV, &name_list, G_TYPE_INVALID)) { g_printerr ("Failed to complete ListNames call: %s\n", error->message); @@ -514,77 +781,348 @@ main (int argc, char **argv) /* Print the results */ g_print ("Names on the message bus:\n"); - i = 0; - while (i < name_list_len) + + for (name_list_ptr = name_list; *name_list_ptr; name_list_ptr++) { - g_assert (name_list[i] != NULL); - g_print (" %s\n", name_list[i]); - ++i; + g_print (" %s\n", *name_list_ptr); } - g_assert (name_list[i] == NULL); - 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_invoke</literal> sends a + method call to the remote object, and blocks until 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>. At the + moment, <literal>dbus_g_proxy_add_signal</literal> requires + the D-BUS types of the remote object; this will likely be + changed later. + </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_invoke (proxy, "Foobar", &error, + G_TYPE_INT, 42, G_TYPE_STRING, "hello", + G_TYPE_INVALID, + DBUS_TYPE_G_UCHAR_ARRAY, &arr, G_TYPE_INVALID)) + { + g_printerr ("Failed to complete Foobar: %s\n", + error->message); + g_error_free (error); + exit (1); + } + 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"); - <para> - - DBusGProxy represents a remote object. dbus_g_proxy_begin_call() sends - a method call to the remote object, and dbus_g_proxy_end_call() retrieves - any return values or exceptions resulting from the method call. - There are also DBusGProxy functions to connect and disconnect signals, - not shown in the code example. - - </para> - - <para> - - dbus_g_bus_get() assumes that the application will use GMainLoop. The - created connection will be associated with the main loop such that - messages will be sent and received when the main loop runs. However, in - the above code example the main loop never runs; D-BUS will not run the - loop implicitly. Instead, dbus_g_proxy_end_call() will block until the - method call has been sent and the reply received. A more complex GUI - application might run the main loop while waiting for the method call - reply. (DBusGPendingCall is currently missing the "notify me when the - call is complete" functionality found in DBusPendingCall, but it should be - added.) + error = NULL; + if (!dbus_g_proxy_invoke (proxy, "HashSize", &error, + DBUS_TYPE_G_STRING_STRING_HASH, hash, G_TYPE_INVALID, + G_TYPE_UINT, &ret, G_TYPE_INVALID)) + { + g_printerr ("Failed to complete HashSize: %s\n", + error->message); + g_error_free (error); + exit (1); + } + 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_invoke (proxy, "GetStuff", &error, + G_TYPE_INVALID, + G_TYPE_BOOLEAN, &boolret, + G_TYPE_STRING, &strret, + G_TYPE_INVALID)) + { + g_printerr ("Failed to complete GetStuff: %s\n", + error->message); + g_error_free (error); + exit (1); + } + 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; - </para> + 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_invoke (proxy, "TwoStrArrays", &error, + G_TYPE_STRV, strs_static_p, + G_TYPE_STRV, strs_dynamic, + G_TYPE_INVALID, + G_TYPE_INVALID)) + { + g_printerr ("Failed to complete TwoStrArrays: %s\n", + error->message); + g_error_free (error); + exit (1); + } + 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; - <para> - - Future plans (see doc/TODO) are to use G_TYPE_STRING in place of - DBUS_TYPE_STRING and so forth. In fact the above code is slightly - incorrect at the moment, since it uses g_strfreev() to free a string array - that was not allocated with g_malloc(). dbus_free_string_array() should - really be used. However, once the GLib bindings are complete the returned - data from dbus_g_proxy_end_call() will be allocated with g_malloc(). + error = NULL; + blah = TRUE; + if (!dbus_g_proxy_invoke (proxy, "GetStrs", &error, + G_TYPE_BOOLEAN, blah, + G_TYPE_INVALID, + G_TYPE_STRV, &strs, + G_TYPE_INVALID)) + { + g_printerr ("Failed to complete GetStrs: %s\n", + error->message); + g_error_free (error); + exit (1); + } + 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, }; - </para> + g_value_init (&val, G_TYPE_STRING); + g_value_set_string (&val, "hello world"); + + error = NULL; + if (!dbus_g_proxy_invoke (proxy, "SendVariant", &error, + G_TYPE_VALUE, &val, G_TYPE_INVALID, + G_TYPE_INVALID)) + { + g_printerr ("Failed to complete SendVariant: %s\n", + error->message); + g_error_free (error); + exit (1); + } + 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_invoke (proxy, "GetVariant", &error, G_TYPE_INVALID, + G_TYPE_VALUE, &val, G_TYPE_INVALID)) + { + g_printerr ("Failed to complete GetVariant: %s\n", + error->message); + g_error_free (error); + exit (1); + } + 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> </sect1> <sect1 id="glib-server"> <title>GLib API: Implementing Objects</title> - <para> - - The GLib binding is defined in the header file - <dbus/dbus-glib.h>. To implement an object, it's also necessary - to use the dbus-glib-tool command line tool. - + 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> - The GLib bindings are incomplete. Implementing an object is not yet - possible, see the TODO file and comments in the source code for details - on what work needs doing. + 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-objet.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>, 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> - </sect1> <sect1 id="qt-client"> |