Editing Documentation/Maemo 5 Developer Guide/DBus/Implementing and Using D-Bus Signals
Warning: You are not logged in.
Your IP address will be recorded in this page's edit history.
Warning: This page is 77 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: | ||
- | = | + | =Implementing and using D-Bus signals = |
- | Performing remote method invocations over the D-Bus is only one half of D-Bus capabilities. As was noted before, D-Bus also supports a ''broadcast'' method of communication, which is ''asynchronous''. This mechanism is called a ''signal'' (in D-Bus terminology), and | + | == D-Bus Signal properties == |
+ | Performing remote method invocations over the D-Bus is only one half of D-Bus capabilities. As was noted before, D-Bus also supports a ''broadcast'' method of communication, which is also ''asynchronous''. This mechanism is called a ''signal'' (in D-Bus terminology), and is useful when it is necessary to notify a lot of receivers about a state change that could affect them. Some examples where signals could be useful are notifying a lot of receivers, when the system is being shut down, network connectivity has been lost, and similar system-wide conditions. This way, the receivers do not need to poll for the status continuously. | ||
- | However, signals are not the solution to all problems. If a recipient is not processing its D-Bus messages quickly enough (or there just are too many), a signal might get lost on its way to the recipient. | + | However, signals are not the solution to all problems. If a recipient is not processing its D-Bus messages quickly enough (or there just are too many), a signal might get lost on its way to the recipient. There might also be other complications, as with any RPC mechanism. For these reasons, if the application requires extra reliability, it will be necessary to think how to arrange it. One possibility would be to occasionally check the state that the application is interested in, assuming it can be checked over the D-Bus. It just should not be done too often; the recommended interval is once a minute or less often, and only when the application is already active for other reasons. This model will lead to reduction in battery life, so its use should be carefully weighed. |
- | Signals in D-Bus are able to carry information. Each signal has its own name (specified in the interface XML), as well as "arguments". In signal's case, the argument list is actually a list of information that is passed along the signal and should not be confused with method call parameters (although both are delivered in the same manner). | + | Signals in D-Bus are able to carry information. Each signal has its own name (specified in the interface XML), as well as "arguments". In signal's case, the argument list is actually just a list of information that is passed along the signal, and should not be confused with method call parameters (although both are delivered in the same manner). |
- | Signals do not "return", meaning that when a D-Bus signal is sent, no reply | + | Signals do not "return", meaning that when a D-Bus signal is sent, no reply will be received, nor will one be expected. If the signal emitter wants to be sure that the signal was delivered, additional mechanisms need to be constructed for this (D-Bus does not include them directly). A D-Bus signal is very similar to most datagram-based network protocols, for example UDP. Sending a signal will succeed, even if there are no receivers interested in that specific signal. |
- | Most D-Bus language bindings | + | Most D-Bus language bindings will attempt to map D-Bus signals into something more natural in the target language. Since GLib already supports the notion of signals (as GLib signals), this mapping is quite natural. So in practice, the client will register for GLib signals, and then handle the signals in callback functions (a special wrapper function must be used to register for the wrapped signals: dbus_g_proxy_connect_signal). |
== Declaring Signals in Interface XML == | == Declaring Signals in Interface XML == | ||
- | Next, the | + | Next, the Value object will be extended so that it contains two threshold values (minimum and maximum), and the object will emit signals, whenever a set operation falls outside the thresholds. |
- | A signal | + | A signal will also be emitted, whenever a value is changed (i.e. the binary content of the new value is different from the old one). |
- | In order to make the signals available to introspection data, the interface XML file | + | In order to make the signals available to introspection data, the interface XML file will be modified accordingly: glib-dbus-signals/value-dbus-interface.xml |
- | < | + | <tt><span>'''<span><font color="#0000FF"><node></font></span>'''</span> |
- | + | <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"><!- ... Listing cut for brevity ... -></font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"><!- Signal (D-Bus) definitions -></font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"><!- NOTE: The current version of dbus-bindings-tool doesn't</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> actually enforce the signal arguments _at_all_. Signals need</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> to be declared in order to be passed through the bus itself,</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> but otherwise no checks are done! For example, you could</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> leave the signal arguments unspecified completely, and the</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> code would still work. -></font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"><!- Signals to tell interested clients about state change.</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> We send a string parameter with them. They never can have</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> arguments with direction=in. -></font></span>''</span> | |
- | + | <span>'''<span><font color="#0000FF"><signal</font></span>'''</span> <span><font color="#009900">name</font></span><span><font color="#990000"><nowiki>=</nowiki></font></span><span><font color="#FF0000">"changed_value1"</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">"s"</font></span> <span><font color="#009900">name</font></span><span><font color="#990000"><nowiki>=</nowiki></font></span><span><font color="#FF0000">"change_source_name"</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"></signal></font></span>'''</span> | |
- | + | <span>'''<span><font color="#0000FF"><signal</font></span>'''</span> <span><font color="#009900">name</font></span><span><font color="#990000"><nowiki>=</nowiki></font></span><span><font color="#FF0000">"changed_value2"</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">"s"</font></span> <span><font color="#009900">name</font></span><span><font color="#990000"><nowiki>=</nowiki></font></span><span><font color="#FF0000">"change_source_name"</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"></signal></font></span>'''</span> | |
- | + | <span>''<span><font color="#9A1900"><!- Signals to tell interested clients that values are outside</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> the internally configured range (thresholds). -></font></span>''</span> | |
- | + | <span>'''<span><font color="#0000FF"><signal</font></span>'''</span> <span><font color="#009900">name</font></span><span><font color="#990000"><nowiki>=</nowiki></font></span><span><font color="#FF0000">"outofrange_value1"</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">"s"</font></span> <span><font color="#009900">name</font></span><span><font color="#990000"><nowiki>=</nowiki></font></span><span><font color="#FF0000">"outofrange_source_name"</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"></signal></font></span>'''</span> | |
- | + | <span>'''<span><font color="#0000FF"><signal</font></span>'''</span> <span><font color="#009900">name</font></span><span><font color="#990000"><nowiki>=</nowiki></font></span><span><font color="#FF0000">"outofrange_value2"</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">"s"</font></span> <span><font color="#009900">name</font></span><span><font color="#990000"><nowiki>=</nowiki></font></span><span><font color="#FF0000">"outofrange_source_name"</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"></signal></font></span>'''</span> | |
- | + | <span>'''<span><font color="#0000FF"></interface></font></span>'''</span> | |
- | + | <span>'''<span><font color="#0000FF"></node></font></span>'''</span></tt> | |
- | </node> | + | |
- | </ | + | |
- | The signal definitions are required if the | + | The signal definitions are required, if planning to use the dbus-bindings-tool; however, the argument specification for each signal is not required by the tool. In fact, it will just ignore all argument specifications, and as can be seen below, a lot of "manual coding" has to be made in order to implement and use the signals (on both the client and server side). The dbus-bindings-tool might get more features in the future, but for now, some labor is required. |
== Emitting Signals from GObject == | == Emitting Signals from GObject == | ||
- | + | So that the signal names can easily be changed later, they will be defined in a header file, and the header file will be used in both the server and client. The following is the section with the signal names: glib-dbus-signals/common-defs.h | |
- | < | + | <tt><span>''<span><font color="#9A1900">/* Symbolic constants for the signal names to use with GLib.</font></span>''</span> |
- | /* Symbolic constants for the signal names to use with GLib. | + | <span>''<span><font color="#9A1900"> These need to map into the D-Bus signal names. */</font></span>''</span> |
- | + | <span>'''<span><font color="#000080"><nowiki>#define</nowiki></font></span>'''</span> SIGNAL_CHANGED_VALUE1 <span><font color="#FF0000">"changed_value1"</font></span> | |
- | #define SIGNAL_CHANGED_VALUE1 "changed_value1" | + | <span>'''<span><font color="#000080"><nowiki>#define</nowiki></font></span>'''</span> SIGNAL_CHANGED_VALUE2 <span><font color="#FF0000">"changed_value2"</font></span> |
- | #define SIGNAL_CHANGED_VALUE2 "changed_value2" | + | <span>'''<span><font color="#000080"><nowiki>#define</nowiki></font></span>'''</span> SIGNAL_OUTOFRANGE_VALUE1 <span><font color="#FF0000">"outofrange_value1"</font></span> |
- | #define SIGNAL_OUTOFRANGE_VALUE1 "outofrange_value1" | + | <span>'''<span><font color="#000080"><nowiki>#define</nowiki></font></span>'''</span> SIGNAL_OUTOFRANGE_VALUE2 <span><font color="#FF0000">"outofrange_value2"</font></span></tt> |
- | #define SIGNAL_OUTOFRANGE_VALUE2 "outofrange_value2" | + | |
- | </ | + | |
- | Before a | + | Before a GObject can emit a GLib signal, the signal itself needs to be defined and created. This is best done in the class constructor code (since the signal types need to be created only once): glib-dbus-signals/server.c |
- | < | + | <tt><span>''<span><font color="#9A1900">/**</font></span>''</span> |
- | /** | + | <span>''<span><font color="#9A1900"> * Define enumerations for the different signals that we can generate</font></span>''</span> |
- | * Define enumerations for the different signals that we can generate | + | <span>''<span><font color="#9A1900"> * (so that we can refer to them within the signals-array [below]</font></span>''</span> |
- | * (so that we can refer to them within the signals-array [below] | + | <span>''<span><font color="#9A1900"> * using symbolic names). These are not the same as the signal name</font></span>''</span> |
- | * using symbolic names). These are not the same as the signal name | + | <span>''<span><font color="#9A1900"> * strings.</font></span>''</span> |
- | * strings. | + | <span>''<span><font color="#9A1900"> *</font></span>''</span> |
- | * | + | <span>''<span><font color="#9A1900"> * NOTE: E_SIGNAL_COUNT is NOT a signal enum. We use it as a</font></span>''</span> |
- | * NOTE: E_SIGNAL_COUNT is NOT a signal enum. We use it as a | + | <span>''<span><font color="#9A1900"> * convenient constant giving the number of signals defined so</font></span>''</span> |
- | * convenient constant giving the number of signals defined so | + | <span>''<span><font color="#9A1900"> * far. It needs to be listed last.</font></span>''</span> |
- | * far. It needs to be listed last. | + | <span>''<span><font color="#9A1900"> */</font></span>''</span> |
- | */ | + | <span>'''<span><font color="#0000FF">typedef</font></span>'''</span> <span>'''<span><font color="#0000FF">enum</font></span>'''</span> <span><font color="#FF0000">{</font></span> |
- | typedef enum { | + | E_SIGNAL_CHANGED_VALUE1<span><font color="#990000">,</font></span> |
- | + | E_SIGNAL_CHANGED_VALUE2<span><font color="#990000">,</font></span> | |
- | + | E_SIGNAL_OUTOFRANGE_VALUE1<span><font color="#990000">,</font></span> | |
- | + | E_SIGNAL_OUTOFRANGE_VALUE2<span><font color="#990000">,</font></span> | |
- | + | E_SIGNAL_COUNT | |
- | + | <span><font color="#FF0000">}</font></span> ValueSignalNumber<span><font color="#990000"><nowiki>;</nowiki></font></span> | |
- | } ValueSignalNumber; | + | <span>''<span><font color="#9A1900">/*... Listing cut for brevity ...*/</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> | |
- | typedef struct { | + | <span>''<span><font color="#9A1900">/* The parent class state. */</font></span>''</span> |
- | + | GObjectClass parent<span><font color="#990000"><nowiki>;</nowiki></font></span> | |
- | + | <span>''<span><font color="#9A1900">/* The minimum number under which values will cause signals to be</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> emitted. */</font></span>''</span> | |
- | + | gint thresholdMin<span><font color="#990000"><nowiki>;</nowiki></font></span> | |
- | + | <span>''<span><font color="#9A1900">/* The maximum number over which values will cause signals to be</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> emitted. */</font></span>''</span> | |
- | + | gint thresholdMax<span><font color="#990000"><nowiki>;</nowiki></font></span> | |
- | + | <span>''<span><font color="#9A1900">/* Signals created for this class. */</font></span>''</span> | |
- | + | guint signals<span><font color="#990000">[</font></span>E_SIGNAL_COUNT<span><font color="#990000">];</font></span> | |
- | + | <span><font color="#FF0000">}</font></span> ValueObjectClass<span><font color="#990000"><nowiki>;</nowiki></font></span> | |
- | } ValueObjectClass; | + | <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 class initializer</font></span>''</span> |
- | * Per class initializer | + | <span>''<span><font color="#9A1900"> *</font></span>''</span> |
- | * | + | <span>''<span><font color="#9A1900"> * Sets up the thresholds (-100 .. 100), creates the signals that we</font></span>''</span> |
- | * Sets up the thresholds (-100 .. 100), creates the signals that we | + | <span>''<span><font color="#9A1900"> * can emit from any object of this class and finally registers the</font></span>''</span> |
- | * can emit from any object of this class and finally registers the | + | <span>''<span><font color="#9A1900"> * type into the GLib/D-Bus wrapper so that it may add its own magic.</font></span>''</span> |
- | * type into the GLib/D-Bus wrapper so that it may add its own magic. | + | <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> |
- | static void value_object_class_init(ValueObjectClass* klass) { | + | <span>''<span><font color="#9A1900">/* Since all signals have the same prototype (each will get one</font></span>''</span> |
- | + | <span>''<span><font color="#9A1900"> string as a parameter), we create them in a loop below. The only</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> difference between them is the index into the klass->signals</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> array, and the signal name.</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> Since the index goes from 0 to E_SIGNAL_COUNT-1, we just specify</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> the signal names into an array and iterate over it.</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> Note that the order here must correspond to the order of the</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> enumerations before. */</font></span>''</span> | |
- | + | <span>'''<span><font color="#0000FF">const</font></span>'''</span> gchar<span><font color="#990000"><nowiki>*</nowiki></font></span> signalNames<span><font color="#990000">[</font></span>E_SIGNAL_COUNT<span><font color="#990000">]</font></span> <span><font color="#990000"><nowiki>=</nowiki></font></span> <span><font color="#FF0000">{</font></span> | |
- | + | SIGNAL_CHANGED_VALUE1<span><font color="#990000">,</font></span> | |
- | + | SIGNAL_CHANGED_VALUE2<span><font color="#990000">,</font></span> | |
- | + | SIGNAL_OUTOFRANGE_VALUE1<span><font color="#990000">,</font></span> | |
- | + | SIGNAL_OUTOFRANGE_VALUE2 <span><font color="#FF0000">}</font></span><span><font color="#990000"><nowiki>;</nowiki></font></span> | |
- | + | <span>''<span><font color="#9A1900">/* Loop variable */</font></span>''</span> | |
- | + | <span><font color="#009900">int</font></span> i<span><font color="#990000"><nowiki>;</nowiki></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="#9A1900">/* Setup sane minimums and maximums for the thresholds. There is no</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> way to change these afterwards (currently), so you can consider</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> them as constants. */</font></span>''</span> | |
- | + | klass<span><font color="#990000">-></font></span>thresholdMin <span><font color="#990000"><nowiki>=</nowiki></font></span> <span><font color="#990000">-</font></span><span><font color="#993399">100</font></span><span><font color="#990000"><nowiki>;</nowiki></font></span> | |
- | + | klass<span><font color="#990000">-></font></span>thresholdMax <span><font color="#990000"><nowiki>=</nowiki></font></span> <span><font color="#993399">100</font></span><span><font color="#990000"><nowiki>;</nowiki></font></span> | |
- | + | <span>'''<span><font color="#000000">dbg</font></span>'''</span><span><font color="#990000">(</font></span><span><font color="#FF0000">"Creating signals"</font></span><span><font color="#990000">);</font></span> | |
- | + | <span>''<span><font color="#9A1900">/* Create the signals in one loop, since they all are similar</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> (except for the names). */</font></span>''</span> | |
- | + | <span>'''<span><font color="#0000FF">for</font></span>'''</span> <span><font color="#990000">(</font></span>i <span><font color="#990000"><nowiki>=</nowiki></font></span> <span><font color="#993399">0</font></span><span><font color="#990000"><nowiki>;</nowiki></font></span> i <span><font color="#990000"><</font></span> E_SIGNAL_COUNT<span><font color="#990000"><nowiki>;</nowiki></font></span> i<span><font color="#990000">++)</font></span> <span><font color="#FF0000">{</font></span> | |
- | + | guint signalId<span><font color="#990000"><nowiki>;</nowiki></font></span> | |
- | + | <span>''<span><font color="#9A1900">/* Most of the time you will encounter the following code without</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> comments. This is why all the parameters are documented</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> directly below. */</font></span>''</span> | |
- | + | signalId <span><font color="#990000"><nowiki>=</nowiki></font></span> | |
- | + | <span>'''<span><font color="#000000">g_signal_new</font></span>'''</span><span><font color="#990000">(</font></span>signalNames<span><font color="#990000">[</font></span>i<span><font color="#990000">],</font></span> <span>''<span><font color="#9A1900">/* str name of the signal */</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900">/* GType to which signal is bound to */</font></span>''</span> | |
- | + | <span>'''<span><font color="#000000">G_OBJECT_CLASS_TYPE</font></span>'''</span><span><font color="#990000">(</font></span>klass<span><font color="#990000">),</font></span> | |
- | + | <span>''<span><font color="#9A1900">/* Combination of GSignalFlags which tell the</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> signal dispatch machinery how and when to</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> dispatch this signal. The most common is the</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> G_SIGNAL_RUN_LAST specification. */</font></span>''</span> | |
- | + | G_SIGNAL_RUN_LAST<span><font color="#990000">,</font></span> | |
- | + | <span>''<span><font color="#9A1900">/* Offset into the class structure for the type</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> function pointer. Since we're implementing a</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> simple class/type, we'll leave this at zero. */</font></span>''</span> | |
- | + | <span><font color="#993399">0</font></span><span><font color="#990000">,</font></span> | |
- | + | <span>''<span><font color="#9A1900">/* GSignalAccumulator to use. We don't need one. */</font></span>''</span> | |
- | + | NULL<span><font color="#990000">,</font></span> | |
- | + | <span>''<span><font color="#9A1900">/* User-data to pass to the accumulator. */</font></span>''</span> | |
- | + | NULL<span><font color="#990000">,</font></span> | |
- | + | <span>''<span><font color="#9A1900">/* Function to use to marshal the signal data into</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> the parameters of the signal call. Luckily for</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> us, GLib (GCClosure) already defines just the</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> function that we want for a signal handler that</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> we don't expect any return values (void) and</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> one that will accept one string as parameter</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> (besides the instance pointer and pointer to</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> user-data).</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> If no such function would exist, you would need</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> to create a new one (by using glib-genmarshal</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> tool). */</font></span>''</span> | |
- | + | g_cclosure_marshal_VOID__STRING<span><font color="#990000">,</font></span> | |
- | + | <span>''<span><font color="#9A1900">/* Return GType of the return value. The handler</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> does not return anything, so we use G_TYPE_NONE</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> to mark that. */</font></span>''</span> | |
- | + | G_TYPE_NONE<span><font color="#990000">,</font></span> | |
- | + | <span>''<span><font color="#9A1900">/* Number of parameter GTypes to follow. */</font></span>''</span> | |
- | + | <span><font color="#993399">1</font></span><span><font color="#990000">,</font></span> | |
- | + | <span>''<span><font color="#9A1900">/* GType(s) of the parameters. We only have one. */</font></span>''</span> | |
- | + | G_TYPE_STRING<span><font color="#990000">);</font></span> | |
- | + | <span>''<span><font color="#9A1900">/* Store the signal Id into the class state, so that we can use</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> it later. */</font></span>''</span> | |
- | + | klass<span><font color="#990000">-></font></span>signals<span><font color="#990000">[</font></span>i<span><font color="#990000">]</font></span> <span><font color="#990000"><nowiki>=</nowiki></font></span> signalId<span><font color="#990000"><nowiki>;</nowiki></font></span> | |
- | + | <span>''<span><font color="#9A1900">/* Proceed with the next signal creation. */</font></span>''</span> | |
- | + | <span><font color="#FF0000">}</font></span> | |
- | + | <span>''<span><font color="#9A1900">/* All signals created. */</font></span>''</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">/*... Listing cut for brevity ...*/</font></span>''</span> | |
- | + | <span><font color="#FF0000">}</font></span></tt> | |
- | } | + | |
- | </ | + | |
- | The signal types | + | The signal types will be kept in the class structure, so that they can be referenced easily by the signal emitting utility (covered next). The class constructor code will also set up the threshold limits, which in this implementation will be immutable (i.e. they cannot be changed). It is advisable to experiment with adding more methods to adjust the thresholds as necessary. |
- | Emitting the signals is quite easy, but in order to reduce code amount, a utility function | + | Emitting the signals is then quite easy, but in order to reduce code amount, a utility function will be created to launch a given signal based on its enumeration: glib-dbus-signals/server.c |
- | < | + | <tt><span>''<span><font color="#9A1900">/**</font></span>''</span> |
- | /** | + | <span>''<span><font color="#9A1900"> * Utility helper to emit a signal given with internal enumeration and</font></span>''</span> |
- | * Utility helper to emit a signal given with internal enumeration and | + | <span>''<span><font color="#9A1900"> * the passed string as the signal data.</font></span>''</span> |
- | * the passed string as the signal data. | + | <span>''<span><font color="#9A1900"> *</font></span>''</span> |
- | * | + | <span>''<span><font color="#9A1900"> * Used in the setter functions below.</font></span>''</span> |
- | * Used in the setter functions below. | + | <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_emitSignal</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> |
- | static void value_object_emitSignal(ValueObject* obj, | + | ValueSignalNumber num<span><font color="#990000">,</font></span> |
- | + | <span>'''<span><font color="#0000FF">const</font></span>'''</span> gchar<span><font color="#990000"><nowiki>*</nowiki></font></span> message<span><font color="#990000">)</font></span> <span><font color="#FF0000">{</font></span> | |
- | + | <span>''<span><font color="#9A1900">/* In order to access the signal identifiers, we need to get a hold</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> of the class structure first. */</font></span>''</span> | |
- | + | ValueObjectClass<span><font color="#990000"><nowiki>*</nowiki></font></span> klass <span><font color="#990000"><nowiki>=</nowiki></font></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>''<span><font color="#9A1900">/* Check that the given num is valid (abort if not).</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> Given that this file is the module actually using this utility,</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> you can consider this check superfluous (but useful for</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> development work). */</font></span>''</span> | |
- | + | <span>'''<span><font color="#000000">g_assert</font></span>'''</span><span><font color="#990000">((</font></span>num <span><font color="#990000"><</font></span> E_SIGNAL_COUNT<span><font color="#990000">)</font></span> <span><font color="#990000">&&</font></span> <span><font color="#990000">(</font></span>num <span><font color="#990000">>=</font></span> <span><font color="#993399">0</font></span><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">"Emitting signal id %d, with message '%s'"</font></span><span><font color="#990000">,</font></span> num<span><font color="#990000">,</font></span> message<span><font color="#990000">);</font></span> | |
- | + | <span>''<span><font color="#9A1900">/* This is the simplest way of emitting signals. */</font></span>''</span> | |
- | + | <span>'''<span><font color="#000000">g_signal_emit</font></span>'''</span><span><font color="#990000">(</font></span><span>''<span><font color="#9A1900">/* Instance of the object that is generating this</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> signal. This will be passed as the first parameter</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> to the signal handler (eventually). But obviously</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> when speaking about D-Bus, a signal caught on the</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> other side of D-Bus will be first processed by</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> the GLib-wrappers (the object proxy) and only then</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> processed by the signal handler. */</font></span>''</span> | |
- | + | obj<span><font color="#990000">,</font></span> | |
- | + | <span>''<span><font color="#9A1900">/* Signal id for the signal to generate. These are</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> stored inside the class state structure. */</font></span>''</span> | |
- | + | klass<span><font color="#990000">-></font></span>signals<span><font color="#990000">[</font></span>num<span><font color="#990000">],</font></span> | |
- | + | <span>''<span><font color="#9A1900">/* Detail of signal. Since we are not using detailed</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> signals, we leave this at zero (default). */</font></span>''</span> | |
- | + | <span><font color="#993399">0</font></span><span><font color="#990000">,</font></span> | |
- | + | <span>''<span><font color="#9A1900">/* Data to marshal into the signal. In our case it's</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> just one string. */</font></span>''</span> | |
- | + | message<span><font color="#990000">);</font></span> | |
- | + | <span>''<span><font color="#9A1900">/* g_signal_emit returns void, so we cannot check for success. */</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900">/* Done emitting signal. */</font></span>''</span> | |
- | + | <span><font color="#FF0000">}</font></span></tt> | |
- | } | + | |
- | </ | + | |
- | + | So that it would not be necessary to check the threshold values in multiple places in the source code, that will also be implemented as a separate function. Emitting the "threshold exceeded" signal is still up to the caller. glib-dbus-signals/server.c | |
- | < | + | <tt><span>''<span><font color="#9A1900">/**</font></span>''</span> |
- | /** | + | <span>''<span><font color="#9A1900"> * Utility to check the given integer against the thresholds.</font></span>''</span> |
- | * Utility to check the given integer against the thresholds. | + | <span>''<span><font color="#9A1900"> * Will return TRUE if thresholds are not exceeded, FALSE otherwise.</font></span>''</span> |
- | * | + | <span>''<span><font color="#9A1900"> *</font></span>''</span> |
- | * | + | <span>''<span><font color="#9A1900"> * Used in the setter functions below.</font></span>''</span> |
- | * Used in the setter functions below. | + | <span>''<span><font color="#9A1900"> */</font></span>''</span> |
- | */ | + | <span>'''<span><font color="#0000FF">static</font></span>'''</span> gboolean <span>'''<span><font color="#000000">value_object_thresholdsOk</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> |
- | static gboolean value_object_thresholdsOk(ValueObject* obj, | + | gint value<span><font color="#990000">)</font></span> <span><font color="#FF0000">{</font></span> |
- | + | <span>''<span><font color="#9A1900">/* Thresholds are in class state data, get access to it */</font></span>''</span> | |
- | + | ValueObjectClass<span><font color="#990000"><nowiki>*</nowiki></font></span> klass <span><font color="#990000"><nowiki>=</nowiki></font></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>'''<span><font color="#0000FF">return</font></span>'''</span> <span><font color="#990000">((</font></span>value <span><font color="#990000">>=</font></span> klass<span><font color="#990000">-></font></span>thresholdMin<span><font color="#990000">)</font></span> <span><font color="#990000">&&</font></span> | |
- | + | <span><font color="#990000">(</font></span>value <span><font color="#990000"><=</font></span> klass<span><font color="#990000">-></font></span>thresholdMax<span><font color="#990000">));</font></span> | |
- | + | <span><font color="#FF0000">}</font></span></tt> | |
- | } | + | |
- | </ | + | |
- | Both utility functions are then used from within the respective set functions, one of which is presented below: | + | Both utility functions are then used from within the respective set functions, one of which is presented below: glib-dbus-signals/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 marshalling code from the stubs gets</font></span>''</span> |
- | * over the D-Bus. (Actually the marshalling code from the stubs gets | + | <span>''<span><font color="#9A1900"> * executed first, but they will eventually execute this function.)</font></span>''</span> |
- | * executed first, but | + | <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">/* Compare the current value against old one. If they're the same,</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> we don't need to do anything (except return success). */</font></span>''</span> | |
- | + | <span>'''<span><font color="#0000FF">if</font></span>'''</span> <span><font color="#990000">(</font></span>obj<span><font color="#990000">-></font></span>value1 <span><font color="#990000"><nowiki>!=</nowiki></font></span> valueIn<span><font color="#990000">)</font></span> <span><font color="#FF0000">{</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">/* Emit the "changed_value1" signal. */</font></span>''</span> | |
- | + | <span>'''<span><font color="#000000">value_object_emitSignal</font></span>'''</span><span><font color="#990000">(</font></span>obj<span><font color="#990000">,</font></span> E_SIGNAL_CHANGED_VALUE1<span><font color="#990000">,</font></span> <span><font color="#FF0000">"value1"</font></span><span><font color="#990000">);</font></span> | |
- | + | <span>''<span><font color="#9A1900">/* If new value falls outside the thresholds, emit</font></span>''</span> | |
- | + | <span>''<span><font color="#9A1900"> "outofrange_value1" signal as well. */</font></span>''</span> | |
- | + | <span>'''<span><font color="#0000FF">if</font></span>'''</span> <span><font color="#990000">(!</font></span><span>'''<span><font color="#000000">value_object_thresholdsOk</font></span>'''</span><span><font color="#990000">(</font></span>obj<span><font color="#990000">,</font></span> valueIn<span><font color="#990000">))</font></span> <span><font color="#FF0000">{</font></span> | |
- | + | <span>'''<span><font color="#000000">value_object_emitSignal</font></span>'''</span><span><font color="#990000">(</font></span>obj<span><font color="#990000">,</font></span> E_SIGNAL_OUTOFRANGE_VALUE1<span><font color="#990000">,</font></span> | |
- | + | <span><font color="#FF0000">"value1"</font></span><span><font color="#990000">);</font></span> | |
- | + | <span><font color="#FF0000">}</font></span> | |
- | + | <span><font color="#FF0000">}</font></span> | |
- | + | <span>''<span><font color="#9A1900">/* Return success to GLib/D-Bus wrappers. In this case we don't 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></tt> | |
- | } | + | |
- | </ | + | |
- | The role of the | + | The role of the "value1" string parameter that is sent along both of the signals above might raise a question. Sending the signal origin name with the signal allows one to reuse the same callback function in the client. It is quite rare that this kind of "source naming" would be useful, but it allows for writing a slightly shorter client program. |
- | The implementation of | + | The implementation of setvalue2 is almost identical, but deals with the gdouble parameter. |
The getvalue functions are identical to the versions before as is the Makefile. | The getvalue functions are identical to the versions before as is the Makefile. | ||
- | Next, the server is built and started on the background (in preparation for testing with | + | Next, the server is built and started on the background (in preparation for testing with dbus-send): |
- | < | + | <div class="graybox"> |
- | [sbox-DIABLO_X86: ~/glib-dbus-signals] | + | [sbox-DIABLO_X86: ~/glib-dbus-signals] > make server |
- | 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 | |
- | cc -I/usr/include/dbus-1.0 -I/usr/lib/dbus-1.0/include \ | + | 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 | + | cc server.o -o server -ldbus-glib-1 -ldbus-1 -lgobject-2.0 -lglib-2.0 |
- | [sbox-DIABLO_X86: ~/glib-dbus-signals] | + | [sbox-DIABLO_X86: ~/glib-dbus-signals] > run-standalone.sh ./server & |
- | [1] 15293 | + | [1] 15293 |
- | server:main Connecting to the Session D-Bus. | + | server:main Connecting to the Session D-Bus. |
- | server:main Registering the well-known name (org.maemo.Platdev_ex) | + | server:main Registering the well-known name (org.maemo.Platdev_ex) |
- | server:main RequestName returned 1. | + | server:main RequestName returned 1. |
- | server:main Creating one Value object. | + | server:main Creating one Value object. |
- | server:value_object_class_init: Called | + | server:value_object_class_init: Called |
- | server:value_object_class_init: Creating signals | + | server:value_object_class_init: Creating signals |
- | server:value_object_class_init: Binding to GLib/D-Bus | + | server:value_object_class_init: Binding to GLib/D-Bus |
- | server:value_object_class_init: Done | + | server:value_object_class_init: Done |
- | server:value_object_init: Called | + | server:value_object_init: Called |
- | server:main Registering it on the D-Bus. | + | server:main Registering it on the D-Bus. |
- | server:main Ready to serve requests (daemonizing). | + | server:main Ready to serve requests (daemonizing). |
- | server: Not daemonizing (built with NO_DAEMON-build define) | + | server: Not daemonizing (built with NO_DAEMON-build define) |
- | [sbox-DIABLO_X86: ~/glib-dbus-signals] | + | [sbox-DIABLO_X86: ~/glib-dbus-signals] > |
- | + | ||
- | + | </div> | |
- | + | The next step is to test the setvalue1 method: | |
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | method | + | |
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | <div class="graybox"> | |
+ | [sbox-DIABLO_X86: ~/glib-dbus-signals] > run-standalone.sh dbus-send \ | ||
+ | --type=method_call --print-reply --dest=org.maemo.Platdev_ex \ | ||
+ | /GlobalValue org.maemo.Value.setvalue1 int32:10 | ||
+ | server:value_object_setvalue1: Called (valueIn=10) | ||
+ | server:value_object_emitSignal: Emitting signal id 0, with message 'value1' | ||
+ | method return sender=:1.38 -> dest=:1.41 | ||
+ | [sbox-DIABLO_X86: ~/glib-dbus-signals] > run-standalone.sh dbus-send \ | ||
+ | --type=method_call --print-reply --dest=org.maemo.Platdev_ex \ | ||
+ | /GlobalValue org.maemo.Value.setvalue1 int32:-200 | ||
+ | server:value_object_setvalue1: Called (valueIn=-200) | ||
+ | server:value_object_emitSignal: Emitting signal id 0, with message 'value1' | ||
+ | server:value_object_emitSignal: Emitting signal id 2, with message 'value1' | ||
+ | method return sender=:1.38 -> dest=:1.42 | ||
- | < | + | </div> |
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | And then setvalue2 (with doubles). At this point, something strange might be noticed in the threshold triggering: | |
- | + | <div class="graybox"> | |
+ | [sbox-DIABLO_X86: ~/glib-dbus-signals] > run-standalone.sh dbus-send \ | ||
+ | --type=method_call --print-reply --dest=org.maemo.Platdev_ex \ | ||
+ | /GlobalValue org.maemo.Value.setvalue2 double:100.5 | ||
+ | server:value_object_setvalue2: Called (valueIn=100.500) | ||
+ | server:value_object_emitSignal: Emitting signal id 1, with message 'value2' | ||
+ | method return sender=:1.38 -> dest=:1.44 | ||
+ | [sbox-DIABLO_X86: ~/glib-dbus-signals] > run-standalone.sh dbus-send \ | ||
+ | --type=method_call --print-reply --dest=org.maemo.Platdev_ex \ | ||
+ | /GlobalValue org.maemo.Value.setvalue2 double:101 | ||
+ | server:value_object_setvalue2: Called (valueIn=101.000) | ||
+ | server:value_object_emitSignal: Emitting signal id 1, with message 'value2' | ||
+ | server:value_object_emitSignal: Emitting signal id 3, with message 'value2' | ||
+ | method return sender=:1.38 -> dest=:1.45 | ||
- | + | </div> | |
- | + | Since the threshold testing logic will truncate the gdouble before testing against the (integer) thresholds, a value of 100.5 will be detected as 100, and will still fit within the thresholds. | |
- | + | Instead of printing out the emitted signal names, their enumeration values are printed. This could be rectified with a small enumeration to string table, but it was emitted from the program for simplicity. | |
- | + | It can also be noticed that other than seeing the server messages about emitting the signals, there is not a trace of them being sent or received. This is because dbus-send does not listen for signals. There is a separate tool for tracing signals, and it will be covered at the end of this chapter (dbus-monitor). | |
- | + | == Catching Signals in GLib/D-Bus Clients == | |
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | In order to receive D-Bus signals in the client, one needs to do quite a bit of work per signal. This is because dbus-bindings-tool does not generate any code for signals (at the moment). The aim is to make the GLib wrappers emit GSignals, whenever an interesting D-Bus signal arrives. This also means that it will be necessary to register the interest for a particular D-Bus signal. | |
- | + | When implementing the callbacks for the signals, it is necessary to take care to implement the prototype correctly. Since the signals will be sent with one attached string value, the callbacks will receive at least the string parameter. Besides the signal attached arguments, the callback will receive the proxy object, through which the signal was received, and optional user-specified data (which will not be used in this example, so it will be always NULL). glib-dbus-signals/client.c | |
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | <tt><span>''<span><font color="#9A1900">/**</font></span>''</span> | |
+ | <span>''<span><font color="#9A1900"> * Signal handler for the "changed" signals. These will be sent by the</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> * Value-object whenever its contents change (whether within</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> * thresholds or not).</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> *</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> * Like before, we use strcmp to differentiate between the two</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> * values, and act accordingly (in this case by retrieving</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> * synchronously the values using getvalue1 or getvalue2.</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> *</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> * NOTE: Since we use synchronous getvalues, it is possible to get</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> * this code stuck if for some reason the server would be stuck</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> * in an eternal loop.</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">valueChangedSignalHandler</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> <span><font color="#009900">char</font></span><span><font color="#990000"><nowiki>*</nowiki></font></span> valueName<span><font color="#990000">,</font></span> | ||
+ | gpointer userData<span><font color="#990000">)</font></span> <span><font color="#FF0000">{</font></span> | ||
+ | <span>''<span><font color="#9A1900">/* Since method calls over D-Bus can fail, we'll need to check</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> for failures. The server might be shut down in the middle of</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> things, or might act badly in other ways. */</font></span>''</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="#000000">g_print</font></span>'''</span><span><font color="#990000">(</font></span>PROGNAME <span><font color="#FF0000">":value-changed (%s)</font></span><span><font color="#CC33CC">\n</font></span><span><font color="#FF0000">"</font></span><span><font color="#990000">,</font></span> valueName<span><font color="#990000">);</font></span> | ||
+ | |||
+ | <span>''<span><font color="#9A1900">/* Find out which value changed, and act accordingly. */</font></span>''</span> | ||
+ | <span>'''<span><font color="#0000FF">if</font></span>'''</span> <span><font color="#990000">(</font></span><span>'''<span><font color="#000000">strcmp</font></span>'''</span><span><font color="#990000">(</font></span>valueName<span><font color="#990000">,</font></span> <span><font color="#FF0000">"value1"</font></span><span><font color="#990000">)</font></span> <span><font color="#990000"><nowiki>==</nowiki></font></span> <span><font color="#993399">0</font></span><span><font color="#990000">)</font></span> <span><font color="#FF0000">{</font></span> | ||
+ | gint v <span><font color="#990000"><nowiki>=</nowiki></font></span> <span><font color="#993399">0</font></span><span><font color="#990000"><nowiki>;</nowiki></font></span> | ||
+ | <span>''<span><font color="#9A1900">/* Execute the RPC to get value1. */</font></span>''</span> | ||
+ | <span>'''<span><font color="#000000">org_maemo_Value_getvalue1</font></span>'''</span><span><font color="#990000">(</font></span>proxy<span><font color="#990000">,</font></span> <span><font color="#990000">&</font></span>v<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">g_print</font></span>'''</span><span><font color="#990000">(</font></span>PROGNAME <span><font color="#FF0000">":value-changed Value1 now %d</font></span><span><font color="#CC33CC">\n</font></span><span><font color="#FF0000">"</font></span><span><font color="#990000">,</font></span> v<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="#9A1900">/* You could interrogate the GError further, to find out exactly</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> what the error was, but in our case, we'll just ignore the</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> error with the hope that some day (preferably soon), the</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> RPC will succeed again (server comes back on the bus). */</font></span>''</span> | ||
+ | <span>'''<span><font color="#000000">handleError</font></span>'''</span><span><font color="#990000">(</font></span><span><font color="#FF0000">"Failed to retrieve 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><font color="#FF0000">}</font></span> <span>'''<span><font color="#0000FF">else</font></span>'''</span> <span><font color="#FF0000">{</font></span> | ||
+ | gdouble v <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>'''<span><font color="#000000">org_maemo_Value_getvalue2</font></span>'''</span><span><font color="#990000">(</font></span>proxy<span><font color="#990000">,</font></span> <span><font color="#990000">&</font></span>v<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">g_print</font></span>'''</span><span><font color="#990000">(</font></span>PROGNAME <span><font color="#FF0000">":value-changed Value2 now %.3f</font></span><span><font color="#CC33CC">\n</font></span><span><font color="#FF0000">"</font></span><span><font color="#990000">,</font></span> v<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">handleError</font></span>'''</span><span><font color="#990000">(</font></span><span><font color="#FF0000">"Failed to retrieve 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><font color="#FF0000">}</font></span> | ||
+ | <span><font color="#FF0000">}</font></span> | ||
+ | <span>''<span><font color="#9A1900">/* Free up error object if one was allocated. */</font></span>''</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><font color="#FF0000">}</font></span></tt> | ||
- | + | The callback will first determine, what was the source value that caused the signal to be generated. For this, it uses the string argument of the signal. It will then retrieve the current value using the respective RPC methods (getvalue1 or getvalue2), and print out the value. | |
- | + | If any errors occur during the method calls, the errors will be printed out, but the program will continue to run. If an error does occur, the GError object will need to be freed (performed with g_clear_error). The program will not be terminated on RPC errors, since the condition might be temporary (the Value object server might be restarted later). | |
- | + | The code for the outOfRangeSignalHandler callback has been omitted, since it does not contain anything beyond what valueChangedSignalHandler demonstrates. | |
- | + | Registering for the signals is a two-step process. First, it is necessary to register the interest in the D-Bus signals, and then install the callbacks for the respective GLib signals. This is done within main: glib-dbus-signals/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) Register signals that we're interested from the Value object</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> * 6) Register signal handlers for them</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> * 7) Start a timer that will launch timerCallback once per second.</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> * 8) 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">/*... Listing cut for brevity ...*/</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="#9A1900">/* Register the signatures for the signal handlers.</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> In our case, we'll have one string parameter passed to use along</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> the signal itself. The parameter list is terminated with</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> G_TYPE_INVALID (i.e., the GType for string objects. */</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 Registering signal handler signatures.</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">/* Add the argument signatures for the signals (needs to be done</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> before connecting the signals). This might go away in the future,</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> when the GLib-bindings will do automatic introspection over the</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> D-Bus, but for now we need the registration phase. */</font></span>''</span> | ||
+ | <span><font color="#FF0000">{</font></span> <span>''<span><font color="#9A1900">/* Create a local scope for variables. */</font></span>''</span> | ||
+ | |||
+ | <span><font color="#009900">int</font></span> i<span><font color="#990000"><nowiki>;</nowiki></font></span> | ||
+ | <span>'''<span><font color="#0000FF">const</font></span>'''</span> gchar<span><font color="#990000"><nowiki>*</nowiki></font></span> signalNames<span><font color="#990000">[]</font></span> <span><font color="#990000"><nowiki>=</nowiki></font></span> <span><font color="#FF0000">{</font></span> SIGNAL_CHANGED_VALUE1<span><font color="#990000">,</font></span> | ||
+ | SIGNAL_CHANGED_VALUE2<span><font color="#990000">,</font></span> | ||
+ | SIGNAL_OUTOFRANGE_VALUE1<span><font color="#990000">,</font></span> | ||
+ | SIGNAL_OUTOFRANGE_VALUE2 <span><font color="#FF0000">}</font></span><span><font color="#990000"><nowiki>;</nowiki></font></span> | ||
+ | <span>''<span><font color="#9A1900">/* Iterate over all the entries in the above array.</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> The upper limit for i might seem strange at first glance,</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> but is quite common idiom to extract the number of elements</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> in a statically allocated arrays in C.</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> NOTE: The idiom will not work with dynamically allocated</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> arrays. (Or rather it will, but the result is probably</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> not what you expect.) */</font></span>''</span> | ||
+ | <span>'''<span><font color="#0000FF">for</font></span>'''</span> <span><font color="#990000">(</font></span>i <span><font color="#990000"><nowiki>=</nowiki></font></span> <span><font color="#993399">0</font></span><span><font color="#990000"><nowiki>;</nowiki></font></span> i <span><font color="#990000"><</font></span> <span>'''<span><font color="#0000FF">sizeof</font></span>'''</span><span><font color="#990000">(</font></span>signalNames<span><font color="#990000">)/</font></span><span>'''<span><font color="#0000FF">sizeof</font></span>'''</span><span><font color="#990000">(</font></span>signalNames<span><font color="#990000">[</font></span><span><font color="#993399">0</font></span><span><font color="#990000">]);</font></span> i<span><font color="#990000">++)</font></span> <span><font color="#FF0000">{</font></span> | ||
+ | <span>''<span><font color="#9A1900">/* Since the function doesn't return anything, we cannot check</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> for errors here. */</font></span>''</span> | ||
+ | <span>'''<span><font color="#000000">dbus_g_proxy_add_signal</font></span>'''</span><span><font color="#990000">(</font></span><span>''<span><font color="#9A1900">/* Proxy to use */</font></span>''</span> | ||
+ | remoteValue<span><font color="#990000">,</font></span> | ||
+ | <span>''<span><font color="#9A1900">/* Signal name */</font></span>''</span> | ||
+ | signalNames<span><font color="#990000">[</font></span>i<span><font color="#990000">],</font></span> | ||
+ | <span>''<span><font color="#9A1900">/* Will receive one string argument */</font></span>''</span> | ||
+ | G_TYPE_STRING<span><font color="#990000">,</font></span> | ||
+ | <span>''<span><font color="#9A1900">/* Termination of the argument list */</font></span>''</span> | ||
+ | G_TYPE_INVALID<span><font color="#990000">);</font></span> | ||
+ | <span><font color="#FF0000">}</font></span> | ||
+ | <span><font color="#FF0000">}</font></span> <span>''<span><font color="#9A1900">/* end of local scope */</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 Registering D-Bus signal handlers.</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">/* We connect each of the following signals one at a time,</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> since we'll be using two different callbacks. */</font></span>''</span> | ||
+ | |||
+ | <span>''<span><font color="#9A1900">/* Again, no return values, cannot hence check for errors. */</font></span>''</span> | ||
+ | <span>'''<span><font color="#000000">dbus_g_proxy_connect_signal</font></span>'''</span><span><font color="#990000">(</font></span><span>''<span><font color="#9A1900">/* Proxy object */</font></span>''</span> | ||
+ | remoteValue<span><font color="#990000">,</font></span> | ||
+ | <span>''<span><font color="#9A1900">/* Signal name */</font></span>''</span> | ||
+ | SIGNAL_CHANGED_VALUE1<span><font color="#990000">,</font></span> | ||
+ | <span>''<span><font color="#9A1900">/* Signal handler to use. Note that the</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> typecast is just to make the compiler</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> happy about the function, since the</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> prototype is not compatible with</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> regular signal handlers. */</font></span>''</span> | ||
+ | <span>'''<span><font color="#000000">G_CALLBACK</font></span>'''</span><span><font color="#990000">(</font></span>valueChangedSignalHandler<span><font color="#990000">),</font></span> | ||
+ | <span>''<span><font color="#9A1900">/* User-data (we don't use any). */</font></span>''</span> | ||
+ | NULL<span><font color="#990000">,</font></span> | ||
+ | <span>''<span><font color="#9A1900">/* GClosureNotify function that is</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> responsible in freeing the passed</font></span>''</span> | ||
+ | <span>''<span><font color="#9A1900"> user-data (we have no data). */</font></span>''</span> | ||
+ | NULL<span><font color="#990000">);</font></span> | ||
+ | |||
+ | <span>'''<span><font color="#000000">dbus_g_proxy_connect_signal</font></span>'''</span><span><font color="#990000">(</font></span>remoteValue<span><font color="#990000">,</font></span> SIGNAL_CHANGED_VALUE2<span><font color="#990000">,</font></span> | ||
+ | <span>'''<span><font color="#000000">G_CALLBACK</font></span>'''</span><span><font color="#990000">(</font></span>valueChangedSignalHandler<span><font color="#990000">),</font></span> | ||
+ | NULL<span><font color="#990000">,</font></span> NULL<span><font color="#990000">);</font></span> | ||
+ | |||
+ | <span>'''<span><font color="#000000">dbus_g_proxy_connect_signal</font></span>'''</span><span><font color="#990000">(</font></span>remoteValue<span><font color="#990000">,</font></span> SIGNAL_OUTOFRANGE_VALUE1<span><font color="#990000">,</font></span> | ||
+ | <span>'''<span><font color="#000000">G_CALLBACK</font></span>'''</span><span><font color="#990000">(</font></span>outOfRangeSignalHandler<span><font color="#990000">),</font></span> | ||
+ | NULL<span><font color="#990000">,</font></span> NULL<span><font color="#990000">);</font></span> | ||
+ | |||
+ | <span>'''<span><font color="#000000">dbus_g_proxy_connect_signal</font></span>'''</span><span><font color="#990000">(</font></span>remoteValue<span><font color="#990000">,</font></span> SIGNAL_OUTOFRANGE_VALUE2<span><font color="#990000">,</font></span> | ||
+ | <span>'''<span><font color="#000000">G_CALLBACK</font></span>'''</span><span><font color="#990000">(</font></span>outOfRangeSignalHandler<span><font color="#990000">),</font></span> | ||
+ | NULL<span><font color="#990000">,</font></span> NULL<span><font color="#990000">);</font></span> | ||
+ | |||
+ | <span>''<span><font color="#9A1900">/* All signals are now registered and we're ready to handle them. */</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 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 will do 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">/* Since the main loop is not stopped (by this code), we shouldn't</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> | ||
- | + | When adding the argument signatures for the signals <br />(with dbus_g_proxy_add_signal), one needs to be very careful with the parameter list. The signal argument types must be exactly the same as are sent from the server (irrespective of the argument specification in the interface XML). This is because the current version of dbus-bindings-tool does not generate any checks to enforce signal arguments based on the interface. In this simple case, only one string is received with each different signal, so this is not a big issue. The implementation for the callback function will need to match the argument specification given to the _add_signal-function, otherwise data layout on the stack will be incorrect, and cause problems. | |
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | Building the client happens in the same manner as before (i.e. make client). Since the server is still (hopefully) running on the background, the client will now be started in the same session: | |
- | + | ||
- | + | ||
- | + | ||
- | + | <div class="graybox"> | |
+ | [sbox-DIABLO_X86: ~/glib-dbus-signals] > run-standalone.sh ./client | ||
+ | client:main Connecting to Session D-Bus. | ||
+ | client:main Creating a GLib proxy object for Value. | ||
+ | client:main Registering signal handler signatures. | ||
+ | client:main Registering D-Bus signal handlers. | ||
+ | client:main Starting main loop (first timer in 1s). | ||
+ | server:value_object_setvalue1: Called (valueIn=-80) | ||
+ | server:value_object_emitSignal: Emitting signal id 0, with message 'value1' | ||
+ | client:timerCallback Set value1 to -80 | ||
+ | server:value_object_setvalue2: Called (valueIn=-120.000) | ||
+ | server:value_object_emitSignal: Emitting signal id 1, with message 'value2' | ||
+ | server:value_object_emitSignal: Emitting signal id 3, with message 'value2' | ||
+ | client:timerCallback Set value2 to -120.000 | ||
+ | client:value-changed (value1) | ||
+ | server:value_object_getvalue1: Called (internal value1 is -80) | ||
+ | client:value-changed Value1 now -80 | ||
+ | client:value-changed (value2) | ||
+ | server:value_object_getvalue2: Called (internal value2 is -120.000) | ||
+ | client:value-changed Value2 now -120.000 | ||
+ | client:out-of-range (value2)! | ||
+ | client:out-of-range Value 2 is outside threshold | ||
+ | server:value_object_setvalue1: Called (valueIn=-70) | ||
+ | server:value_object_emitSignal: Emitting signal id 0, with message 'value1' | ||
+ | client:timerCallback Set value1 to -70 | ||
+ | server:value_object_setvalue2: Called (valueIn=-110.000) | ||
+ | server:value_object_emitSignal: Emitting signal id 1, with message 'value2' | ||
+ | server:value_object_emitSignal: Emitting signal id 3, with message 'value2' | ||
+ | client:timerCallback Set value2 to -110.000 | ||
+ | client:value-changed (value1) | ||
+ | server:value_object_getvalue1: Called (internal value1 is -70) | ||
+ | client:value-changed Value1 now -70 | ||
+ | client:value-changed (value2) | ||
+ | server:value_object_getvalue2: Called (internal value2 is -110.000) | ||
+ | client:value-changed Value2 now -110.000 | ||
+ | ... | ||
- | + | </div> | |
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | The client will start with the timer callback being executed once per second (as before). Each iteration, it will call the setvalue1 and setvalue2 RPC methods with increasing values. The number for value2 is intentionally set below the minimum threshold, so that it will cause an outofrange_value2 signal to be emitted. For each set, the changed_value signals will also be emitted. Whenever the client receives either of the value change signals, it will perform a getvalue RPC method call to retrieve the current value and print it out. | |
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | This will continue until the client is terminated. | |
- | + | ==Tracing D-Bus Signals == | |
- | + | ||
- | + | Sometimes it is useful to see which signals are actually carried on the buses, especially when adding signal handlers for signals that are emitted from undocumented interfaces. The dbus-monitor tool will attach to the D-Bus daemon, and ask it to watch for signals and report them back to the tool, so that it can decode the signals automatically, as they appear on the bus. | |
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | While the server and client are still running, the next step is to start the dbus-monitor (in a separate session this time) to see whether the signals are transmitted correctly. It should be noted that signals will appear on the bus even if there are no clients currently interested in them. In this case, signals are emitted by the server, based on client-issued RPC methods, so if the client is terminated, the signals will cease. | |
- | + | ||
- | + | ||
- | + | ||
- | + | [sbox-DIABLO_X86: ~/glib-dbus-signals] > run-standalone.sh dbus-monitor type='signal' | |
- | + | signal sender=:1.38 -> dest=(null destination) | |
+ | interface=org.maemo.Value; member=changed_value1 | ||
+ | string "value1" | ||
+ | signal sender=:1.38 -> dest=(null destination) | ||
+ | interface=org.maemo.Value; member=changed_value2 | ||
+ | string "value2" | ||
+ | signal sender=:1.38 -> dest=(null destination) | ||
+ | interface=org.maemo.Value; member=outofrange_value2 | ||
+ | string "value2" | ||
+ | signal sender=:1.38 -> dest=(null destination) | ||
+ | interface=org.maemo.Value; member=changed_value1 | ||
+ | string "value1" | ||
+ | signal sender=:1.38 -> dest=(null destination) | ||
+ | interface=org.maemo.Value; member=changed_value2 | ||
+ | string "value2" | ||
+ | signal sender=:1.38 -> dest=(null destination) | ||
+ | interface=org.maemo.Value; member=outofrange_value2 | ||
+ | string "value2" | ||
+ | signal sender=:1.38 -> dest=(null destination) | ||
+ | interface=org.maemo.Value; member=changed_value1 | ||
+ | string "value1" | ||
+ | signal sender=:1.38 -> dest=(null destination) | ||
+ | interface=org.maemo.Value; member=changed_value2 | ||
+ | string "value2" | ||
+ | signal sender=:1.38 -> dest=(null destination) | ||
+ | interface=org.maemo.Value; member=changed_value1 | ||
+ | string "value1" | ||
+ | signal sender=:1.38 -> dest=(null destination) | ||
+ | interface=org.maemo.Value; member=changed_value2 | ||
+ | string "value2" | ||
- | |||
- | |||
- | |||
- | + | The tool will automatically decode the parameters to the best of its ability (the string parameter for the signals above). It does not know the semantic meaning for the different signals, so sometimes it will be necessary to perform some additional testing to decide what they actually mean. This is especially true, when mapping out undocumented interfaces (for which there might not be source code available). | |
- | + | Some examples of displaying signals on the system bus on a device follow: | |
- | + | * A device turning off the backlight after inactivity | |
- | + | ||
- | + | ||
- | + | ||
- | + | Nokia-N810-xx-xx:~# run-standalone.sh dbus-monitor --system | |
- | + | signal sender=:1.3 -> dest=(null destination) path=/com/nokia/mce/signal; | |
- | + | interface=com.nokia.mce.signal; member=display_status_ind | |
- | + | string "dimmed" | |
+ | signal sender=:1.3 -> dest=(null destination) path=/com/nokia/mce/signal; | ||
+ | interface=com.nokia.mce.signal; member=system_inactivity_ind | ||
+ | boolean true | ||
+ | signal sender=:1.3 -> dest=(null destination) path=/com/nokia/mce/signal; | ||
+ | interface=com.nokia.mce.signal; member=save_unsaved_data_ind | ||
+ | signal sender=:1.3 -> dest=(null destination) path=/com/nokia/mce/signal; | ||
+ | interface=com.nokia.mce.signal; member=display_status_ind | ||
+ | string "off" | ||
- | |||
- | |||
- | |||
- | |||
- | + | * A device coming back to life after a screen tap | |
+ | |||
+ | signal sender=:1.3 -> dest=(null destination) path=/com/nokia/mce/signal; | ||
+ | interface=com.nokia.mce.signal; member=system_inactivity_ind | ||
+ | boolean false | ||
+ | signal sender=:1.3 -> dest=(null destination) path=/com/nokia/mce/signal; | ||
+ | interface=com.nokia.mce.signal; member=display_status_ind | ||
+ | string "on" | ||
+ | signal sender=:1.3 -> dest=(null destination) path=/com/nokia/mce/signal; | ||
+ | interface=com.nokia.mce.signal; member=tklock_mode_ind | ||
+ | string "unlocked" | ||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
* A device going into offline mode | * A device going into offline mode | ||
+ | |||
+ | signal sender=:1.0 -> dest=(null destination) | ||
+ | path=/org/freedesktop/Hal/devices/computer_logicaldev_input_0; | ||
+ | interface=org.freedesktop.Hal.Device; member=Condition | ||
+ | string "ButtonPressed" | ||
+ | string "power" | ||
+ | signal sender=:1.3 -> dest=(null destination) path=/com/nokia/mce/signal; | ||
+ | interface=com.nokia.mce.signal; member=sig_device_mode_ind | ||
+ | string "offline" | ||
+ | signal sender=:1.26 -> dest=(null destination) | ||
+ | path=/com/nokia/wlancond/signal; | ||
+ | interface=com.nokia.wlancond.signal; member=disconnected | ||
+ | string "wlan0" | ||
+ | signal sender=:1.28 -> dest=(null destination) path=/com/nokia/icd; | ||
+ | interface=com.nokia.icd; member=status_changed | ||
+ | string "SSID" | ||
+ | string "WLAN_INFRA" | ||
+ | string "DISCONNECTING" | ||
+ | string "com.nokia.icd.error.network_error" | ||
+ | signal sender=:1.22 -> dest=(null destination) path=/org/bluez/hci0; | ||
+ | interface=org.bluez.Adapter; member=ModeChanged | ||
+ | string "off" | ||
+ | signal sender=:1.25 -> dest=(null destination) | ||
+ | path=/com/nokia/btcond/signal; | ||
+ | interface=com.nokia.btcond.signal; member=hci_dev_down | ||
+ | string "hci0" | ||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
* A device going back into normal mode | * A device going back into normal mode | ||
+ | |||
+ | signal sender=:1.0 -> dest=(null destination) | ||
+ | path=/org/freedesktop/Hal/devices/computer_logicaldev_input_0; | ||
+ | interface=org.freedesktop.Hal.Device; member=Condition | ||
+ | string "ButtonPressed" | ||
+ | string "power" | ||
+ | signal sender=:1.3 -> dest=(null destination) path=/com/nokia/mce/signal; | ||
+ | interface=com.nokia.mce.signal; member=sig_device_mode_ind | ||
+ | string "normal" | ||
+ | signal sender=:1.39 -> dest=(null destination) | ||
+ | path=/org/kernel/class/firmware/hci_h4p; | ||
+ | interface=org.kernel.kevent; member=add | ||
+ | signal sender=:1.39 -> dest=(null destination) | ||
+ | path=/org/kernel/class/firmware/hci_h4p; | ||
+ | interface=org.kernel.kevent; member=remove | ||
+ | signal sender=:1.25 -> dest=(null destination) | ||
+ | path=/com/nokia/btcond/signal; | ||
+ | interface=com.nokia.btcond.signal; member=hci_dev_up | ||
+ | string "hci0" | ||
+ | signal sender=:1.22 -> dest=(null destination) path=/org/bluez/hci0; | ||
+ | interface=org.bluez.Adapter; member=ModeChanged | ||
+ | string "connectable" | ||
+ | signal sender=:1.22 -> dest=(null destination) path=/org/bluez/hci0; | ||
+ | interface=org.bluez.Adapter; member=NameChanged | ||
+ | string "Nokia N810" | ||
+ | signal sender=:1.28 -> dest=(null destination) path=/com/nokia/icd; | ||
+ | interface=com.nokia.icd; member=status_changed | ||
+ | string "SSID" | ||
+ | string "WLAN_INFRA" | ||
+ | string "CONNECTING" | ||
+ | string "" | ||
+ | signal sender=:1.26 -> dest=(null destination) | ||
+ | path=/com/nokia/wlancond/signal; | ||
+ | interface=com.nokia.wlancond.signal; member=connected | ||
+ | string "wlan0" | ||
+ | array [ | ||
+ | byte 0 | ||
+ | byte 13 | ||
+ | byte 157 | ||
+ | byte 198 | ||
+ | byte 120 | ||
+ | byte 175 | ||
+ | ] | ||
+ | int32 536870912 | ||
+ | signal sender=:1.100 -> dest=(null destination) path=/com/nokia/eap/signal; | ||
+ | interface=com.nokia.eap.signal; member=auth_status | ||
+ | uint32 4 | ||
+ | signal sender=:1.28 -> dest=(null destination) path=/com/nokia/icd; | ||
+ | interface=com.nokia.icd; member=proxies | ||
+ | uint32 0 | ||
+ | string "" | ||
+ | int32 0 | ||
+ | string "" | ||
+ | int32 0 | ||
+ | string "" | ||
+ | int32 0 | ||
+ | string "" | ||
+ | int32 0 | ||
+ | string "" | ||
+ | int32 0 | ||
+ | array [ ] | ||
+ | string "" | ||
+ | signal sender=:1.28 -> dest=(null destination) path=/com/nokia/icd; | ||
+ | interface=com.nokia.icd; member=status_changed | ||
+ | string "SSID" | ||
+ | string "WLAN_INFRA" | ||
+ | string "CONNECTED" | ||
+ | string "" | ||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | + | It is also possible to send signals from the command line, which is useful when wanting to emulate some feature of a device inside the SDK. This (like RPC method calls) can be performed with the dbus-send tool, and an example of this kind of simulation was given in the LibOSSO section. | |
- | + | ||
- | + |
Learn more about Contributing to the wiki.