Editing Documentation/Maemo 5 Developer Guide/DBus/Using GLib Wrappers For D-Bus
Warning: You are not logged in.
Your IP address will be recorded in this page's edit history.
Warning: This page is 121 kilobytes long; some browsers may have problems editing pages approaching or longer than 32kb. Please consider breaking the page into smaller sections.
The edit can be undone.
Please check the comparison below to verify that this is what you want to do, and then save the changes below to finish undoing the edit.
Latest revision | Your text | ||
Line 1: | Line 1: | ||
+ | =Using GLib wrappers for D-Bus = | ||
+ | |||
==Introduction to GObjects == | ==Introduction to GObjects == | ||
+ | In order to support the runtime binding of GTK+ widgets to interpreted languages, a complicated system for implementing object-oriented machinery for C was developed. Depending on the particular viewpoint, this system is either called GObject or GType. GType is the low-level runtime type system, which is used to implement GObjects, and GObjects are the implementations of objects using the GType framework. For a short introduction about GObjects, please see the [http://en.wikipedia.org/wiki/GType Wikipedia entry on GType]. GObject/GType is part of GLib, but most of GLib is useable without the GType part of the library. In fact, the GObject/GType functionality is separated into its own library (libgobject). | ||
- | + | The following example uses the GType in order to implement a very simple object that is published over the D-Bus. This also means that some of the inherent complexity involved in implementing a fully fledged GObject can be forgone. For this reason, while the object is usable over the D-Bus, it may not have all the support required for using it directly as a GObject (full dynamic type registration and properties are missing). | |
- | + | The implementation is a simple non-inheriting stand-alone class, implementing an interface to access and modify two private members: value1 and value2, the first of which is an 32-bit signed integer, and the second a gdouble. | |
- | + | ||
- | The implementation is a simple non-inheriting stand-alone class, implementing an interface to access and modify two private members: | + | |
The per-class constructor and the per-object constructor must be implemented. Both of these are quite short for the first version of the implementation. | The per-class constructor and the per-object constructor must be implemented. Both of these are quite short for the first version of the implementation. | ||
== D-Bus Interface Definition Using XML == | == D-Bus Interface Definition Using XML == | ||
- | |||
Because the primary objective here is to make the object available over D-Bus, the example starts by covering one of the easiest way of achieving this: the dbus-bindings-tool. The tool generates a lot of the bindings code for both the client and server side. As input the uses an XML file describing the interface for the service that is being implemented. | Because the primary objective here is to make the object available over D-Bus, the example starts by covering one of the easiest way of achieving this: the dbus-bindings-tool. The tool generates a lot of the bindings code for both the client and server side. As input the uses an XML file describing the interface for the service that is being implemented. | ||
- | The first step is to describe one method in XML. Each method is described with a separate method element, whose name attribute is the name of the method to be generated (this name is copied into the generated stub code automatically by the tool). The first method is | + | The first step is to describe one method in XML. Each method is described with a separate method element, whose name attribute is the name of the method to be generated (this name is copied into the generated stub code automatically by the tool). The first method is setvalue1, which gets one argument, new_value, which is an 32-bit signed integer: glib-dbus-sync/value-dbus-interface.xml |
- | < | + | <tt><span>''<span><font color="#9A1900"><!- setvalue1(int newValue): sets value1 -></font></span>''</span> |
- | + | <span>'''<span><font color="#0000FF"><method</font></span>'''</span> <span><font color="#009900">name</font></span><span><font color="#990000"><nowiki>=</nowiki></font></span><span><font color="#FF0000">"setvalue1"</font></span><span>'''<span><font color="#0000FF">></font></span>'''</span> | |
- | <method name="setvalue1"> | + | <span>'''<span><font color="#0000FF"><arg</font></span>'''</span> <span><font color="#009900">type</font></span><span><font color="#990000"><nowiki>=</nowiki></font></span><span><font color="#FF0000">"i"</font></span> <span><font color="#009900">name</font></span><span><font color="#990000"><nowiki>=</nowiki></font></span><span><font color="#FF0000">"new_value"</font></span> <span><font color="#009900">direction</font></span><span><font color="#990000"><nowiki>=</nowiki></font></span><span><font color="#FF0000">"in"</font></span><span>'''<span><font color="#0000FF">/></font></span>'''</span> |
- | + | <span>'''<span><font color="#0000FF"></method></font></span>'''</span></tt> | |
- | </method> | + | |
- | </ | + | |
- | + | Each argument needs to be defined explicitly with the arg element. The type attribute is required because it defines the data type for the argument. Arguments are sometimes called parameters when they are used with D-Bus methods. Each argument needs to specify the "direction" of the argument. Parameters for method calls are "going into" the service, hence the correct content for the direction attribute is in. Return values from method calls are "coming out" of the service. Hence, their direction is out. If a method call does not return any value (returns void), no argument with the direction out needs to be specified. | |
- | D-Bus by itself does not limit the number of return arguments. C language supports only one return value from a function, but a lot of the higher level languages do not have this restriction. | + | It should also be noted that D-Bus by itself does not limit the number of return arguments. C language supports only one return value from a function, but a lot of the higher level languages do not have this restriction. |
The following argument types are supported for D-Bus methods (with respective closest types in GLib): | The following argument types are supported for D-Bus methods (with respective closest types in GLib): | ||
- | * | + | * b: boolean (gboolean) |
- | * | + | * y: 8-bit unsigned integer (guint8) |
- | * | + | * q/n: 16-bit unsigned/signed integer (guint16/gint16) |
- | * | + | * u/i: 32-bit unsigned/signed integer (guint32/gint32) |
- | * | + | * t/x: 64-bit unsigned/signed integer (guint64/gint64) |
- | * | + | * d: IEEE 754 double precision floating point number (gdouble) |
- | * | + | * s: UTF-8 encoded text string with NUL termination (only one NUL allowed) (gchar* with additional restrictions) |
- | * | + | * a: Array of the following type specification (case-dependent) |
- | * | + | * o/g/r/(/)/v/e//: Complex types, please see the [http://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-signatures official D-Bus documentation on type signatures]. |
- | From the above list, it can be seen that setvalue1 accepts one 32-bit signed integer argument ( | + | From the above list, it can be seen that setvalue1 accepts one 32-bit signed integer argument (new_value). The name of the argument affects the generated stub code prototypes (not the implementation), but is quite useful for documentation, and also for D-Bus introspection. |
- | The next step is the interface specification of another method: | + | The next step is the interface specification of another method: getvalue1, which returns the current integer value of the object. It has no method call parameters (no arguments with direction="in"), and only returns one 32-bit signed integer: glib-dbus-sync/value-dbus-interface.xml |
- | < | + | <tt> <span>''<span><font color="#9A1900"><!- getvalue1(): returns the first value (int) -></font></span>''</span> |
- | + | <span>'''<span><font color="#0000FF"><method</font></span>'''</span> <span><font color="#009900">name</font></span><span><font color="#990000"><nowiki>=</nowiki></font></span><span><font color="#FF0000">"getvalue1"</font></span><span>'''<span><font color="#0000FF">></font></span>'''</span> | |
- | + | <span>'''<span><font color="#0000FF"><arg</font></span>'''</span> <span><font color="#009900">type</font></span><span><font color="#990000"><nowiki>=</nowiki></font></span><span><font color="#FF0000">"i"</font></span> <span><font color="#009900">name</font></span><span><font color="#990000"><nowiki>=</nowiki></font></span><span><font color="#FF0000">"cur_value"</font></span> <span><font color="#009900">direction</font></span><span><font color="#990000"><nowiki>=</nowiki></font></span><span><font color="#FF0000">"out"</font></span><span>'''<span><font color="#0000FF">/></font></span>'''</span> | |
- | + | <span>'''<span><font color="#0000FF"></method></font></span>'''</span></tt> | |
- | + | ||
- | </ | + | |
Naming of the return arguments is also supported in D-Bus (as above). This does not influence the generated stub code, but serves as additional documentation. | Naming of the return arguments is also supported in D-Bus (as above). This does not influence the generated stub code, but serves as additional documentation. | ||
- | + | The methods need to be bound to a specific (D-Bus) interface. This is achieved by placing the method elements within an interface element. The interface name attribute is optional but very much recommended (otherwise the interface becomes unnamed and provides less useful information on introspection). | |
- | Multiple interfaces can be implemented in the same object, and if this is the case, the multiple interface elements are listed within the node element. The node element is the "top-level" element. In this case, only one explicit interface is implemented (the binding tools add the introspection interfaces automatically, so specifying them is not necessary in the XML). And so, the result is the minimum required interface XML file: | + | Multiple interfaces can be implemented in the same object, and if this is the case, the multiple interface elements are listed within the node element. The node element is the "top-level" element. In this case, only one explicit interface is implemented (the binding tools add the introspection interfaces automatically, so specifying them is not necessary in the XML). And so, the result is the minimum required interface XML file: glib-dbus-sync/value-dbus-interface.xml |
- | < | + | <tt><span>'''<span><font color="#000080"><?xml</font></span>'''</span> <span><font color="#009900">version</font></span><span><font color="#990000"><nowiki>=</nowiki></font></span><span><font color="#FF0000">"1.0"</font></span> <span><font color="#009900">encoding</font></span><span><font color="#990000"><nowiki>=</nowiki></font></span><span><font color="#FF0000">"UTF-8"</font></span> <span>'''<span><font color="#000080">?></font></span>'''</span> |
- | + | <span>'''<span><font color="#0000FF"><node></font></span>'''</span> | |
- | <node> | + | <span>'''<span><font color="#0000FF"><interface</font></span>'''</span> <span><font color="#009900">name</font></span><span><font color="#990000"><nowiki>=</nowiki></font></span><span><font color="#FF0000">"org.maemo.Value"</font></span><span>'''<span><font color="#0000FF">></font></span>'''</span> |
- | + | <span>''<span><font color="#9A1900"><!- getvalue1(): returns the first value (int) -></font></span>''</span> | |
- | + | <span>'''<span><font color="#0000FF"><method</font></span>'''</span> <span><font color="#009900">name</font></span><span><font color="#990000"><nowiki>=</nowiki></font></span><span><font color="#FF0000">"getvalue1"</font></span><span>'''<span><font color="#0000FF">></font></span>'''</span> | |
- | + | <span>'''<span><font color="#0000FF"><arg</font></span>'''</span> <span><font color="#009900">type</font></span><span><font color="#990000"><nowiki>=</nowiki></font></span><span><font color="#FF0000">"i"</font></span> <span><font color="#009900">name</font></span><span><font color="#990000"><nowiki>=</nowiki></font></span><span><font color="#FF0000">"cur_value"</font></span> <span><font color="#009900">direction</font></span><span><font color="#990000"><nowiki>=</nowiki></font></span><span><font color="#FF0000">"out"</font></span><span>'''<span><font color="#0000FF">/></font></span>'''</span> | |
- | + | <span>'''<span><font color="#0000FF"></method></font></span>'''</span> | |
- | + | <span>''<span><font color="#9A1900"><!- setvalue1(int newValue): sets value1 -></font></span>''</span> | |
- | + | <span>'''<span><font color="#0000FF"><method</font></span>'''</span> <span><font color="#009900">name</font></span><span><font color="#990000"><nowiki>=</nowiki></font></span><span><font color="#FF0000">"setvalue1"</font></span><span>'''<span><font color="#0000FF">></font></span>'''</span> | |
- | + | <span>'''<span><font color="#0000FF"><arg</font></span>'''</span> <span><font color="#009900">type</font></span><span><font color="#990000"><nowiki>=</nowiki></font></span><span><font color="#FF0000">"i"</font></span> <span><font color="#009900">name</font></span><span><font color="#990000"><nowiki>=</nowiki></font></span><span><font color="#FF0000">"new_value"</font></span> <span><font color="#009900">direction</font></span><span><font color="#990000"><nowiki>=</nowiki></font></span><span><font color="#FF0000">"in"</font></span><span>'''<span><font color="#0000FF">/></font></span>'''</span> | |
- | + | <span>'''<span><font color="#0000FF"></method></font></span>'''</span> | |
- | + | <span>'''<span><font color="#0000FF"></interface></font></span>'''</span> | |
- | + | <span>'''<span><font color="#0000FF"></node></font></span>'''</span></tt> | |
- | </node> | + | |
- | </ | + | |
- | + | The minimal interface specification is then extended by adding the correct reference to the proper DTD. This allows validation tools to work automatically with the XML file. Methods are also added to manipulate the second value. The full interface file now contains comments, describing the purpose of the interface and the methods. This is highly recommended, if publishing the interface is planned at some point, as the bare XML does not carry semantic information. glib-dbus-sync/value-dbus-interface.xml | |
- | < | + | <tt><span>'''<span><font color="#000080"><?xml</font></span>'''</span> <span><font color="#009900">version</font></span><span><font color="#990000"><nowiki>=</nowiki></font></span><span><font color="#FF0000">"1.0"</font></span> <span><font color="#009900">encoding</font></span><span><font color="#990000"><nowiki>=</nowiki></font></span><span><font color="#FF0000">"UTF-8"</font></span> <span>'''<span><font color="#000080">?></font></span>'''</span> |
- | + | <span>''<span><font color="#9A1900"><!- This Maemo code example is licensed under a MIT-style license,</font></span>''</span> | |
- | <! | + | <span>''<span><font color="#9A1900"> that can be found in the file called "License" in the same</font></span>''</span> |
- | + | <span>''<span><font color="#9A1900"> directory as this file.</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> Copyright (c) 2007-2009 Nokia Corporation. All rights reserved. -></font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"><!- If you keep the following DOCTYPE tag in your interface</font></span>''</span> | |
- | <! | + | <span>''<span><font color="#9A1900"> specification, xmllint can fetch the DTD over the Internet</font></span>''</span> |
- | + | <span>''<span><font color="#9A1900"> for validation automatically. -></font></span>''</span> | |
- | + | <span>'''<span><font color="#000080"><!DOCTYPE</font></span>'''</span> <span><font color="#009900">node</font></span> <span><font color="#009900">PUBLIC</font></span> | |
- | <!DOCTYPE node PUBLIC | + | <span><font color="#FF0000">"-//freedesktop//DTD D-Bus Object Introspection 1.0//EN"</font></span> |
- | + | <span><font color="#FF0000">"http://standards.freedesktop.org/dbus/1.0/introspect.dtd"</font></span><span>'''<span><font color="#000080">></font></span>'''</span> | |
- | + | <span>''<span><font color="#9A1900"><!- This file defines the D-Bus interface for a simple object, that</font></span>''</span> | |
- | <! | + | <span>''<span><font color="#9A1900"> holds a simple state consisting of two values (one a 32-bit</font></span>''</span> |
- | + | <span>''<span><font color="#9A1900"> integer, the other a double).</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> </font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> The interface name is "org.maemo.Value".</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> One known reference implementation is provided for it by the</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> "/GlobalValue" object found via a well-known name of</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> "org.maemo.Platdev_ex". -></font></span>''</span> | |
- | + | <span>'''<span><font color="#0000FF"><node></font></span>'''</span> | |
- | <node> | + | <span>'''<span><font color="#0000FF"><interface</font></span>'''</span> <span><font color="#009900">name</font></span><span><font color="#990000"><nowiki>=</nowiki></font></span><span><font color="#FF0000">"org.maemo.Value"</font></span><span>'''<span><font color="#0000FF">></font></span>'''</span> |
- | + | <span>''<span><font color="#9A1900"><!- Method definitions -></font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"><!- getvalue1(): returns the first value (int) -></font></span>''</span> | |
- | + | <span>'''<span><font color="#0000FF"><method</font></span>'''</span> <span><font color="#009900">name</font></span><span><font color="#990000"><nowiki>=</nowiki></font></span><span><font color="#FF0000">"getvalue1"</font></span><span>'''<span><font color="#0000FF">></font></span>'''</span> | |
- | + | <span>''<span><font color="#9A1900"><!- NOTE Naming arguments is not mandatory, but is recommended</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> so that D-Bus introspection tools are more useful.</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> Otherwise the arguments are automatically named</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> "arg0", "arg1" and so on. -></font></span>''</span> | |
- | + | <span>'''<span><font color="#0000FF"><arg</font></span>'''</span> <span><font color="#009900">type</font></span><span><font color="#990000"><nowiki>=</nowiki></font></span><span><font color="#FF0000">"i"</font></span> <span><font color="#009900">name</font></span><span><font color="#990000"><nowiki>=</nowiki></font></span><span><font color="#FF0000">"cur_value"</font></span> <span><font color="#009900">direction</font></span><span><font color="#990000"><nowiki>=</nowiki></font></span><span><font color="#FF0000">"out"</font></span><span>'''<span><font color="#0000FF">/></font></span>'''</span> | |
- | + | <span>'''<span><font color="#0000FF"></method></font></span>'''</span> | |
- | + | <span>''<span><font color="#9A1900"><!- getvalue2(): returns the second value (double) -></font></span>''</span> | |
- | + | <span>'''<span><font color="#0000FF"><method</font></span>'''</span> <span><font color="#009900">name</font></span><span><font color="#990000"><nowiki>=</nowiki></font></span><span><font color="#FF0000">"getvalue2"</font></span><span>'''<span><font color="#0000FF">></font></span>'''</span> | |
- | + | <span>'''<span><font color="#0000FF"><arg</font></span>'''</span> <span><font color="#009900">type</font></span><span><font color="#990000"><nowiki>=</nowiki></font></span><span><font color="#FF0000">"d"</font></span> <span><font color="#009900">name</font></span><span><font color="#990000"><nowiki>=</nowiki></font></span><span><font color="#FF0000">"cur_value"</font></span> <span><font color="#009900">direction</font></span><span><font color="#990000"><nowiki>=</nowiki></font></span><span><font color="#FF0000">"out"</font></span><span>'''<span><font color="#0000FF">/></font></span>'''</span> | |
- | + | <span>'''<span><font color="#0000FF"></method></font></span>'''</span> | |
- | + | <span>''<span><font color="#9A1900"><!- setvalue1(int newValue): sets value1 -></font></span>''</span> | |
- | + | <span>'''<span><font color="#0000FF"><method</font></span>'''</span> <span><font color="#009900">name</font></span><span><font color="#990000"><nowiki>=</nowiki></font></span><span><font color="#FF0000">"setvalue1"</font></span><span>'''<span><font color="#0000FF">></font></span>'''</span> | |
- | + | <span>'''<span><font color="#0000FF"><arg</font></span>'''</span> <span><font color="#009900">type</font></span><span><font color="#990000"><nowiki>=</nowiki></font></span><span><font color="#FF0000">"i"</font></span> <span><font color="#009900">name</font></span><span><font color="#990000"><nowiki>=</nowiki></font></span><span><font color="#FF0000">"new_value"</font></span> <span><font color="#009900">direction</font></span><span><font color="#990000"><nowiki>=</nowiki></font></span><span><font color="#FF0000">"in"</font></span><span>'''<span><font color="#0000FF">/></font></span>'''</span> | |
- | + | <span>'''<span><font color="#0000FF"></method></font></span>'''</span> | |
- | + | <span>''<span><font color="#9A1900"><!- setvalue2(double newValue): sets value2 -></font></span>''</span> | |
- | + | <span>'''<span><font color="#0000FF"><method</font></span>'''</span> <span><font color="#009900">name</font></span><span><font color="#990000"><nowiki>=</nowiki></font></span><span><font color="#FF0000">"setvalue2"</font></span><span>'''<span><font color="#0000FF">></font></span>'''</span> | |
- | + | <span>'''<span><font color="#0000FF"><arg</font></span>'''</span> <span><font color="#009900">type</font></span><span><font color="#990000"><nowiki>=</nowiki></font></span><span><font color="#FF0000">"d"</font></span> <span><font color="#009900">name</font></span><span><font color="#990000"><nowiki>=</nowiki></font></span><span><font color="#FF0000">"new_value"</font></span> <span><font color="#009900">direction</font></span><span><font color="#990000"><nowiki>=</nowiki></font></span><span><font color="#FF0000">"in"</font></span><span>'''<span><font color="#0000FF">/></font></span>'''</span> | |
- | + | <span>'''<span><font color="#0000FF"></method></font></span>'''</span> | |
- | + | <span>'''<span><font color="#0000FF"></interface></font></span>'''</span> | |
- | + | <span>'''<span><font color="#0000FF"></node></font></span>'''</span></tt> | |
- | </node> | + | |
- | </ | + | |
- | When dealing with automatic code generation, it is quite useful to also automate testing of the "source files" (in this case, XML). One important validation technique for XML is verifying for well-formedness (all XML files need to satisfy the rules in XML spec 1.0), another is validating the structure of XML (that elements are nested correctly, that only correct elements are present and that the element attributes and data are legal). Structural validation rules are described by a DTD (Document Type Definition) document for the XML format that the file is supposed to adhere to. The DTD is specified in the XML, within the | + | When dealing with automatic code generation, it is quite useful to also automate testing of the "source files" (in this case, XML). One important validation technique for XML is verifying for well-formedness (all XML files need to satisfy the rules in XML spec 1.0), another is validating the structure of XML (that elements are nested correctly, that only correct elements are present and that the element attributes and data are legal). Structural validation rules are described by a DTD (Document Type Definition) document for the XML format that the file is supposed to adhere to. The DTD is specified in the XML, within the DOCTYPE processing directive. |
- | This is still not perfect, | + | This is still not perfect, as DTD validation can only check for syntax and structure, but not for meaning or semantics. |
- | The next step is to add a target called | + | The next step is to add a target called checkxml to the '''Makefile''', so that it can be run whenever the validity of the interface XML is to be checked. glib-dbus-sync/Makefile |
- | < | + | <tt><span>''<span><font color="#9A1900"><nowiki># One extra target (which requires xmllint, from package libxml2-utils)</nowiki></font></span>''</span> |
- | # One extra target (which requires xmllint, from package libxml2-utils) | + | <span>''<span><font color="#9A1900"><nowiki># is available to verify the well-formedness and the structure of the</nowiki></font></span>''</span> |
- | # is available to verify the well-formedness and the structure of the | + | <span>''<span><font color="#9A1900"><nowiki># interface definition xml file.</nowiki></font></span>''</span> |
- | # interface definition xml file. | + | <span>''<span><font color="#9A1900"><nowiki>#</nowiki></font></span>''</span> |
- | # | + | <span>''<span><font color="#9A1900"><nowiki># Use the 'checkxml' target to run the interface XML through xmllint</nowiki></font></span>''</span> |
- | # Use the 'checkxml' target to run the interface XML through xmllint | + | <span>''<span><font color="#9A1900"><nowiki># verification. You need to be connected to the Internet in order</nowiki></font></span>''</span> |
- | # verification. You need to be connected to the Internet in order | + | <span>''<span><font color="#9A1900"><nowiki># for xmllint to retrieve the DTD from fd.o (unless you setup local</nowiki></font></span>''</span> |
- | # for xmllint to retrieve the DTD from fd.o (unless you setup local | + | <span>''<span><font color="#9A1900"><nowiki># catalogs, which are not covered here).</nowiki></font></span>''</span> |
- | # catalogs, which are not covered here). | + | <span>''<span><font color="#9A1900"><nowiki># ... Listing cut for brevity ...</nowiki></font></span>''</span> |
- | # ... Listing cut for brevity ... | + | <span>''<span><font color="#9A1900"><nowiki># Interface XML name (used in multiple targets)</nowiki></font></span>''</span> |
- | # Interface XML name (used in multiple targets) | + | interface_xml <span><font color="#990000"><nowiki>:=</nowiki></font></span> value-dbus-interface<span><font color="#990000">.</font></span>xml |
- | interface_xml := value-dbus-interface.xml | + | <span>''<span><font color="#9A1900"><nowiki># ... Listing cut for brevity ...</nowiki></font></span>''</span> |
- | # ... Listing cut for brevity ... | + | <span>''<span><font color="#9A1900"><nowiki># Special target to run DTD validation on the interface XML. Not run</nowiki></font></span>''</span> |
- | # Special target to run DTD validation on the interface XML. Not run | + | <span>''<span><font color="#9A1900"><nowiki># automatically (since xmllint is not always available and also needs</nowiki></font></span>''</span> |
- | # automatically (since xmllint is not always available and also needs | + | <span>''<span><font color="#9A1900"><nowiki># Internet connectivity).</nowiki></font></span>''</span> |
- | # Internet connectivity). | + | checkxml<span><font color="#990000"><nowiki>:</nowiki></font></span> <span><font color="#009900">$(interface_xml)</font></span> |
- | checkxml: $(interface_xml) | + | @xmllint -valid -noout <span><font color="#009900">$<</font></span> |
- | + | @echo <span><font color="#009900">$<</font></span> checks out ok | |
- | + | </tt> | |
- | </ | + | |
- | < | + | <div class="graybox"> |
- | + | ||
- | + | ||
- | + | ||
- | + | ||
+ | [sbox-DIABLO_X86: ~/glib-dbus-sync] > make checkxml | ||
+ | value-dbus-interface.xml checks out ok | ||
- | + | </div> | |
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | </ | + | |
- | + | ||
- | + | ||
- | + | ||
- | ^ | + | Just to demonstrate what kind of error messages to expect when there are problems in the XML, the valid interface specification is modified slightly by adding one invalid element (invalidElement), and by removing one starting tag (method). |
- | make: *** [checkxml] Error 1 | + | |
- | </ | + | <div class="graybox"> |
+ | [sbox-DIABLO_X86: ~/glib-dbus-sync] > make checkxml | ||
+ | value-dbus-interface.xml:36: element invalidElement: validity error : | ||
+ | No declaration for element invalidElement | ||
+ | </invalidElement> | ||
+ | ^ | ||
+ | value-dbus-interface.xml:53: parser error : | ||
+ | Opening and ending tag mismatch: method line 39 and interface | ||
+ | </interface> | ||
+ | ^ | ||
+ | value-dbus-interface.xml:54: parser error : | ||
+ | Opening and ending tag mismatch: interface line 22 and node | ||
+ | </node> | ||
+ | ^ | ||
+ | value-dbus-interface.xml:55: parser error : | ||
+ | Premature end of data in tag node line 21 | ||
+ | |||
+ | ^ | ||
+ | make: *** [checkxml] Error 1 | ||
+ | |||
+ | </div> | ||
The first error (validity error) is detected, because the file does not adhere to the DTD. The other errors (parser errors) are detected, because the file is no longer a well-formed XML document. | The first error (validity error) is detected, because the file does not adhere to the DTD. The other errors (parser errors) are detected, because the file is no longer a well-formed XML document. | ||
- | If the makefile targets depend on | + | If the makefile targets depend on checkxml, the validation can be integrated into the process of the build. However, as was noted before, it may not always be the best solution. |
==Generating Automatic Stub Code == | ==Generating Automatic Stub Code == | ||
+ | The following step is to generate the "glue" code that implements the mapping from GLib into D-Bus. The generated code is used later on, but it is useful to see at this point what the dbus-binding-tool program generates. | ||
- | The | + | The '''Makefile''' is expanded to invoke the tool whenever the interface XML changes and store the resulting glue code separately for both the client and server. glib-dbus-sync/Makefile |
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | clean: | + | <tt><span>''<span><font color="#9A1900"><nowiki># Define a list of generated files so that they can be cleaned as well</nowiki></font></span>''</span> |
- | + | cleanfiles <span><font color="#990000"><nowiki>:=</nowiki></font></span> value-client-stub<span><font color="#990000">.</font></span>h <span><font color="#990000">\</font></span> | |
- | </ | + | value-server-stub<span><font color="#990000">.</font></span>h |
+ | |||
+ | <span>''<span><font color="#9A1900"><nowiki># ... Listing cut for brevity ...</nowiki></font></span>''</span> | ||
+ | |||
+ | <span>''<span><font color="#9A1900"><nowiki># If the interface XML changes, the respective stub interfaces are</nowiki></font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"><nowiki># automatically regenerated. Normally this also means that your</nowiki></font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"><nowiki># builds fail after this because you are missing implementation</nowiki></font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"><nowiki># code.</nowiki></font></span>''</span> | ||
+ | value-server-stub<span><font color="#990000">.</font></span>h<span><font color="#990000"><nowiki>:</nowiki></font></span> <span><font color="#009900">$(interface_xml)</font></span> | ||
+ | dbus-binding-tool -prefix<span><font color="#990000"><nowiki>=</nowiki></font></span>value_object -mode<span><font color="#990000"><nowiki>=</nowiki></font></span>glib-server <span><font color="#990000">\</font></span> | ||
+ | <span><font color="#009900">$<</font></span> <span><font color="#990000">></font></span> <span><font color="#009900">$@</font></span> | ||
+ | |||
+ | value-client-stub<span><font color="#990000">.</font></span>h<span><font color="#990000"><nowiki>:</nowiki></font></span> <span><font color="#009900">$(interface_xml)</font></span> | ||
+ | dbus-binding-tool -prefix<span><font color="#990000"><nowiki>=</nowiki></font></span>value_object -mode<span><font color="#990000"><nowiki>=</nowiki></font></span>glib-client <span><font color="#990000">\</font></span> | ||
+ | <span><font color="#009900">$<</font></span> <span><font color="#990000">></font></span> <span><font color="#009900">$@</font></span> | ||
+ | |||
+ | <span>''<span><font color="#9A1900"><nowiki># ... Listing cut for brevity ...</nowiki></font></span>''</span> | ||
+ | |||
+ | clean<span><font color="#990000"><nowiki>:</nowiki></font></span> | ||
+ | <span><font color="#009900">$(RM)</font></span> <span><font color="#009900">$(targets)</font></span> <span><font color="#009900">$(cleanfiles)</font></span> <span><font color="#990000"><nowiki>*.</nowiki></font></span>o | ||
+ | </tt> | ||
- | + | Two parameters are passed for the dbus-binding-tool program. The <code>--prefix</code> parameter is used to tell which text should be prefixed to all generated structure and function names. This helps to avoid namespace collisions when pulling the generated glue files back into the programs. The value_object is used because it seems like a logical prefix for the project. The recommendation is to use a prefix that is not used in the code (even in the object implementation in server). This way, there is no risk of reusing the same names that are generated with the tool. | |
- | The second parameter selects what kind of output the tool generates. At the moment, the tool only supports generating GLib/D-Bus bindings, but this might change in the future. Furthermore, | + | The second parameter selects what kind of output the tool generates. At the moment, the tool only supports generating GLib/D-Bus bindings, but this might change in the future. Furthermore, it needs to be selected which "side" of the D-Bus the bindings are generated for. The -client side is for code that wishes to use GLib to access the Value object implementation over D-Bus. The -server side is respectively for the implementation of the Value object. |
Running the tool results in the following two header files: | Running the tool results in the following two header files: | ||
- | < | + | <div class="graybox"> |
- | [sbox-DIABLO_X86: ~/glib-dbus-sync] | + | [sbox-DIABLO_X86: ~/glib-dbus-sync] > make value-server-stub.h value-client-stub.h |
- | dbus-binding-tool --prefix=value_object --mode=glib-server \ | + | dbus-binding-tool --prefix=value_object --mode=glib-server \ |
- | + | value-dbus-interface.xml > value-server-stub.h | |
- | dbus-binding-tool --prefix=value_object --mode=glib-client \ | + | dbus-binding-tool --prefix=value_object --mode=glib-client \ |
- | + | value-dbus-interface.xml > value-client-stub.h | |
- | [sbox-DIABLO_X86: ~/glib-dbus-sync] | + | [sbox-DIABLO_X86: ~/glib-dbus-sync] > ls -la value*stub.h |
- | -rw-rw-r-- 1 user user 5184 Nov 21 14:02 value-client-stub.h | + | -rw-rw-r-- 1 user user 5184 Nov 21 14:02 value-client-stub.h |
- | -rw-rw-r-- 1 user user 10603 Nov 21 14:02 value-server-stub.h | + | -rw-rw-r-- 1 user user 10603 Nov 21 14:02 value-server-stub.h |
- | + | ||
- | + | </div> | |
- | + | Check what the tool produced, starting with the server stub file: glib-dbus-sync/value-server-stub.h | |
- | + | ||
- | + | <tt><span>''<span><font color="#9A1900">/* Generated by dbus-binding-tool; do not edit! */</font></span>''</span> | |
+ | |||
+ | <span>''<span><font color="#9A1900">/*... Listing cut for brevity ...*/</font></span>''</span> | ||
+ | |||
+ | <span>'''<span><font color="#000080"><nowiki>#include</nowiki></font></span>'''</span> <span><font color="#FF0000"><dbus/dbus-glib.h></font></span> | ||
+ | <span>'''<span><font color="#0000FF">static</font></span>'''</span> <span>'''<span><font color="#0000FF">const</font></span>'''</span> DBusGMethodInfo dbus_glib_value_object_methods<span><font color="#990000">[]</font></span> <span><font color="#990000"><nowiki>=</nowiki></font></span> <span><font color="#FF0000">{</font></span> | ||
+ | <span><font color="#FF0000">{</font></span> <span><font color="#990000">(</font></span>GCallback<span><font color="#990000">)</font></span> value_object_getvalue1<span><font color="#990000">,</font></span> | ||
+ | dbus_glib_marshal_value_object_BOOLEAN__POINTER_POINTER<span><font color="#990000">,</font></span> <span><font color="#993399">0</font></span> <span><font color="#FF0000">}</font></span><span><font color="#990000">,</font></span> | ||
+ | <span><font color="#FF0000">{</font></span> <span><font color="#990000">(</font></span>GCallback<span><font color="#990000">)</font></span> value_object_getvalue2<span><font color="#990000">,</font></span> | ||
+ | dbus_glib_marshal_value_object_BOOLEAN__POINTER_POINTER<span><font color="#990000">,</font></span> <span><font color="#993399">47</font></span> <span><font color="#FF0000">}</font></span><span><font color="#990000">,</font></span> | ||
+ | <span><font color="#FF0000">{</font></span> <span><font color="#990000">(</font></span>GCallback<span><font color="#990000">)</font></span> value_object_setvalue1<span><font color="#990000">,</font></span> | ||
+ | dbus_glib_marshal_value_object_BOOLEAN__INT_POINTER<span><font color="#990000">,</font></span> <span><font color="#993399">94</font></span> <span><font color="#FF0000">}</font></span><span><font color="#990000">,</font></span> | ||
+ | <span><font color="#FF0000">{</font></span> <span><font color="#990000">(</font></span>GCallback<span><font color="#990000">)</font></span> value_object_setvalue2<span><font color="#990000">,</font></span> | ||
+ | dbus_glib_marshal_value_object_BOOLEAN__DOUBLE_POINTER<span><font color="#990000">,</font></span> <span><font color="#993399">137</font></span> <span><font color="#FF0000">}</font></span><span><font color="#990000">,</font></span> | ||
+ | <span><font color="#FF0000">}</font></span><span><font color="#990000"><nowiki>;</nowiki></font></span> | ||
+ | |||
+ | <span>'''<span><font color="#0000FF">const</font></span>'''</span> DBusGObjectInfo dbus_glib_value_object_object_info <span><font color="#990000"><nowiki>=</nowiki></font></span> <span><font color="#FF0000">{</font></span> | ||
+ | <span><font color="#993399">0</font></span><span><font color="#990000">,</font></span> | ||
+ | dbus_glib_value_object_methods<span><font color="#990000">,</font></span> | ||
+ | <span><font color="#993399">4</font></span><span><font color="#990000">,</font></span> | ||
+ | <span><font color="#FF0000">"org.maemo.Value</font></span><span><font color="#CC33CC">\0</font></span><span><font color="#FF0000">getvalue1</font></span><span><font color="#CC33CC">\0</font></span><span><font color="#FF0000">S</font></span><span><font color="#CC33CC">\0</font></span><span><font color="#FF0000">cur_value</font></span><span><font color="#CC33CC">\0</font></span><span><font color="#FF0000">O</font></span><span><font color="#CC33CC">\0</font></span><span><font color="#FF0000">F</font></span><span><font color="#CC33CC">\0</font></span><span><font color="#FF0000">N</font></span><span><font color="#CC33CC">\0</font></span><span><font color="#FF0000">i</font></span><span><font color="#CC33CC">\0\0</font></span><span><font color="#FF0000">"</font></span> | ||
+ | <span><font color="#FF0000">"org.maemo.Value</font></span><span><font color="#CC33CC">\0</font></span><span><font color="#FF0000">getvalue2</font></span><span><font color="#CC33CC">\0</font></span><span><font color="#FF0000">S</font></span><span><font color="#CC33CC">\0</font></span><span><font color="#FF0000">cur_value</font></span><span><font color="#CC33CC">\0</font></span><span><font color="#FF0000">O</font></span><span><font color="#CC33CC">\0</font></span><span><font color="#FF0000">F</font></span><span><font color="#CC33CC">\0</font></span><span><font color="#FF0000">N</font></span><span><font color="#CC33CC">\0</font></span><span><font color="#FF0000">d</font></span><span><font color="#CC33CC">\0\0</font></span><span><font color="#FF0000">"</font></span> | ||
+ | <span><font color="#FF0000">"org.maemo.Value</font></span><span><font color="#CC33CC">\0</font></span><span><font color="#FF0000">setvalue1</font></span><span><font color="#CC33CC">\0</font></span><span><font color="#FF0000">S</font></span><span><font color="#CC33CC">\0</font></span><span><font color="#FF0000">new_value</font></span><span><font color="#CC33CC">\0</font></span><span><font color="#FF0000">I</font></span><span><font color="#CC33CC">\0</font></span><span><font color="#FF0000">i</font></span><span><font color="#CC33CC">\0\0</font></span><span><font color="#FF0000">"</font></span> | ||
+ | <span><font color="#FF0000">"org.maemo.Value</font></span><span><font color="#CC33CC">\0</font></span><span><font color="#FF0000">setvalue2</font></span><span><font color="#CC33CC">\0</font></span><span><font color="#FF0000">S</font></span><span><font color="#CC33CC">\0</font></span><span><font color="#FF0000">new_value</font></span><span><font color="#CC33CC">\0</font></span><span><font color="#FF0000">I</font></span><span><font color="#CC33CC">\0</font></span><span><font color="#FF0000">d</font></span><span><font color="#CC33CC">\0\0\0</font></span><span><font color="#FF0000">"</font></span><span><font color="#990000">,</font></span> | ||
+ | <span><font color="#FF0000">"</font></span><span><font color="#CC33CC">\0</font></span><span><font color="#FF0000">"</font></span><span><font color="#990000">,</font></span> | ||
+ | <span><font color="#FF0000">"</font></span><span><font color="#CC33CC">\0</font></span><span><font color="#FF0000">"</font></span> | ||
+ | <span><font color="#FF0000">}</font></span><span><font color="#990000"><nowiki>;</nowiki></font></span></tt> | ||
- | + | The interest here lies in the method table, mainly because it lists the names of the functions that need to be implemented: value_object_getvalue1, 0_object_getvalue2, value_object_setvalue1 and value_object_setvalue2. Each entry in the table consists of a function address, and the function to use to marshal data from/to GLib/D-Bus (the functions that start with dbus_glib_marshal_*). The marshaling functions are defined in the same file, but were omitted from the listing above. | |
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | Marshaling in its most generic form means the conversion of parameters or arguments from one format to another, in order to make two different parameter passing conventions compatible. It is a common feature found in almost all RPC mechanisms. Because GLib has its own type system and a D-Bus of its own, it would be very tedious to write the conversion code manually. This is where the binding generation tool really helps. | |
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | The | + | The other interesting feature of the above listing is the _object_info structure. The structure is passed to the D-Bus daemon when the object is ready to be published on the bus (so that clients may invoke methods on it). The very long string (that contains binary zeros) is the compact format of the interface specification. Similarities can be seen between the names in the string and the names of the interface, methods and arguments that were declared in the XML. The structure is also an important part of the D-Bus introspection mechanism. |
- | + | As the snippet says at the very first line, it should never be edited manually. This holds true while using the XML file as the source of an interface. It is also possible to use the XML only once, when starting the project, and then just start copy-pasting the generated glue code around, while discarding the XML file and dbus-binding-tool. Needless to say, this makes maintenance of the interface much more difficult and is not really recommended. The generated stub code is not edited in this material. | |
- | + | ||
- | + | ||
- | + | ||
- | As the snippet says at the very first line, | + | |
The example continues next with the server implementation for the functions that are called via the method table. | The example continues next with the server implementation for the functions that are called via the method table. | ||
== Creating Simple GObject for D-Bus == | == Creating Simple GObject for D-Bus == | ||
+ | The starting point here is with the per-instance and per-class state structures for the object. The per-class structure contains only the bare minimum contents, which are required from all classes in GObject. The per-instance structure contains the required "parent object" state (GObject), but also includes the two internal values (value1 and value2), with which the rest of this example is concerned: glib-dbus-sync/server.c | ||
- | + | <tt><span>''<span><font color="#9A1900">/* This defines the per-instance state.</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> Each GObject must start with the 'parent' definition so that common</font></span>''</span> | |
- | < | + | <span>''<span><font color="#9A1900"> operations that all GObjects support can be called on it. */</font></span>''</span> |
- | + | <span>'''<span><font color="#0000FF">typedef</font></span>'''</span> <span>'''<span><font color="#0000FF">struct</font></span>'''</span> <span><font color="#FF0000">{</font></span> | |
- | + | <span>''<span><font color="#9A1900">/* The parent class object state. */</font></span>''</span> | |
- | + | GObject parent<span><font color="#990000"><nowiki>;</nowiki></font></span> | |
- | typedef struct { | + | <span>''<span><font color="#9A1900">/* Our first per-object state variable. */</font></span>''</span> |
- | + | gint value1<span><font color="#990000"><nowiki>;</nowiki></font></span> | |
- | + | <span>''<span><font color="#9A1900">/* Our second per-object state variable. */</font></span>''</span> | |
- | + | gdouble value2<span><font color="#990000"><nowiki>;</nowiki></font></span> | |
- | + | <span><font color="#FF0000">}</font></span> ValueObject<span><font color="#990000"><nowiki>;</nowiki></font></span> | |
- | + | <span>''<span><font color="#9A1900">/* Per class state.</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> For the first Value implementation we only have the bare minimum,</font></span>''</span> | |
- | } ValueObject; | + | <span>''<span><font color="#9A1900"> that is, the common implementation for any GObject class. */</font></span>''</span> |
- | /* Per class state. | + | <span>'''<span><font color="#0000FF">typedef</font></span>'''</span> <span>'''<span><font color="#0000FF">struct</font></span>'''</span> <span><font color="#FF0000">{</font></span> |
- | + | <span>''<span><font color="#9A1900">/* The parent class state. */</font></span>''</span> | |
- | + | GObjectClass parent<span><font color="#990000"><nowiki>;</nowiki></font></span> | |
- | typedef struct { | + | <span><font color="#FF0000">}</font></span> ValueObjectClass<span><font color="#990000"><nowiki>;</nowiki></font></span> |
- | + | </tt> | |
- | + | ||
- | + | ||
- | </ | + | |
- | + | ||
- | + | ||
- | + | The convenience macros are defined in a way expected for all GTypes. The G_TYPE_ macros are defined in GType and include the magic by which the object implementation does not need to know much about the internal specifics of GType. The GType macros are described in the [http://maemo.org/api_refs/5.0/beta/gobject/ GObject API reference for GType]. | |
- | + | Some of the macros are used internally in this implementation later on. glib-dbus-sync/server.c | |
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | /* Macro for the above. It is common to define macros using the | + | <tt><span>''<span><font color="#9A1900">/* Forward declaration of the function that returns the GType of</font></span>''</span> |
- | + | <span>''<span><font color="#9A1900"> the Value implementation. Not used in this program because we only</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> need to push this over the D-Bus. */</font></span>''</span> | |
- | #define VALUE_TYPE_OBJECT (value_object_get_type()) | + | GType <span>'''<span><font color="#000000">value_object_get_type</font></span>'''</span><span><font color="#990000">(</font></span><span><font color="#009900">void</font></span><span><font color="#990000">);</font></span> |
+ | |||
+ | <span>''<span><font color="#9A1900">/* Macro for the above. It is common to define macros using the</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> naming convention (seen below) for all GType implementations,</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> and that is why we are going to do that here as well. */</font></span>''</span> | ||
+ | <span>'''<span><font color="#000080"><nowiki>#define</nowiki></font></span>'''</span> <span>'''<span><font color="#000000">VALUE_TYPE_OBJECT</font></span>'''</span> <span><font color="#990000">(</font></span><span>'''<span><font color="#000000">value_object_get_type</font></span>'''</span><span><font color="#990000">())</font></span> | ||
+ | |||
+ | <span>'''<span><font color="#000080"><nowiki>#define</nowiki></font></span>'''</span> <span>'''<span><font color="#000000">VALUE_OBJECT</font></span>'''</span><span><font color="#990000">(</font></span>object<span><font color="#990000">)</font></span> <span><font color="#990000">\</font></span> | ||
+ | <span><font color="#990000">(</font></span><span>'''<span><font color="#000000">G_TYPE_CHECK_INSTANCE_CAST</font></span>'''</span><span><font color="#990000">((</font></span>object<span><font color="#990000">),</font></span> <span><font color="#990000">\</font></span> | ||
+ | VALUE_TYPE_OBJECT<span><font color="#990000">,</font></span> ValueObject<span><font color="#990000">))</font></span> | ||
+ | <span>'''<span><font color="#000080"><nowiki>#define</nowiki></font></span>'''</span> <span>'''<span><font color="#000000">VALUE_OBJECT_CLASS</font></span>'''</span><span><font color="#990000">(</font></span>klass<span><font color="#990000">)</font></span> <span><font color="#990000">\</font></span> | ||
+ | <span><font color="#990000">(</font></span><span>'''<span><font color="#000000">G_TYPE_CHECK_CLASS_CAST</font></span>'''</span><span><font color="#990000">((</font></span>klass<span><font color="#990000">),</font></span> <span><font color="#990000">\</font></span> | ||
+ | VALUE_TYPE_OBJECT<span><font color="#990000">,</font></span> ValueObjectClass<span><font color="#990000">))</font></span> | ||
+ | <span>'''<span><font color="#000080"><nowiki>#define</nowiki></font></span>'''</span> <span>'''<span><font color="#000000">VALUE_IS_OBJECT</font></span>'''</span><span><font color="#990000">(</font></span>object<span><font color="#990000">)</font></span> <span><font color="#990000">\</font></span> | ||
+ | <span><font color="#990000">(</font></span><span>'''<span><font color="#000000">G_TYPE_CHECK_INSTANCE_TYPE</font></span>'''</span><span><font color="#990000">((</font></span>object<span><font color="#990000">),</font></span> <span><font color="#990000">\</font></span> | ||
+ | VALUE_TYPE_OBJECT<span><font color="#990000">))</font></span> | ||
+ | <span>'''<span><font color="#000080"><nowiki>#define</nowiki></font></span>'''</span> <span>'''<span><font color="#000000">VALUE_IS_OBJECT_CLASS</font></span>'''</span><span><font color="#990000">(</font></span>klass<span><font color="#990000">)</font></span> <span><font color="#990000">\</font></span> | ||
+ | <span><font color="#990000">(</font></span><span>'''<span><font color="#000000">G_TYPE_CHECK_CLASS_TYPE</font></span>'''</span><span><font color="#990000">((</font></span>klass<span><font color="#990000">),</font></span> <span><font color="#990000">\</font></span> | ||
+ | VALUE_TYPE_OBJECT<span><font color="#990000">))</font></span> | ||
+ | <span>'''<span><font color="#000080"><nowiki>#define</nowiki></font></span>'''</span> <span>'''<span><font color="#000000">VALUE_OBJECT_GET_CLASS</font></span>'''</span><span><font color="#990000">(</font></span>obj<span><font color="#990000">)</font></span> <span><font color="#990000">\</font></span> | ||
+ | <span><font color="#990000">(</font></span><span>'''<span><font color="#000000">G_TYPE_INSTANCE_GET_CLASS</font></span>'''</span><span><font color="#990000">((</font></span>obj<span><font color="#990000">),</font></span> <span><font color="#990000">\</font></span> | ||
+ | VALUE_TYPE_OBJECT<span><font color="#990000">,</font></span> ValueObjectClass<span><font color="#990000">))</font></span> | ||
+ | |||
+ | <span>''<span><font color="#9A1900">/* Utility macro to define the value_object GType structure. */</font></span>''</span> | ||
+ | <span>'''<span><font color="#000000">G_DEFINE_TYPE</font></span>'''</span><span><font color="#990000">(</font></span>ValueObject<span><font color="#990000">,</font></span> value_object<span><font color="#990000">,</font></span> G_TYPE_OBJECT<span><font color="#990000">)</font></span> | ||
+ | </tt> | ||
- | + | After the macros, the next phase contains the instance initialization and class initialization functions, of which the class initialization function contains the integration call into GLib/D-Bus: glib-dbus-sync/server.c | |
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | /* | + | <tt><span>''<span><font color="#9A1900">/**</font></span>''</span> |
- | + | <span>''<span><font color="#9A1900"> * Because the stub generator references the functions from a call</font></span>''</span> | |
- | </ | + | <span>''<span><font color="#9A1900"> * table, the functions must be declared before the stub is included.</font></span>''</span> |
+ | <span>''<span><font color="#9A1900"> */</font></span>''</span> | ||
+ | gboolean <span>'''<span><font color="#000000">value_object_getvalue1</font></span>'''</span><span><font color="#990000">(</font></span>ValueObject<span><font color="#990000"><nowiki>*</nowiki></font></span> obj<span><font color="#990000">,</font></span> gint<span><font color="#990000"><nowiki>*</nowiki></font></span> value_out<span><font color="#990000">,</font></span> | ||
+ | GError<span><font color="#990000"><nowiki>**</nowiki></font></span> error<span><font color="#990000">);</font></span> | ||
+ | gboolean <span>'''<span><font color="#000000">value_object_getvalue2</font></span>'''</span><span><font color="#990000">(</font></span>ValueObject<span><font color="#990000"><nowiki>*</nowiki></font></span> obj<span><font color="#990000">,</font></span> gdouble<span><font color="#990000"><nowiki>*</nowiki></font></span> value_out<span><font color="#990000">,</font></span> | ||
+ | GError<span><font color="#990000"><nowiki>**</nowiki></font></span> error<span><font color="#990000">);</font></span> | ||
+ | gboolean <span>'''<span><font color="#000000">value_object_setvalue1</font></span>'''</span><span><font color="#990000">(</font></span>ValueObject<span><font color="#990000"><nowiki>*</nowiki></font></span> obj<span><font color="#990000">,</font></span> gint value_in<span><font color="#990000">,</font></span> | ||
+ | GError<span><font color="#990000"><nowiki>**</nowiki></font></span> error<span><font color="#990000">);</font></span> | ||
+ | gboolean <span>'''<span><font color="#000000">value_object_setvalue2</font></span>'''</span><span><font color="#990000">(</font></span>ValueObject<span><font color="#990000"><nowiki>*</nowiki></font></span> obj<span><font color="#990000">,</font></span> gdouble value_in<span><font color="#990000">,</font></span> | ||
+ | GError<span><font color="#990000"><nowiki>**</nowiki></font></span> error<span><font color="#990000">);</font></span> | ||
+ | <span>''<span><font color="#9A1900">/**</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> * Pull in the stub for the server side.</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> */</font></span>''</span> | ||
+ | <span>'''<span><font color="#000080"><nowiki>#include</nowiki></font></span>'''</span> <span><font color="#FF0000">"value-server-stub.h"</font></span> | ||
+ | <span>''<span><font color="#9A1900">/*... Listing cut for brevity ...*/</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900">/**</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> * Per object initializer</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> *</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> * Only sets up internal state (both values set to zero)</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> */</font></span>''</span> | ||
+ | <span>'''<span><font color="#0000FF">static</font></span>'''</span> <span><font color="#009900">void</font></span> <span>'''<span><font color="#000000">value_object_init</font></span>'''</span><span><font color="#990000">(</font></span>ValueObject<span><font color="#990000"><nowiki>*</nowiki></font></span> obj<span><font color="#990000">)</font></span> <span><font color="#FF0000">{</font></span> | ||
+ | <span>'''<span><font color="#000000">dbg</font></span>'''</span><span><font color="#990000">(</font></span><span><font color="#FF0000">"Called"</font></span><span><font color="#990000">);</font></span> | ||
+ | <span>'''<span><font color="#000000">g_assert</font></span>'''</span><span><font color="#990000">(</font></span>obj <span><font color="#990000"><nowiki>!=</nowiki></font></span> NULL<span><font color="#990000">);</font></span> | ||
+ | obj<span><font color="#990000">-></font></span>value1 <span><font color="#990000"><nowiki>=</nowiki></font></span> <span><font color="#993399">0</font></span><span><font color="#990000"><nowiki>;</nowiki></font></span> | ||
+ | obj<span><font color="#990000">-></font></span>value2 <span><font color="#990000"><nowiki>=</nowiki></font></span> <span><font color="#993399">0.0</font></span><span><font color="#990000"><nowiki>;</nowiki></font></span> | ||
+ | <span><font color="#FF0000">}</font></span> | ||
+ | <span>''<span><font color="#9A1900">/**</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> * Per class initializer</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> *</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> * Registers the type into the GLib/D-Bus wrapper so that it may add</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> * its own magic.</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> */</font></span>''</span> | ||
+ | <span>'''<span><font color="#0000FF">static</font></span>'''</span> <span><font color="#009900">void</font></span> <span>'''<span><font color="#000000">value_object_class_init</font></span>'''</span><span><font color="#990000">(</font></span>ValueObjectClass<span><font color="#990000"><nowiki>*</nowiki></font></span> klass<span><font color="#990000">)</font></span> <span><font color="#FF0000">{</font></span> | ||
+ | <span>'''<span><font color="#000000">dbg</font></span>'''</span><span><font color="#990000">(</font></span><span><font color="#FF0000">"Called"</font></span><span><font color="#990000">);</font></span> | ||
+ | <span>'''<span><font color="#000000">g_assert</font></span>'''</span><span><font color="#990000">(</font></span>klass <span><font color="#990000"><nowiki>!=</nowiki></font></span> NULL<span><font color="#990000">);</font></span> | ||
+ | <span>'''<span><font color="#000000">dbg</font></span>'''</span><span><font color="#990000">(</font></span><span><font color="#FF0000">"Binding to GLib/D-Bus"</font></span><span><font color="#990000">);</font></span> | ||
+ | <span>''<span><font color="#9A1900">/* Time to bind this GType into the GLib/D-Bus wrappers.</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> NOTE: This is not yet "publishing" the object on the D-Bus, but</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> because it is only allowed to do this once per class</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> creation, the safest place to put it is in the class</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> initializer.</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> Specifically, this function adds "method introspection</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> data" to the class so that methods can be called over</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> the D-Bus. */</font></span>''</span> | ||
+ | <span>'''<span><font color="#000000">dbus_g_object_type_install_info</font></span>'''</span><span><font color="#990000">(</font></span>VALUE_TYPE_OBJECT<span><font color="#990000">,</font></span> | ||
+ | <span><font color="#990000">&</font></span>dbus_glib_value_object_object_info<span><font color="#990000">);</font></span> | ||
+ | <span>'''<span><font color="#000000">dbg</font></span>'''</span><span><font color="#990000">(</font></span><span><font color="#FF0000">"Done"</font></span><span><font color="#990000">);</font></span> | ||
+ | <span>''<span><font color="#9A1900">/* All done. Class is ready to be used for instantiating objects */</font></span>''</span> | ||
+ | <span><font color="#FF0000">}</font></span> | ||
+ | </tt> | ||
- | The | + | The dbus_g_object_type_install_info takes a pointer to the structure describing the D-Bus integration (dbus_glib_value_object_object_info), which is generated by dbus-bindings-tool. This function creates all the necessary runtime information for the GType, so the details can be left alone. It also attaches the introspection data to the GType, so that D-Bus introspection may return information on the interface that the object implements. |
- | The next functions to be implemented are the get and set functions, which allow us to inspect the interface as well. The names of the functions and their prototypes are ultimately dictated by | + | The next functions to be implemented are the get and set functions, which allow us to inspect the interface as well. The names of the functions and their prototypes are ultimately dictated by dbus-bindings-tool generated stub header files. This means that if the interface XML is sufficiently changed, the code fails to build (because the generated stubs yield different prototypes): glib-dbus-sync/server.c |
- | < | + | <tt><span>''<span><font color="#9A1900">/**</font></span>''</span> |
- | /** | + | <span>''<span><font color="#9A1900"> * Function that gets called when someone tries to execute "setvalue1"</font></span>''</span> |
- | * Function that gets called when someone tries to execute "setvalue1" | + | <span>''<span><font color="#9A1900"> * over the D-Bus. (Actually the marshaling code from the stubs gets</font></span>''</span> |
- | * over the D-Bus. (Actually the marshaling code from the stubs gets | + | <span>''<span><font color="#9A1900"> * executed first, but they eventually execute this function.)</font></span>''</span> |
- | * executed first, but they eventually execute this function.) | + | <span>''<span><font color="#9A1900"> *</font></span>''</span> |
- | * | + | <span>''<span><font color="#9A1900"> * NOTE: If you change the name of this function, the generated</font></span>''</span> |
- | * NOTE: If you change the name of this function, the generated | + | <span>''<span><font color="#9A1900"> * stubs no longer find it! On the other hand, if you</font></span>''</span> |
- | * stubs no longer find it! On the other hand, if you | + | <span>''<span><font color="#9A1900"> * decide to modify the interface XML, this is one of the places</font></span>''</span> |
- | * decide to modify the interface XML, this is one of the places | + | <span>''<span><font color="#9A1900"> * that you have to modify as well.</font></span>''</span> |
- | * that you have to modify as well. | + | <span>''<span><font color="#9A1900"> * This applies to the next four functions (including this one).</font></span>''</span> |
- | * This applies to the next four functions (including this one). | + | <span>''<span><font color="#9A1900"> */</font></span>''</span> |
- | */ | + | gboolean <span>'''<span><font color="#000000">value_object_setvalue1</font></span>'''</span><span><font color="#990000">(</font></span>ValueObject<span><font color="#990000"><nowiki>*</nowiki></font></span> obj<span><font color="#990000">,</font></span> gint valueIn<span><font color="#990000">,</font></span> |
- | gboolean value_object_setvalue1(ValueObject* obj, gint valueIn, | + | GError<span><font color="#990000"><nowiki>**</nowiki></font></span> error<span><font color="#990000">)</font></span> <span><font color="#FF0000">{</font></span> |
- | + | <span>'''<span><font color="#000000">dbg</font></span>'''</span><span><font color="#990000">(</font></span><span><font color="#FF0000">"Called (valueIn=%d)"</font></span><span><font color="#990000">,</font></span> valueIn<span><font color="#990000">);</font></span> | |
- | + | <span>'''<span><font color="#000000">g_assert</font></span>'''</span><span><font color="#990000">(</font></span>obj <span><font color="#990000"><nowiki>!=</nowiki></font></span> NULL<span><font color="#990000">);</font></span> | |
- | + | <span>''<span><font color="#9A1900">/* Change the value. */</font></span>''</span> | |
- | + | obj<span><font color="#990000">-></font></span>value1 <span><font color="#990000"><nowiki>=</nowiki></font></span> valueIn<span><font color="#990000"><nowiki>;</nowiki></font></span> | |
- | + | <span>''<span><font color="#9A1900">/* Return success to GLib/D-Bus wrappers. In this case we do not need</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> to touch the supplied error pointer-pointer. */</font></span>''</span> | |
- | + | <span>'''<span><font color="#0000FF">return</font></span>'''</span> TRUE<span><font color="#990000"><nowiki>;</nowiki></font></span> | |
- | + | <span><font color="#FF0000">}</font></span> | |
- | } | + | <span>''<span><font color="#9A1900">/**</font></span>''</span> |
- | /** | + | <span>''<span><font color="#9A1900"> * Function that gets executed on "setvalue2".</font></span>''</span> |
- | * Function that gets executed on "setvalue2". | + | <span>''<span><font color="#9A1900"> * Other than this function operating with different type input</font></span>''</span> |
- | * Other than this function operating with different type input | + | <span>''<span><font color="#9A1900"> * parameter (and different internal value), all the comments from</font></span>''</span> |
- | * parameter (and different internal value), all the comments from | + | <span>''<span><font color="#9A1900"> * set_value1 apply here as well.</font></span>''</span> |
- | * set_value1 apply here as well. | + | <span>''<span><font color="#9A1900"> */</font></span>''</span> |
- | */ | + | gboolean <span>'''<span><font color="#000000">value_object_setvalue2</font></span>'''</span><span><font color="#990000">(</font></span>ValueObject<span><font color="#990000"><nowiki>*</nowiki></font></span> obj<span><font color="#990000">,</font></span> gdouble valueIn<span><font color="#990000">,</font></span> |
- | gboolean value_object_setvalue2(ValueObject* obj, gdouble valueIn, | + | GError<span><font color="#990000"><nowiki>**</nowiki></font></span> error<span><font color="#990000">)</font></span> <span><font color="#FF0000">{</font></span> |
- | + | <span>'''<span><font color="#000000">dbg</font></span>'''</span><span><font color="#990000">(</font></span><span><font color="#FF0000">"Called (valueIn=%.3f)"</font></span><span><font color="#990000">,</font></span> valueIn<span><font color="#990000">);</font></span> | |
- | + | <span>'''<span><font color="#000000">g_assert</font></span>'''</span><span><font color="#990000">(</font></span>obj <span><font color="#990000"><nowiki>!=</nowiki></font></span> NULL<span><font color="#990000">);</font></span> | |
- | + | obj<span><font color="#990000">-></font></span>value2 <span><font color="#990000"><nowiki>=</nowiki></font></span> valueIn<span><font color="#990000"><nowiki>;</nowiki></font></span> | |
- | + | <span>'''<span><font color="#0000FF">return</font></span>'''</span> TRUE<span><font color="#990000"><nowiki>;</nowiki></font></span> | |
- | + | <span><font color="#FF0000">}</font></span> | |
- | } | + | <span>''<span><font color="#9A1900">/**</font></span>''</span> |
- | /** | + | <span>''<span><font color="#9A1900"> * Function that gets executed on "getvalue1".</font></span>''</span> |
- | * Function that gets executed on "getvalue1". | + | <span>''<span><font color="#9A1900"> */</font></span>''</span> |
- | */ | + | gboolean <span>'''<span><font color="#000000">value_object_getvalue1</font></span>'''</span><span><font color="#990000">(</font></span>ValueObject<span><font color="#990000"><nowiki>*</nowiki></font></span> obj<span><font color="#990000">,</font></span> gint<span><font color="#990000"><nowiki>*</nowiki></font></span> valueOut<span><font color="#990000">,</font></span> |
- | gboolean value_object_getvalue1(ValueObject* obj, gint* valueOut, | + | GError<span><font color="#990000"><nowiki>**</nowiki></font></span> error<span><font color="#990000">)</font></span> <span><font color="#FF0000">{</font></span> |
- | + | <span>'''<span><font color="#000000">dbg</font></span>'''</span><span><font color="#990000">(</font></span><span><font color="#FF0000">"Called (internal value1 is %d)"</font></span><span><font color="#990000">,</font></span> obj<span><font color="#990000">-></font></span>value1<span><font color="#990000">);</font></span> | |
- | + | <span>'''<span><font color="#000000">g_assert</font></span>'''</span><span><font color="#990000">(</font></span>obj <span><font color="#990000"><nowiki>!=</nowiki></font></span> NULL<span><font color="#990000">);</font></span> | |
- | + | <span>''<span><font color="#9A1900">/* Check that the target pointer is not NULL.</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> Even if the only caller for this is the GLib-wrapper code,</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> we cannot trust the stub generated code and should handle the</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> situation. We terminate with an error in this case.</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> Another option is to create a new GError, and store</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> the error condition there. */</font></span>''</span> | |
- | + | <span>'''<span><font color="#000000">g_assert</font></span>'''</span><span><font color="#990000">(</font></span>valueOut <span><font color="#990000"><nowiki>!=</nowiki></font></span> NULL<span><font color="#990000">);</font></span> | |
- | + | <span>''<span><font color="#9A1900">/* Copy the current first value to caller specified memory. */</font></span>''</span> | |
- | + | <span><font color="#990000"><nowiki>*</nowiki></font></span>valueOut <span><font color="#990000"><nowiki>=</nowiki></font></span> obj<span><font color="#990000">-></font></span>value1<span><font color="#990000"><nowiki>;</nowiki></font></span> | |
- | + | <span>''<span><font color="#9A1900">/* Return success. */</font></span>''</span> | |
- | + | <span>'''<span><font color="#0000FF">return</font></span>'''</span> TRUE<span><font color="#990000"><nowiki>;</nowiki></font></span> | |
- | + | <span><font color="#FF0000">}</font></span> | |
- | } | + | <span>''<span><font color="#9A1900">/**</font></span>''</span> |
- | /** | + | <span>''<span><font color="#9A1900"> * Function that gets executed on "getvalue2".</font></span>''</span> |
- | * Function that gets executed on "getvalue2". | + | <span>''<span><font color="#9A1900"> * (Again, similar to "getvalue1").</font></span>''</span> |
- | * (Again, similar to "getvalue1"). | + | <span>''<span><font color="#9A1900"> */</font></span>''</span> |
- | */ | + | gboolean <span>'''<span><font color="#000000">value_object_getvalue2</font></span>'''</span><span><font color="#990000">(</font></span>ValueObject<span><font color="#990000"><nowiki>*</nowiki></font></span> obj<span><font color="#990000">,</font></span> gdouble<span><font color="#990000"><nowiki>*</nowiki></font></span> valueOut<span><font color="#990000">,</font></span> |
- | gboolean value_object_getvalue2(ValueObject* obj, gdouble* valueOut, | + | GError<span><font color="#990000"><nowiki>**</nowiki></font></span> error<span><font color="#990000">)</font></span> <span><font color="#FF0000">{</font></span> |
- | + | <span>'''<span><font color="#000000">dbg</font></span>'''</span><span><font color="#990000">(</font></span><span><font color="#FF0000">"Called (internal value2 is %.3f)"</font></span><span><font color="#990000">,</font></span> obj<span><font color="#990000">-></font></span>value2<span><font color="#990000">);</font></span> | |
- | + | <span>'''<span><font color="#000000">g_assert</font></span>'''</span><span><font color="#990000">(</font></span>obj <span><font color="#990000"><nowiki>!=</nowiki></font></span> NULL<span><font color="#990000">);</font></span> | |
- | + | <span>'''<span><font color="#000000">g_assert</font></span>'''</span><span><font color="#990000">(</font></span>valueOut <span><font color="#990000"><nowiki>!=</nowiki></font></span> NULL<span><font color="#990000">);</font></span> | |
- | + | <span><font color="#990000"><nowiki>*</nowiki></font></span>valueOut <span><font color="#990000"><nowiki>=</nowiki></font></span> obj<span><font color="#990000">-></font></span>value2<span><font color="#990000"><nowiki>;</nowiki></font></span> | |
- | + | <span>'''<span><font color="#0000FF">return</font></span>'''</span> TRUE<span><font color="#990000"><nowiki>;</nowiki></font></span> | |
- | + | <span><font color="#FF0000">}</font></span> | |
- | } | + | </tt> |
- | </ | + | |
- | The GLib/D-Bus wrapper logic implements all the necessary parameter conversion from D-Bus into the functions, so it is only necessary to handle the GLib corresponding types ( | + | The GLib/D-Bus wrapper logic implements all the necessary parameter conversion from D-Bus into the functions, so it is only necessary to handle the GLib corresponding types (gint and gdouble). The method implementations always receive an object reference to the object as their first parameter, and a pointer to a place where to store new GError objects if the method decides that an error should be created. This error is then be propagated back to the caller of the D-Bus method. This simple get/set example never sets errors, so the last parameter can be ignored here. |
- | + | Returning the values is not performed via the conventional C way (by using return someVal), but instead the return values are written using the given pointers. The return value of the method is always a gboolean, signifying either success or failure. If failure (FALSE) is returned, it is also necessary to create and setup a GError object, and store its address to the error location. | |
== Publishing GType on D-Bus == | == Publishing GType on D-Bus == | ||
Line 409: | Line 452: | ||
Once the implementation is complete, an instance of the class must be published onto the D-Bus. This is done inside the main of the server example and involves performing a D-Bus method call on the bus. | Once the implementation is complete, an instance of the class must be published onto the D-Bus. This is done inside the main of the server example and involves performing a D-Bus method call on the bus. | ||
- | To ensure that the server and the client do not have to be changed, if the object or well-known names are changed later, the server and the client are put into a common header file that is used by both: | + | To ensure that the server and the client do not have to be changed, if the object or well-known names are changed later, the server and the client are put into a common header file that is used by both: glib-dbus-sync/common-defs.h |
- | < | + | <tt><span>''<span><font color="#9A1900">/* Well-known name for this service. */</font></span>''</span> |
- | /* Well-known name for this service. */ | + | <span>'''<span><font color="#000080"><nowiki>#define</nowiki></font></span>'''</span> VALUE_SERVICE_NAME <span><font color="#FF0000">"org.maemo.Platdev_ex"</font></span> |
- | #define VALUE_SERVICE_NAME "org.maemo.Platdev_ex" | + | <span>''<span><font color="#9A1900">/* Object path to the provided object. */</font></span>''</span> |
- | /* Object path to the provided object. */ | + | <span>'''<span><font color="#000080"><nowiki>#define</nowiki></font></span>'''</span> VALUE_SERVICE_OBJECT_PATH <span><font color="#FF0000">"/GlobalValue"</font></span> |
- | #define VALUE_SERVICE_OBJECT_PATH "/GlobalValue" | + | <span>''<span><font color="#9A1900">/* And we are interested in using it through this interface.</font></span>''</span> |
- | /* And we are interested in using it through this interface. | + | <span>''<span><font color="#9A1900"> This must match the entry in the interface definition XML. */</font></span>''</span> |
- | + | <span>'''<span><font color="#000080"><nowiki>#define</nowiki></font></span>'''</span> VALUE_SERVICE_INTERFACE <span><font color="#FF0000">"org.maemo.Value"</font></span></tt> | |
- | #define VALUE_SERVICE_INTERFACE "org.maemo.Value" | + | |
- | </ | + | |
- | The decision to use | + | The decision to use /GlobalValue as the object path is based on clarity only. Most of the time something like /org/maemo/Value is used instead. |
- | Before using any of the | + | Before using any of the GType functions, the runtime system must be initialized by calling g_type_init. This creates the built-in types and sets up all the machinery necessary for creating custom types. When using GTK+, the function is called automatically if GTK+ is initialized. Because this example only uses GLib, the function must be called manually. |
- | After initializing the | + | After initializing the GType system, the next step is to open a connection to the session bus, which is used for the remainder of the publishing sequence: glib-dbus-sync/server.c |
- | < | + | <tt><span>''<span><font color="#9A1900">/* Pull symbolic constants that are shared (in this example) between</font></span>''</span> |
- | /* Pull symbolic constants that are shared (in this example) between | + | <span>''<span><font color="#9A1900"> the client and the server. */</font></span>''</span> |
- | + | <span>'''<span><font color="#000080"><nowiki>#include</nowiki></font></span>'''</span> <span><font color="#FF0000">"common-defs.h"</font></span> | |
- | #include "common-defs.h" | + | |
+ | <span>''<span><font color="#9A1900">/*... Listing cut for brevity ...*/</font></span>''</span> | ||
+ | |||
+ | <span><font color="#009900">int</font></span> <span>'''<span><font color="#000000">main</font></span>'''</span><span><font color="#990000">(</font></span><span><font color="#009900">int</font></span> argc<span><font color="#990000">,</font></span> <span><font color="#009900">char</font></span><span><font color="#990000"><nowiki>**</nowiki></font></span> argv<span><font color="#990000">)</font></span> <span><font color="#FF0000">{</font></span> | ||
+ | |||
+ | <span>''<span><font color="#9A1900">/*... Listing cut for brevity ...*/</font></span>''</span> | ||
+ | |||
+ | <span>''<span><font color="#9A1900">/* Initialize the GType/GObject system. */</font></span>''</span> | ||
+ | <span>'''<span><font color="#000000">g_type_init</font></span>'''</span><span><font color="#990000">();</font></span> | ||
+ | |||
+ | <span>''<span><font color="#9A1900">/*... Listing cut for brevity ...*/</font></span>''</span> | ||
+ | |||
+ | <span>'''<span><font color="#000000">g_print</font></span>'''</span><span><font color="#990000">(</font></span>PROGNAME <span><font color="#FF0000">":main Connecting to the Session D-Bus.</font></span><span><font color="#CC33CC">\n</font></span><span><font color="#FF0000">"</font></span><span><font color="#990000">);</font></span> | ||
+ | bus <span><font color="#990000"><nowiki>=</nowiki></font></span> <span>'''<span><font color="#000000">dbus_g_bus_get</font></span>'''</span><span><font color="#990000">(</font></span>DBUS_BUS_SESSION<span><font color="#990000">,</font></span> <span><font color="#990000">&</font></span>error<span><font color="#990000">);</font></span> | ||
+ | <span>'''<span><font color="#0000FF">if</font></span>'''</span> <span><font color="#990000">(</font></span>error <span><font color="#990000"><nowiki>!=</nowiki></font></span> NULL<span><font color="#990000">)</font></span> <span><font color="#FF0000">{</font></span> | ||
+ | <span>''<span><font color="#9A1900">/* Print error and terminate. */</font></span>''</span> | ||
+ | <span>'''<span><font color="#000000">handleError</font></span>'''</span><span><font color="#990000">(</font></span><span><font color="#FF0000">"Could not connect to session bus"</font></span><span><font color="#990000">,</font></span> error<span><font color="#990000">-></font></span>message<span><font color="#990000">,</font></span> TRUE<span><font color="#990000">);</font></span> | ||
+ | <span><font color="#FF0000">}</font></span> | ||
+ | </tt> | ||
- | + | In order for prospective clients to find the object on the session bus, the server must be attached to a well-known name. This is done with the RequestName method call on the D-Bus server (over D-Bus). In order to target the server, a GLib/D-Bus proxy object must be created first: glib-dbus-sync/server.c | |
- | + | <tt> <span>'''<span><font color="#000000">g_print</font></span>'''</span><span><font color="#990000">(</font></span>PROGNAME <span><font color="#FF0000">":main Registering the well-known name (%s)</font></span><span><font color="#CC33CC">\n</font></span><span><font color="#FF0000">"</font></span><span><font color="#990000">,</font></span> | |
+ | VALUE_SERVICE_NAME<span><font color="#990000">);</font></span> | ||
+ | |||
+ | <span>''<span><font color="#9A1900">/* In order to register a well-known name, we need to use the</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> "RequestMethod" of the /org/freedesktop/DBus interface. Each</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> bus provides an object that implements this interface.</font></span>''</span> | ||
+ | |||
+ | <span>''<span><font color="#9A1900"> In order to do the call, we need a proxy object first.</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> DBUS_SERVICE_DBUS = "org.freedesktop.DBus"</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> DBUS_PATH_DBUS = "/org/freedesktop/DBus"</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> DBUS_INTERFACE_DBUS = "org.freedesktop.DBus" */</font></span>''</span> | ||
+ | busProxy <span><font color="#990000"><nowiki>=</nowiki></font></span> <span>'''<span><font color="#000000">dbus_g_proxy_new_for_name</font></span>'''</span><span><font color="#990000">(</font></span>bus<span><font color="#990000">,</font></span> | ||
+ | DBUS_SERVICE_DBUS<span><font color="#990000">,</font></span> | ||
+ | DBUS_PATH_DBUS<span><font color="#990000">,</font></span> | ||
+ | DBUS_INTERFACE_DBUS<span><font color="#990000">);</font></span> | ||
+ | <span>'''<span><font color="#0000FF">if</font></span>'''</span> <span><font color="#990000">(</font></span>busProxy <span><font color="#990000"><nowiki>==</nowiki></font></span> NULL<span><font color="#990000">)</font></span> <span><font color="#FF0000">{</font></span> | ||
+ | <span>'''<span><font color="#000000">handleError</font></span>'''</span><span><font color="#990000">(</font></span><span><font color="#FF0000">"Failed to get a proxy for D-Bus"</font></span><span><font color="#990000">,</font></span> | ||
+ | <span><font color="#FF0000">"Unknown(dbus_g_proxy_new_for_name)"</font></span><span><font color="#990000">,</font></span> TRUE<span><font color="#990000">);</font></span> | ||
+ | <span><font color="#FF0000">}</font></span> | ||
+ | |||
+ | <span>''<span><font color="#9A1900">/* Attempt to register the well-known name.</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> The RPC call requires two parameters:</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> - arg0: (D-Bus STRING): name to request</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> - arg1: (D-Bus UINT32): flags for registration.</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> (please see "org.freedesktop.DBus.RequestName" in</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> </font></span>''</span><span><u><span><font color="#0000FF">http://dbus.freedesktop.org/doc/dbus-specification.html</font></span></u></span><span>''<span><font color="#9A1900">)</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> Returns one uint32 giving the result of the RPC call.</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> We are interested in 1 (we are now the primary owner of the name)</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> or 4 (we were already the owner of the name, however in this</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> application it does not make much sense).</font></span>''</span> | ||
+ | |||
+ | <span>''<span><font color="#9A1900"> The function returns FALSE if it sets the GError. */</font></span>''</span> | ||
+ | <span>'''<span><font color="#0000FF">if</font></span>'''</span> <span><font color="#990000">(!</font></span><span>'''<span><font color="#000000">dbus_g_proxy_call</font></span>'''</span><span><font color="#990000">(</font></span>busProxy<span><font color="#990000">,</font></span> | ||
+ | <span>''<span><font color="#9A1900">/* Method name to call. */</font></span>''</span> | ||
+ | <span><font color="#FF0000">"RequestName"</font></span><span><font color="#990000">,</font></span> | ||
+ | <span>''<span><font color="#9A1900">/* Where to store the GError. */</font></span>''</span> | ||
+ | <span><font color="#990000">&</font></span>error<span><font color="#990000">,</font></span> | ||
+ | <span>''<span><font color="#9A1900">/* Parameter type of argument 0. Note that</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> because we are dealing with GLib/D-Bus</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> wrappers, you need to find a suitable</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> GType instead of using the "native" D-Bus</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> type codes. */</font></span>''</span> | ||
+ | G_TYPE_STRING<span><font color="#990000">,</font></span> | ||
+ | <span>''<span><font color="#9A1900">/* Data of argument 0. In our case, this is</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> the well-known name for our server</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> example ("org.maemo.Platdev_ex"). */</font></span>''</span> | ||
+ | VALUE_SERVICE_NAME<span><font color="#990000">,</font></span> | ||
+ | <span>''<span><font color="#9A1900">/* Parameter type of argument 1. */</font></span>''</span> | ||
+ | G_TYPE_UINT<span><font color="#990000">,</font></span> | ||
+ | <span>''<span><font color="#9A1900">/* Data of argument 0. This is the "flags"</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> argument of the "RequestName" method which</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> can be use to specify what the bus service</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> should do when the name already exists on</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> the bus. We go with defaults. */</font></span>''</span> | ||
+ | <span><font color="#993399">0</font></span><span><font color="#990000">,</font></span> | ||
+ | <span>''<span><font color="#9A1900">/* Input arguments are terminated with a</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> special GType marker. */</font></span>''</span> | ||
+ | G_TYPE_INVALID<span><font color="#990000">,</font></span> | ||
+ | <span>''<span><font color="#9A1900">/* Parameter type of return value 0.</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> For "RequestName" it is UINT32 so we pick</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> the GType that maps into UINT32 in the</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> wrappers. */</font></span>''</span> | ||
+ | G_TYPE_UINT<span><font color="#990000">,</font></span> | ||
+ | <span>''<span><font color="#9A1900">/* Data of return value 0. These are always</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> pointers to the locations where the</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> proxy can copy the results. */</font></span>''</span> | ||
+ | <span><font color="#990000">&</font></span>result<span><font color="#990000">,</font></span> | ||
+ | <span>''<span><font color="#9A1900">/* Terminate list of return values. */</font></span>''</span> | ||
+ | G_TYPE_INVALID<span><font color="#990000">))</font></span> <span><font color="#FF0000">{</font></span> | ||
+ | <span>'''<span><font color="#000000">handleError</font></span>'''</span><span><font color="#990000">(</font></span><span><font color="#FF0000">"D-Bus.RequestName RPC failed"</font></span><span><font color="#990000">,</font></span> error<span><font color="#990000">-></font></span>message<span><font color="#990000">,</font></span> | ||
+ | TRUE<span><font color="#990000">);</font></span> | ||
+ | <span>''<span><font color="#9A1900">/* Note that the whole call failed, not "just" the name</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> registration (we deal with that below). This means that</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> something bad probably has happened and there is not much we can</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> do (hence program termination). */</font></span>''</span> | ||
+ | <span><font color="#FF0000">}</font></span> | ||
+ | <span>''<span><font color="#9A1900">/* Check the result code of the registration RPC. */</font></span>''</span> | ||
+ | <span>'''<span><font color="#000000">g_print</font></span>'''</span><span><font color="#990000">(</font></span>PROGNAME <span><font color="#FF0000">":main RequestName returned %d.</font></span><span><font color="#CC33CC">\n</font></span><span><font color="#FF0000">"</font></span><span><font color="#990000">,</font></span> result<span><font color="#990000">);</font></span> | ||
+ | <span>'''<span><font color="#0000FF">if</font></span>'''</span> <span><font color="#990000">(</font></span>result <span><font color="#990000"><nowiki>!=</nowiki></font></span> <span><font color="#993399">1</font></span><span><font color="#990000">)</font></span> <span><font color="#FF0000">{</font></span> | ||
+ | <span>'''<span><font color="#000000">handleError</font></span>'''</span><span><font color="#990000">(</font></span><span><font color="#FF0000">"Failed to get the primary well-known name."</font></span><span><font color="#990000">,</font></span> | ||
+ | <span><font color="#FF0000">"RequestName result != 1"</font></span><span><font color="#990000">,</font></span> TRUE<span><font color="#990000">);</font></span> | ||
+ | <span>''<span><font color="#9A1900">/* In this case we could also continue instead of terminating.</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> We could retry the RPC later. Or "lurk" on the bus waiting for</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> someone to tell us what to do. If we were publishing</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> multiple services and/or interfaces, it even might make sense</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> to continue with the rest anyway.</font></span>''</span> | ||
+ | |||
+ | <span>''<span><font color="#9A1900"> In our simple program, we terminate. Not much left to do for</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> this poor program if the clients are not able to find the</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> Value object using the well-known name. */</font></span>''</span> | ||
+ | <span><font color="#FF0000">}</font></span> | ||
+ | </tt> | ||
- | + | The dbus_g_proxy_call function is used to do synchronous method calls in GLib/D-Bus wrappers, and in this case, it is used to run the two argument RequestName method call. The method returns one value (and uint32), which encodes the result of the well-known name registration. | |
- | + | You need to be careful with the order and correctness of the parameters to the function call, as it is easy to get something wrong, and the C compiler cannot really check for parameter type validity here. | |
- | + | ||
- | + | After the successful name registration, an instance of the ValueObject is created and published on the D-Bus: glib-dbus-sync/server.c | |
- | + | <tt> <span>'''<span><font color="#000000">g_print</font></span>'''</span><span><font color="#990000">(</font></span>PROGNAME <span><font color="#FF0000">":main Creating one Value object.</font></span><span><font color="#CC33CC">\n</font></span><span><font color="#FF0000">"</font></span><span><font color="#990000">);</font></span> | |
- | + | <span>''<span><font color="#9A1900">/* The NULL at the end means that we have stopped listing the</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> property names and their values that had been used to</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> set the properties to initial values. Our simple Value</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> implementation does not support GObject properties, and also</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> does not inherit anything interesting from GObject directly, so</font></span>''</span> | |
- | </ | + | <span>''<span><font color="#9A1900"> there are no properties to set. For more examples on properties</font></span>''</span> |
- | + | <span>''<span><font color="#9A1900"> see the first GTK+ example programs from the maemo Application</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> Development material.</font></span>''</span> | |
- | + | ||
- | < | + | <span>''<span><font color="#9A1900"> NOTE: You need to keep at least one reference to the published</font></span>''</span> |
- | + | <span>''<span><font color="#9A1900"> object at all times, unless you want it to disappear from</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> the D-Bus (implied by API reference for</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> dbus_g_connection_register_g_object(). */</font></span>''</span> | |
- | + | valueObj <span><font color="#990000"><nowiki>=</nowiki></font></span> <span>'''<span><font color="#000000">g_object_new</font></span>'''</span><span><font color="#990000">(</font></span>VALUE_TYPE_OBJECT<span><font color="#990000">,</font></span> NULL<span><font color="#990000">);</font></span> | |
- | + | <span>'''<span><font color="#0000FF">if</font></span>'''</span> <span><font color="#990000">(</font></span>valueObj <span><font color="#990000"><nowiki>==</nowiki></font></span> NULL<span><font color="#990000">)</font></span> <span><font color="#FF0000">{</font></span> | |
- | + | <span>'''<span><font color="#000000">handleError</font></span>'''</span><span><font color="#990000">(</font></span><span><font color="#FF0000">"Failed to create one Value instance."</font></span><span><font color="#990000">,</font></span> | |
- | + | <span><font color="#FF0000">"Unknown(OOM?)"</font></span><span><font color="#990000">,</font></span> TRUE<span><font color="#990000">);</font></span> | |
- | + | <span><font color="#FF0000">}</font></span> | |
- | + | ||
- | + | <span>'''<span><font color="#000000">g_print</font></span>'''</span><span><font color="#990000">(</font></span>PROGNAME <span><font color="#FF0000">":main Registering it on the D-Bus.</font></span><span><font color="#CC33CC">\n</font></span><span><font color="#FF0000">"</font></span><span><font color="#990000">);</font></span> | |
- | + | <span>''<span><font color="#9A1900">/* The function does not return any status, so can not check for</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> errors here. */</font></span>''</span> | |
- | + | <span>'''<span><font color="#000000">dbus_g_connection_register_g_object</font></span>'''</span><span><font color="#990000">(</font></span>bus<span><font color="#990000">,</font></span> | |
- | + | VALUE_SERVICE_OBJECT_PATH<span><font color="#990000">,</font></span> | |
- | + | <span>'''<span><font color="#000000">G_OBJECT</font></span>'''</span><span><font color="#990000">(</font></span>valueObj<span><font color="#990000">));</font></span> | |
- | + | ||
- | + | <span>'''<span><font color="#000000">g_print</font></span>'''</span><span><font color="#990000">(</font></span>PROGNAME <span><font color="#FF0000">":main Ready to serve requests (daemonizing).</font></span><span><font color="#CC33CC">\n</font></span><span><font color="#FF0000">"</font></span><span><font color="#990000">);</font></span> | |
- | + | ||
- | + | <span>''<span><font color="#9A1900">/*... Listing cut for brevity ...*/</font></span>''</span> | |
- | + | ||
- | + | <span><font color="#FF0000">}</font></span> | |
- | + | </tt> | |
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | </ | + | |
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | < | + | |
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | } | + | |
- | </ | + | |
After this, the main enters into the main loop, and serves client requests coming over the D-Bus, until the server is terminated. | After this, the main enters into the main loop, and serves client requests coming over the D-Bus, until the server is terminated. | ||
Line 592: | Line 630: | ||
All callback registration is performed automatically by the GLib/D-Bus wrappers on object publication, so there is no need to worry about them. | All callback registration is performed automatically by the GLib/D-Bus wrappers on object publication, so there is no need to worry about them. | ||
- | Implementing the dependencies and rules for the server and the generated stub code gives this snippet: | + | Implementing the dependencies and rules for the server and the generated stub code gives this snippet: glib-dbus-sync/Makefile |
- | < | + | <tt>server<span><font color="#990000"><nowiki>:</nowiki></font></span> server<span><font color="#990000">.</font></span>o |
- | + | <span><font color="#009900">$(CC)</font></span> <span><font color="#009900">$^</font></span> -o <span><font color="#009900">$@</font></span> <span><font color="#009900">$(LDFLAGS)</font></span> | |
- | + | ||
+ | <span>''<span><font color="#9A1900"><nowiki># ... Listing cut for brevity ...</nowiki></font></span>''</span> | ||
+ | |||
+ | <span>''<span><font color="#9A1900"><nowiki># The server and client depend on the respective implementation source</nowiki></font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"><nowiki># files, but also on the common interface as well as the generated</nowiki></font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"><nowiki># stub interfaces.</nowiki></font></span>''</span> | ||
+ | server<span><font color="#990000">.</font></span>o<span><font color="#990000"><nowiki>:</nowiki></font></span> server<span><font color="#990000">.</font></span>c common-defs<span><font color="#990000">.</font></span>h value-server-stub<span><font color="#990000">.</font></span>h | ||
+ | <span><font color="#009900">$(CC)</font></span> <span><font color="#009900">$(CFLAGS)</font></span> -DPROGNAME<span><font color="#990000"><nowiki>=</nowiki></font></span>\"<span><font color="#009900">$(</font></span>basename <span><font color="#009900">$@</font></span><span><font color="#990000">)</font></span>\" -c <span><font color="#009900">$<</font></span> -o <span><font color="#009900">$@</font></span></tt> | ||
- | + | When implementing makefiles that separate compilation from linking, passing the target name (automatic variable $@) directly is not possible as the PROGNAME-define (because that expands into server.o, and it would look slightly silly if all the messages were prefixed with the name). Instead, a GNU make function (basename) is used to strip any prefixes and suffixes out of the parameter. This way, the PROGNAME is set to server. | |
- | + | The next step is to build the server and start it: | |
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | <div class="graybox"> | |
+ | [sbox-DIABLO_X86: ~/glib-dbus-sync] > make server | ||
+ | dbus-binding-tool --prefix=value_object --mode=glib-server \ | ||
+ | value-dbus-interface.xml > value-server-stub.h | ||
+ | cc -I/usr/include/dbus-1.0 -I/usr/lib/dbus-1.0/include | ||
+ | -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include -g -Wall | ||
+ | -DG_DISABLE_DEPRECATED -DNO_DAEMON -DPROGNAME=\"server\" | ||
+ | -c server.c -o server.o | ||
+ | cc server.o -o server -ldbus-glib-1 -ldbus-1 -lgobject-2.0 -lglib-2.0 | ||
+ | [sbox-DIABLO_X86: ~/glib-dbus-sync] > run-standalone.sh ./server | ||
+ | server:main Connecting to the Session D-Bus. | ||
+ | server:main Registering the well-known name (org.maemo.Platdev_ex) | ||
+ | server:main RequestName returned 1. | ||
+ | server:main Creating one Value object. | ||
+ | server:value_object_class_init: Called | ||
+ | server:value_object_class_init: Binding to GLib/D-Bus | ||
+ | server:value_object_class_init: Done | ||
+ | server:value_object_init: Called | ||
+ | server:main Registering it on the D-Bus. | ||
+ | server:main Ready to serve requests (daemonizing). | ||
+ | server: Not daemonizing (built with NO_DAEMON-build define) | ||
- | + | </div> | |
+ | |||
+ | After this, dbus-send is used to test out the implementation details from the server. This is done in the same session (for simplicity) by first suspending the server with Ctrl+z, and then continuing running it with the bg shell built-in command. This is done so that it is easier to see the reaction of the server to each dbus-send command. | ||
- | + | The first step here is to test the getvalue1 and setvalue1 methods: | |
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | <div class="graybox"> | |
+ | [Ctrl+z] | ||
+ | [1]+ Stopped run-standalone.sh ./server | ||
+ | [sbox-DIABLO_X86: ~/glib-dbus-sync] > bg | ||
+ | [1]+ run-standalone.sh ./server & | ||
+ | [sbox-DIABLO_X86: ~/glib-dbus-sync] > run-standalone.sh dbus-send \ | ||
+ | --type=method_call --print-reply --dest=org.maemo.Platdev_ex \ | ||
+ | /GlobalValue org.maemo.Value.getvalue1 | ||
+ | server:value_object_getvalue1: Called (internal value1 is 0) | ||
+ | method return sender=:1.15 -> dest=:1.20 | ||
+ | int32 0 | ||
+ | [sbox-DIABLO_X86: ~/glib-dbus-sync] > run-standalone.sh dbus-send \ | ||
+ | --type=method_call --print-reply --dest=org.maemo.Platdev_ex \ | ||
+ | /GlobalValue org.maemo.Value.setvalue1 int32:5 | ||
+ | server:value_object_setvalue1: Called (valueIn=5) | ||
+ | method return sender=:1.15 -> dest=:1.21 | ||
+ | [sbox-DIABLO_X86: ~/glib-dbus-sync] > run-standalone.sh dbus-send \ | ||
+ | --type=method_call --print-reply --dest=org.maemo.Platdev_ex \ | ||
+ | /GlobalValue org.maemo.Value.getvalue1 | ||
+ | server:value_object_getvalue1: Called (internal value1 is 5) | ||
+ | method return sender=:1.15 -> dest=:1.22 | ||
+ | int32 5 | ||
- | + | </div> | |
- | + | The next step is to test the double state variable with getvalue2 and setvalue2 methods: | |
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | <div class="graybox"> | |
+ | [sbox-DIABLO_X86: ~/glib-dbus-sync] > run-standalone.sh dbus-send \ | ||
+ | --type=method_call --print-reply --dest=org.maemo.Platdev_ex \ | ||
+ | /GlobalValue org.maemo.Value.getvalue2 | ||
+ | server:value_object_getvalue2: Called (internal value2 is 0.000) | ||
+ | method return sender=:1.15 -> dest=:1.23 | ||
+ | double 0 | ||
+ | [sbox-DIABLO_X86: ~/glib-dbus-sync] > run-standalone.sh dbus-send \ | ||
+ | --type=method_call --print-reply --dest=org.maemo.Platdev_ex \ | ||
+ | /GlobalValue org.maemo.Value.setvalue2 double:42.0 | ||
+ | server:value_object_setvalue2: Called (valueIn=42.000) | ||
+ | method return sender=:1.15 -> dest=:1.24 | ||
+ | [sbox-DIABLO_X86: ~/glib-dbus-sync] > run-standalone.sh dbus-send \ | ||
+ | --type=method_call --print-reply --dest=org.maemo.Platdev_ex \ | ||
+ | /GlobalValue org.maemo.Value.getvalue2 | ||
+ | server:value_object_getvalue2: Called (internal value2 is 42.000) | ||
+ | method return sender=:1.15 -> dest=:1.25 | ||
+ | double 42 | ||
- | < | + | </div> |
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | This results in a fully functional D-Bus service implementation, | + | This results in a fully functional D-Bus service implementation, albeit a very simple one. |
The next step is to utilize the service from a client. | The next step is to utilize the service from a client. | ||
== Using GLib/D-Bus Wrapper from Client == | == Using GLib/D-Bus Wrapper from Client == | ||
- | |||
By using the generated client stub file, the client that invokes the methods on the Value object can now be written. The D-Bus method calls can also be performed "manually" (either with GLib/D-Bus functions, or even by using libdbus directly, but the latter is discouraged). | By using the generated client stub file, the client that invokes the methods on the Value object can now be written. The D-Bus method calls can also be performed "manually" (either with GLib/D-Bus functions, or even by using libdbus directly, but the latter is discouraged). | ||
- | The | + | The dbus-bindings-tool (when run with the <code>--mode</code><nowiki>=glib-client parameter) generates functions for each of the interface methods, and the functions handle data marshaling operations internally. </nowiki> |
- | Two generated stub functions are presented below, and they are used shortly: | + | Two generated stub functions are presented below, and they are used shortly: glib-dbus-sync/value-client-stub.h |
- | < | + | <tt><span>''<span><font color="#9A1900">/* Generated by dbus-binding-tool; do not edit! */</font></span>''</span> |
- | /* Generated by dbus-binding-tool; do not edit! */ | + | <span>''<span><font color="#9A1900">/*... Listing cut for brevity ...*/</font></span>''</span> |
- | + | <span>'''<span><font color="#0000FF">static</font></span>'''</span> | |
- | static | + | <span>'''<span><font color="#000080"><nowiki>#ifdef</nowiki></font></span>'''</span> G_HAVE_INLINE |
- | #ifdef G_HAVE_INLINE | + | inline |
- | inline | + | <span>'''<span><font color="#000080"><nowiki>#endif</nowiki></font></span>'''</span> |
- | #endif | + | gboolean |
- | gboolean | + | <span>'''<span><font color="#000000">org_maemo_Value_getvalue1</font></span>'''</span> <span><font color="#990000">(</font></span>DBusGProxy <span><font color="#990000"><nowiki>*</nowiki></font></span>proxy<span><font color="#990000">,</font></span> gint<span><font color="#990000"><nowiki>*</nowiki></font></span> OUT_cur_value<span><font color="#990000">,</font></span> |
- | org_maemo_Value_getvalue1 (DBusGProxy *proxy, gint* OUT_cur_value, | + | GError <span><font color="#990000"><nowiki>**</nowiki></font></span>error<span><font color="#990000">)</font></span> |
- | + | <span><font color="#FF0000">{</font></span> | |
- | { | + | <span>'''<span><font color="#0000FF">return</font></span>'''</span> <span>'''<span><font color="#000000">dbus_g_proxy_call</font></span>'''</span> <span><font color="#990000">(</font></span>proxy<span><font color="#990000">,</font></span> <span><font color="#FF0000">"getvalue1"</font></span><span><font color="#990000">,</font></span> error<span><font color="#990000">,</font></span> G_TYPE_INVALID<span><font color="#990000">,</font></span> |
- | + | G_TYPE_INT<span><font color="#990000">,</font></span> OUT_cur_value<span><font color="#990000">,</font></span> G_TYPE_INVALID<span><font color="#990000">);</font></span> | |
- | + | <span><font color="#FF0000">}</font></span> | |
- | } | + | <span>''<span><font color="#9A1900">/*... Listing cut for brevity ...*/</font></span>''</span> |
- | + | <span>'''<span><font color="#0000FF">static</font></span>'''</span> | |
- | static | + | <span>'''<span><font color="#000080"><nowiki>#ifdef</nowiki></font></span>'''</span> G_HAVE_INLINE |
- | #ifdef G_HAVE_INLINE | + | inline |
- | inline | + | <span>'''<span><font color="#000080"><nowiki>#endif</nowiki></font></span>'''</span> |
- | #endif | + | gboolean |
- | gboolean | + | <span>'''<span><font color="#000000">org_maemo_Value_setvalue1</font></span>'''</span> <span><font color="#990000">(</font></span>DBusGProxy <span><font color="#990000"><nowiki>*</nowiki></font></span>proxy<span><font color="#990000">,</font></span> <span>'''<span><font color="#0000FF">const</font></span>'''</span> gint IN_new_value<span><font color="#990000">,</font></span> |
- | org_maemo_Value_setvalue1 (DBusGProxy *proxy, const gint IN_new_value, | + | GError <span><font color="#990000"><nowiki>**</nowiki></font></span>error<span><font color="#990000">)</font></span> |
- | + | <span><font color="#FF0000">{</font></span> | |
- | { | + | <span>'''<span><font color="#0000FF">return</font></span>'''</span> <span>'''<span><font color="#000000">dbus_g_proxy_call</font></span>'''</span> <span><font color="#990000">(</font></span>proxy<span><font color="#990000">,</font></span> <span><font color="#FF0000">"setvalue1"</font></span><span><font color="#990000">,</font></span> error<span><font color="#990000">,</font></span> G_TYPE_INT<span><font color="#990000">,</font></span> |
- | + | IN_new_value<span><font color="#990000">,</font></span> G_TYPE_INVALID<span><font color="#990000">,</font></span> | |
- | + | G_TYPE_INVALID<span><font color="#990000">);</font></span> | |
- | + | <span><font color="#FF0000">}</font></span></tt> | |
- | } | + | |
- | </ | + | |
- | The two functions presented above are both ''blocking'', which means that they wait for the result to arrive over the D-Bus, and only then return to the caller. The generated stub code also includes ''asynchronous'' functions (their names end with | + | The two functions presented above are both '''blocking''', which means that they wait for the result to arrive over the D-Bus, and only then return to the caller. The generated stub code also includes '''asynchronous''' functions (their names end with _async). |
For now, it is important to notice how the prototypes of the functions are named, and what are the parameters that they expect to be passed to them. | For now, it is important to notice how the prototypes of the functions are named, and what are the parameters that they expect to be passed to them. | ||
- | The | + | The org_maemo_Value prefix is taken from the interface XML file, from the name attribute of the interface element. All dots are converted into underscores (because C reserves the dot character for other uses), but otherwise the name is preserved (barring dashes in the name). |
The rest of the function name is the method name for each method defined in the interface XML file. | The rest of the function name is the method name for each method defined in the interface XML file. | ||
- | The first parameter for all the generated stub functions is always a pointer to a | + | The first parameter for all the generated stub functions is always a pointer to a DBusProxy object, which is necessary to use with the GLib/D-Bus wrapper functions. After the proxy, a list of method parameters is passed. The binding tool prefixes the parameter names with either IN_ or OUT_ depending on the "direction" of the parameter. The rest of the parameter name is taken from the name attributed of the arg element for the method, or if not given, is automatically generated as arg0, arg1, etc. Input parameters are passed as values (unless they are complex or strings, in which case they are passed as pointers). Output parameters are always passed as pointers. |
- | The functions always returns a | + | The functions always returns a gboolean, indicating failure or success, and if they fail, they also create and set the error pointer to an GError -object which can then be checked for the reason for the error (unless the caller passed a NULL pointer for error, in which case the error object is not created). glib-dbus-sync/client.c |
- | < | + | <tt><span>'''<span><font color="#000080"><nowiki>#include</nowiki></font></span>'''</span> <span><font color="#FF0000"><glib.h></font></span> |
- | #include <glib.h> | + | <span>'''<span><font color="#000080"><nowiki>#include</nowiki></font></span>'''</span> <span><font color="#FF0000"><dbus/dbus-glib.h></font></span> |
- | #include <dbus/dbus-glib.h> | + | <span>'''<span><font color="#000080"><nowiki>#include</nowiki></font></span>'''</span> <span><font color="#FF0000"><stdlib.h></font></span> <span>''<span><font color="#9A1900">/* exit, EXIT_FAILURE */</font></span>''</span> |
- | #include <stdlib.h> /* exit, EXIT_FAILURE */ | + | <span>'''<span><font color="#000080"><nowiki>#include</nowiki></font></span>'''</span> <span><font color="#FF0000"><string.h></font></span> <span>''<span><font color="#9A1900">/* strcmp */</font></span>''</span> |
- | #include <string.h> /* strcmp */ | + | <span>''<span><font color="#9A1900">/* Pull the common symbolic defines. */</font></span>''</span> |
- | /* Pull the common symbolic defines. */ | + | <span>'''<span><font color="#000080"><nowiki>#include</nowiki></font></span>'''</span> <span><font color="#FF0000">"common-defs.h"</font></span> |
- | #include "common-defs.h" | + | <span>''<span><font color="#9A1900">/* Pull in the client stubs that were generated with</font></span>''</span> |
- | /* Pull in the client stubs that were generated with | + | <span>''<span><font color="#9A1900"> dbus-binding-tool */</font></span>''</span> |
- | + | <span>'''<span><font color="#000080"><nowiki>#include</nowiki></font></span>'''</span> <span><font color="#FF0000">"value-client-stub.h"</font></span></tt> | |
- | #include "value-client-stub.h" | + | |
- | </ | + | |
- | This allows the client code to use the stub code directly as follows: | + | This allows the client code to use the stub code directly as follows: glib-dbus-sync/client.c |
- | < | + | <tt><span>''<span><font color="#9A1900">/**</font></span>''</span> |
- | /** | + | <span>''<span><font color="#9A1900"> * This function is called repeatedly from within the mainloop</font></span>''</span> |
- | * This function is called repeatedly from within the mainloop | + | <span>''<span><font color="#9A1900"> * timer launch code.</font></span>''</span> |
- | * timer launch code. | + | <span>''<span><font color="#9A1900"> *</font></span>''</span> |
- | * | + | <span>''<span><font color="#9A1900"> * The function starts with two statically initialized variables</font></span>''</span> |
- | * The function starts with two statically initialized variables | + | <span>''<span><font color="#9A1900"> * (int and double) which are incremented after each time this</font></span>''</span> |
- | * (int and double) which are incremented after each time this | + | <span>''<span><font color="#9A1900"> * function runs and use the setvalue* remote methods to set the</font></span>''</span> |
- | * function runs and use the setvalue* remote methods to set the | + | <span>''<span><font color="#9A1900"> * new values. If the set methods fail, program is not aborted, but an</font></span>''</span> |
- | * new values. If the set methods fail, program is not aborted, but an | + | <span>''<span><font color="#9A1900"> * message is issued to the user describing the error.</font></span>''</span> |
- | * message is issued to the user describing the error. | + | <span>''<span><font color="#9A1900"> */</font></span>''</span> |
- | */ | + | <span>'''<span><font color="#0000FF">static</font></span>'''</span> gboolean <span>'''<span><font color="#000000">timerCallback</font></span>'''</span><span><font color="#990000">(</font></span>DBusGProxy<span><font color="#990000"><nowiki>*</nowiki></font></span> remoteobj<span><font color="#990000">)</font></span> <span><font color="#FF0000">{</font></span> |
- | static gboolean timerCallback(DBusGProxy* remoteobj) { | + | |
+ | <span>''<span><font color="#9A1900">/* Local values that we'll start updating to the remote object. */</font></span>''</span> | ||
+ | <span>'''<span><font color="#0000FF">static</font></span>'''</span> gint localValue1 <span><font color="#990000"><nowiki>=</nowiki></font></span> <span><font color="#990000">-</font></span><span><font color="#993399">80</font></span><span><font color="#990000"><nowiki>;</nowiki></font></span> | ||
+ | <span>'''<span><font color="#0000FF">static</font></span>'''</span> gdouble localValue2 <span><font color="#990000"><nowiki>=</nowiki></font></span> <span><font color="#990000">-</font></span><span><font color="#993399">120.0</font></span><span><font color="#990000"><nowiki>;</nowiki></font></span> | ||
+ | |||
+ | GError<span><font color="#990000"><nowiki>*</nowiki></font></span> error <span><font color="#990000"><nowiki>=</nowiki></font></span> NULL<span><font color="#990000"><nowiki>;</nowiki></font></span> | ||
+ | |||
+ | <span>''<span><font color="#9A1900">/* Set the first value. */</font></span>''</span> | ||
+ | <span>'''<span><font color="#000000">org_maemo_Value_setvalue1</font></span>'''</span><span><font color="#990000">(</font></span>remoteobj<span><font color="#990000">,</font></span> localValue1<span><font color="#990000">,</font></span> <span><font color="#990000">&</font></span>error<span><font color="#990000">);</font></span> | ||
+ | <span>'''<span><font color="#0000FF">if</font></span>'''</span> <span><font color="#990000">(</font></span>error <span><font color="#990000"><nowiki>!=</nowiki></font></span> NULL<span><font color="#990000">)</font></span> <span><font color="#FF0000">{</font></span> | ||
+ | <span>'''<span><font color="#000000">handleError</font></span>'''</span><span><font color="#990000">(</font></span><span><font color="#FF0000">"Failed to set value1"</font></span><span><font color="#990000">,</font></span> error<span><font color="#990000">-></font></span>message<span><font color="#990000">,</font></span> FALSE<span><font color="#990000">);</font></span> | ||
+ | <span><font color="#FF0000">}</font></span> <span>'''<span><font color="#0000FF">else</font></span>'''</span> <span><font color="#FF0000">{</font></span> | ||
+ | <span>'''<span><font color="#000000">g_print</font></span>'''</span><span><font color="#990000">(</font></span>PROGNAME <span><font color="#FF0000">":timerCallback Set value1 to %d</font></span><span><font color="#CC33CC">\n</font></span><span><font color="#FF0000">"</font></span><span><font color="#990000">,</font></span> localValue1<span><font color="#990000">);</font></span> | ||
+ | <span><font color="#FF0000">}</font></span> | ||
+ | |||
+ | <span>''<span><font color="#9A1900">/* If there was an error with the first, release the error, and</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> don't attempt the second time. Also, don't add to the local</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> values. We assume that errors from the first set are caused by</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> server going off the D-Bus, but are hopeful that it comes</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> back, and hence keep trying (returning TRUE). */</font></span>''</span> | ||
+ | <span>'''<span><font color="#0000FF">if</font></span>'''</span> <span><font color="#990000">(</font></span>error <span><font color="#990000"><nowiki>!=</nowiki></font></span> NULL<span><font color="#990000">)</font></span> <span><font color="#FF0000">{</font></span> | ||
+ | <span>'''<span><font color="#000000">g_clear_error</font></span>'''</span><span><font color="#990000">(&</font></span>error<span><font color="#990000">);</font></span> | ||
+ | <span>'''<span><font color="#0000FF">return</font></span>'''</span> TRUE<span><font color="#990000"><nowiki>;</nowiki></font></span> | ||
+ | <span><font color="#FF0000">}</font></span> | ||
+ | |||
+ | <span>''<span><font color="#9A1900">/* Now try to set the second value as well. */</font></span>''</span> | ||
+ | <span>'''<span><font color="#000000">org_maemo_Value_setvalue2</font></span>'''</span><span><font color="#990000">(</font></span>remoteobj<span><font color="#990000">,</font></span> localValue2<span><font color="#990000">,</font></span> <span><font color="#990000">&</font></span>error<span><font color="#990000">);</font></span> | ||
+ | <span>'''<span><font color="#0000FF">if</font></span>'''</span> <span><font color="#990000">(</font></span>error <span><font color="#990000"><nowiki>!=</nowiki></font></span> NULL<span><font color="#990000">)</font></span> <span><font color="#FF0000">{</font></span> | ||
+ | <span>'''<span><font color="#000000">handleError</font></span>'''</span><span><font color="#990000">(</font></span><span><font color="#FF0000">"Failed to set value2"</font></span><span><font color="#990000">,</font></span> error<span><font color="#990000">-></font></span>message<span><font color="#990000">,</font></span> FALSE<span><font color="#990000">);</font></span> | ||
+ | <span>'''<span><font color="#000000">g_clear_error</font></span>'''</span><span><font color="#990000">(&</font></span>error<span><font color="#990000">);</font></span> <span>''<span><font color="#9A1900">/* Or g_error_free in this case. */</font></span>''</span> | ||
+ | <span><font color="#FF0000">}</font></span> <span>'''<span><font color="#0000FF">else</font></span>'''</span> <span><font color="#FF0000">{</font></span> | ||
+ | <span>'''<span><font color="#000000">g_print</font></span>'''</span><span><font color="#990000">(</font></span>PROGNAME <span><font color="#FF0000">":timerCallback Set value2 to %.3lf</font></span><span><font color="#CC33CC">\n</font></span><span><font color="#FF0000">"</font></span><span><font color="#990000">,</font></span> localValue2<span><font color="#990000">);</font></span> | ||
+ | <span><font color="#FF0000">}</font></span> | ||
+ | |||
+ | <span>''<span><font color="#9A1900">/* Step the local values forward. */</font></span>''</span> | ||
+ | localValue1 <span><font color="#990000">+=</font></span> <span><font color="#993399">10</font></span><span><font color="#990000"><nowiki>;</nowiki></font></span> | ||
+ | localValue2 <span><font color="#990000">+=</font></span> <span><font color="#993399">10.0</font></span><span><font color="#990000"><nowiki>;</nowiki></font></span> | ||
+ | |||
+ | <span>''<span><font color="#9A1900">/* Tell the timer launcher that we want to remain on the timer</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> call list in the future as well. Returning FALSE here stops</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> the launch of this timer callback. */</font></span>''</span> | ||
+ | <span>'''<span><font color="#0000FF">return</font></span>'''</span> TRUE<span><font color="#990000"><nowiki>;</nowiki></font></span> | ||
+ | <span><font color="#FF0000">}</font></span></tt> | ||
- | + | What is left is connecting to the correct D-Bus, creating a GProxy object which are done in the test program: glib-dbus-sync/client.c | |
- | + | ||
- | + | ||
- | + | <tt><span>''<span><font color="#9A1900">/**</font></span>''</span> | |
+ | <span>''<span><font color="#9A1900"> * The test program itself.</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> *</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> * 1) Setup GType/GSignal</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> * 2) Create GMainLoop object</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> * 3) Connect to the Session D-Bus</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> * 4) Create a proxy GObject for the remote Value object</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> * 5) Start a timer that launches timerCallback once per second.</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> * 6) Run main-loop (forever)</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> */</font></span>''</span> | ||
+ | <span><font color="#009900">int</font></span> <span>'''<span><font color="#000000">main</font></span>'''</span><span><font color="#990000">(</font></span><span><font color="#009900">int</font></span> argc<span><font color="#990000">,</font></span> <span><font color="#009900">char</font></span><span><font color="#990000"><nowiki>**</nowiki></font></span> argv<span><font color="#990000">)</font></span> <span><font color="#FF0000">{</font></span> | ||
+ | <span>''<span><font color="#9A1900">/* The D-Bus connection object. Provided by GLib/D-Bus wrappers. */</font></span>''</span> | ||
+ | DBusGConnection<span><font color="#990000"><nowiki>*</nowiki></font></span> bus<span><font color="#990000"><nowiki>;</nowiki></font></span> | ||
+ | <span>''<span><font color="#9A1900">/* This represents the Value object locally (acting as a proxy</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> for all method calls and signal delivery. */</font></span>''</span> | ||
+ | DBusGProxy<span><font color="#990000"><nowiki>*</nowiki></font></span> remoteValue<span><font color="#990000"><nowiki>;</nowiki></font></span> | ||
+ | <span>''<span><font color="#9A1900">/* This refers to the GMainLoop object */</font></span>''</span> | ||
+ | GMainLoop<span><font color="#990000"><nowiki>*</nowiki></font></span> mainloop<span><font color="#990000"><nowiki>;</nowiki></font></span> | ||
+ | GError<span><font color="#990000"><nowiki>*</nowiki></font></span> error <span><font color="#990000"><nowiki>=</nowiki></font></span> NULL<span><font color="#990000"><nowiki>;</nowiki></font></span> | ||
+ | |||
+ | <span>''<span><font color="#9A1900">/* Initialize the GType/GObject system. */</font></span>''</span> | ||
+ | <span>'''<span><font color="#000000">g_type_init</font></span>'''</span><span><font color="#990000">();</font></span> | ||
+ | |||
+ | <span>''<span><font color="#9A1900">/* Create a new GMainLoop with default context (NULL) and initial</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> state of "not running" (FALSE). */</font></span>''</span> | ||
+ | mainloop <span><font color="#990000"><nowiki>=</nowiki></font></span> <span>'''<span><font color="#000000">g_main_loop_new</font></span>'''</span><span><font color="#990000">(</font></span>NULL<span><font color="#990000">,</font></span> FALSE<span><font color="#990000">);</font></span> | ||
+ | <span>''<span><font color="#9A1900">/* Failure to create the main loop is fatal (for us). */</font></span>''</span> | ||
+ | <span>'''<span><font color="#0000FF">if</font></span>'''</span> <span><font color="#990000">(</font></span>mainloop <span><font color="#990000"><nowiki>==</nowiki></font></span> NULL<span><font color="#990000">)</font></span> <span><font color="#FF0000">{</font></span> | ||
+ | <span>'''<span><font color="#000000">handleError</font></span>'''</span><span><font color="#990000">(</font></span><span><font color="#FF0000">"Failed to create the mainloop"</font></span><span><font color="#990000">,</font></span> <span><font color="#FF0000">"Unknown (OOM?)"</font></span><span><font color="#990000">,</font></span> | ||
+ | TRUE<span><font color="#990000">);</font></span> | ||
+ | <span><font color="#FF0000">}</font></span> | ||
+ | |||
+ | <span>'''<span><font color="#000000">g_print</font></span>'''</span><span><font color="#990000">(</font></span>PROGNAME <span><font color="#FF0000">":main Connecting to Session D-Bus.</font></span><span><font color="#CC33CC">\n</font></span><span><font color="#FF0000">"</font></span><span><font color="#990000">);</font></span> | ||
+ | bus <span><font color="#990000"><nowiki>=</nowiki></font></span> <span>'''<span><font color="#000000">dbus_g_bus_get</font></span>'''</span><span><font color="#990000">(</font></span>DBUS_BUS_SESSION<span><font color="#990000">,</font></span> <span><font color="#990000">&</font></span>error<span><font color="#990000">);</font></span> | ||
+ | <span>'''<span><font color="#0000FF">if</font></span>'''</span> <span><font color="#990000">(</font></span>error <span><font color="#990000"><nowiki>!=</nowiki></font></span> NULL<span><font color="#990000">)</font></span> <span><font color="#FF0000">{</font></span> | ||
+ | <span>'''<span><font color="#000000">handleError</font></span>'''</span><span><font color="#990000">(</font></span><span><font color="#FF0000">"Couldn't connect to the Session bus"</font></span><span><font color="#990000">,</font></span> error<span><font color="#990000">-></font></span>message<span><font color="#990000">,</font></span> | ||
+ | TRUE<span><font color="#990000">);</font></span> | ||
+ | <span>''<span><font color="#9A1900">/* Normally you'd have to also g_error_free() the error object</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> but because the program terminates within handleError,</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> it is not necessary here. */</font></span>''</span> | ||
+ | <span><font color="#FF0000">}</font></span> | ||
+ | |||
+ | <span>'''<span><font color="#000000">g_print</font></span>'''</span><span><font color="#990000">(</font></span>PROGNAME <span><font color="#FF0000">":main Creating a GLib proxy object for Value.</font></span><span><font color="#CC33CC">\n</font></span><span><font color="#FF0000">"</font></span><span><font color="#990000">);</font></span> | ||
+ | |||
+ | <span>''<span><font color="#9A1900">/* Create the proxy object that we are using to access the object</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> on the server. If you use dbus_g_proxy_for_name_owner(),</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> you are also notified when the server that implements the</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> object is removed (or rather, the interface is removed). Because</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> we do not care who actually implements the interface, we'll use the</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> more common function. See the API documentation at</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> </font></span>''</span><span><u><span><font color="#0000FF">http://maemo.org/api_refs/4.0/dbus/</font></span></u></span><span>''<span><font color="#9A1900"> for more details. */</font></span>''</span> | ||
+ | remoteValue <span><font color="#990000"><nowiki>=</nowiki></font></span> | ||
+ | <span>'''<span><font color="#000000">dbus_g_proxy_new_for_name</font></span>'''</span><span><font color="#990000">(</font></span>bus<span><font color="#990000">,</font></span> | ||
+ | VALUE_SERVICE_NAME<span><font color="#990000">,</font></span> <span>''<span><font color="#9A1900">/* name */</font></span>''</span> | ||
+ | VALUE_SERVICE_OBJECT_PATH<span><font color="#990000">,</font></span> <span>''<span><font color="#9A1900">/* obj path */</font></span>''</span> | ||
+ | VALUE_SERVICE_INTERFACE <span>''<span><font color="#9A1900">/* interface */</font></span>''</span><span><font color="#990000">);</font></span> | ||
+ | <span>'''<span><font color="#0000FF">if</font></span>'''</span> <span><font color="#990000">(</font></span>remoteValue <span><font color="#990000"><nowiki>==</nowiki></font></span> NULL<span><font color="#990000">)</font></span> <span><font color="#FF0000">{</font></span> | ||
+ | <span>'''<span><font color="#000000">handleError</font></span>'''</span><span><font color="#990000">(</font></span><span><font color="#FF0000">"Couldn't create the proxy object"</font></span><span><font color="#990000">,</font></span> | ||
+ | <span><font color="#FF0000">"Unknown(dbus_g_proxy_new_for_name)"</font></span><span><font color="#990000">,</font></span> TRUE<span><font color="#990000">);</font></span> | ||
+ | <span><font color="#FF0000">}</font></span> | ||
+ | |||
+ | <span>'''<span><font color="#000000">g_print</font></span>'''</span><span><font color="#990000">(</font></span>PROGNAME <span><font color="#FF0000">":main Starting main loop (first timer in 1s).</font></span><span><font color="#CC33CC">\n</font></span><span><font color="#FF0000">"</font></span><span><font color="#990000">);</font></span> | ||
+ | |||
+ | <span>''<span><font color="#9A1900">/* Register a timer callback that does RPC sets on the values.</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> The userdata pointer is used to pass the proxy object to the</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> callback so that it can launch modifications to the object. */</font></span>''</span> | ||
+ | <span>'''<span><font color="#000000">g_timeout_add</font></span>'''</span><span><font color="#990000">(</font></span><span><font color="#993399">1000</font></span><span><font color="#990000">,</font></span> <span><font color="#990000">(</font></span>GSourceFunc<span><font color="#990000">)</font></span>timerCallback<span><font color="#990000">,</font></span> remoteValue<span><font color="#990000">);</font></span> | ||
+ | |||
+ | <span>''<span><font color="#9A1900">/* Run the program. */</font></span>''</span> | ||
+ | <span>'''<span><font color="#000000">g_main_loop_run</font></span>'''</span><span><font color="#990000">(</font></span>mainloop<span><font color="#990000">);</font></span> | ||
+ | <span>''<span><font color="#9A1900">/* Because the main loop is not stopped (by this code), we should not</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> ever get here. The program might abort() for other reasons. */</font></span>''</span> | ||
+ | |||
+ | <span>''<span><font color="#9A1900">/* If it does, return failure as exit code. */</font></span>''</span> | ||
+ | <span>'''<span><font color="#0000FF">return</font></span>'''</span> EXIT_FAILURE<span><font color="#990000"><nowiki>;</nowiki></font></span> | ||
+ | <span><font color="#FF0000">}</font></span></tt> | ||
- | + | Integrating the client into the '''Makefile''' is done the same way as was did for the server before: glib-dbus-sync/Makefile | |
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | <tt>client<span><font color="#990000"><nowiki>:</nowiki></font></span> client<span><font color="#990000">.</font></span>o | |
- | + | <span><font color="#009900">$(CC)</font></span> <span><font color="#009900">$^</font></span> -o <span><font color="#009900">$@</font></span> <span><font color="#009900">$(LDFLAGS)</font></span> | |
- | + | ||
- | + | <span>''<span><font color="#9A1900"><nowiki># ... Listing cut for brevity ...</nowiki></font></span>''</span> | |
- | + | ||
- | + | client<span><font color="#990000">.</font></span>o<span><font color="#990000"><nowiki>:</nowiki></font></span> client<span><font color="#990000">.</font></span>c common-defs<span><font color="#990000">.</font></span>h value-client-stub<span><font color="#990000">.</font></span>h | |
- | + | <span><font color="#009900">$(CC)</font></span> <span><font color="#009900">$(CFLAGS)</font></span> -DPROGNAME<span><font color="#990000"><nowiki>=</nowiki></font></span>\"<span><font color="#009900">$(</font></span>basename <span><font color="#009900">$@</font></span><span><font color="#990000">)</font></span>\" -c <span><font color="#009900">$<</font></span> -o <span><font color="#009900">$@</font></span></tt> | |
- | + | ||
- | + | ||
- | + | After building the client it needs to be started and allowed to execute in the same terminal session where the server is still running: | |
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | / | + | <div class="graybox"> |
- | + | [sbox-DIABLO_X86: ~/glib-dbus-sync] > make client | |
- | + | dbus-binding-tool --prefix=value_object --mode=glib-client \ | |
+ | value-dbus-interface.xml > value-client-stub.h | ||
+ | cc -I/usr/include/dbus-1.0 -I/usr/lib/dbus-1.0/include \ | ||
+ | -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include -g -Wall \ | ||
+ | -DG_DISABLE_DEPRECATED -DNO_DAEMON -DPROGNAME=\"client\" \ | ||
+ | -c client.c -o client.o | ||
+ | cc client.o -o client -ldbus-glib-1 -ldbus-1 -lgobject-2.0 -lglib-2.0 | ||
+ | [sbox-DIABLO_X86: ~/glib-dbus-sync] > run-standalone.sh ./client | ||
+ | client:main Connecting to Session D-Bus. | ||
+ | client:main Creating a GLib proxy object for Value. | ||
+ | client:main Starting main loop (first timer in 1s). | ||
+ | server:value_object_setvalue1: Called (valueIn=-80) | ||
+ | client:timerCallback Set value1 to -80 | ||
+ | server:value_object_setvalue2: Called (valueIn=-120.000) | ||
+ | client:timerCallback Set value2 to -120.000 | ||
+ | server:value_object_setvalue1: Called (valueIn=-70) | ||
+ | client:timerCallback Set value1 to -70 | ||
+ | server:value_object_setvalue2: Called (valueIn=-110.000) | ||
+ | client:timerCallback Set value2 to -110.000 | ||
+ | server:value_object_setvalue1: Called (valueIn=-60) | ||
+ | client:timerCallback Set value1 to -60 | ||
+ | ... | ||
+ | [Ctrl+c] | ||
+ | [sbox-DIABLO_X86: ~/glib-dbus-sync] > fg | ||
+ | run-standalone.sh ./server | ||
+ | [Ctrl+c] | ||
- | + | </div> | |
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | </ | + | |
- | + | Because the client normally runs forever, it is now terminated and the server is moved to the foreground, so that it can also be terminated. This concludes the first GLib/D-Bus example, but for more information about the GLib D-Bus wrappers, please consult the D-BUS GLib Bindings Documentation at [[/node19.html#maemoapi 52]]. | |
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | Because the client normally runs forever, it is now terminated and the server is moved to the foreground, so that it can also be terminated. This concludes the first GLib/D-Bus example, but for more information about the GLib D-Bus wrappers, please consult the | + | |
== D-Bus Introspection == | == D-Bus Introspection == | ||
Line 929: | Line 970: | ||
The main goal of supporting introspection in D-Bus is allowing dynamic bindings to be made with high-level programming languages. This way, the language wrappers for D-Bus can be more intelligent automatically (assuming they utilize the introspection interface). The GLib-wrappers do not use the introspection interface. | The main goal of supporting introspection in D-Bus is allowing dynamic bindings to be made with high-level programming languages. This way, the language wrappers for D-Bus can be more intelligent automatically (assuming they utilize the introspection interface). The GLib-wrappers do not use the introspection interface. | ||
- | Introspection is achieved with three D-Bus methods: | + | Introspection is achieved with three D-Bus methods: ListNames, GetNameOwner and Introspect. The destination object must support the introspection interface in order to provide this information. If the dbus-bindings-tool is used, and the GObject is registered correctly, the service automatically supports introspection. |
For the time being, D-Bus does not come with introspection utilities, but some are available from other sources. One simple program is the "DBus Inspector" that is written in Python, and uses the Python D-Bus bindings and GTK+. If you are planning to write your own tool, you must be prepared to parse XML data, because that is the format of results that the Introspect method returns. | For the time being, D-Bus does not come with introspection utilities, but some are available from other sources. One simple program is the "DBus Inspector" that is written in Python, and uses the Python D-Bus bindings and GTK+. If you are planning to write your own tool, you must be prepared to parse XML data, because that is the format of results that the Introspect method returns. | ||
- | [[Image:dbus-inspector-on-globalvalue-with-names.png|Image dbus-inspector-on-globalvalue-with-names]]Using DBUS Inspector on | + | <div align="CENTER">[[Image:dbus-inspector-on-globalvalue-with-names.png|Image dbus-inspector-on-globalvalue-with-names]]</div><div align="CENTER"><font size="-1">Using DBUS Inspector on GlobalValue on a desktop system. Note that the version of GlobalValue used here also implements signals.</font></div><br> |
Introspection can also be useful when trying to find out what are the different interfaces and methods available for use on a system. However, bear in mind that not all D-Bus services actually implement the introspection interface. Their well-known names can be received, but their interface descriptions come up empty when using Introspect. | Introspection can also be useful when trying to find out what are the different interfaces and methods available for use on a system. However, bear in mind that not all D-Bus services actually implement the introspection interface. Their well-known names can be received, but their interface descriptions come up empty when using Introspect. | ||
- | |||
- | |||
- | |||
- |
Learn more about Contributing to the wiki.