Documentation/Maemo 5 Developer Guide/Using Generic Platform Components/Application Preferences-Gconf

= Application Preferences - GConf=

GConf is used by the GNOME desktop environment for storing shared configuration settings for the desktop and applications. The daemon process GConfd follows the changes in the database. When a change occurs in the database, the daemon applies the new settings to the applications using them. For example, the control panel application uses GConf.

If settings are used only by a single application, Glib utility for .ini style files should be used instead. Applications should naturally have working default settings. Settings should be saved only when the user changes them.

This chapter uses the following example:
 * hildon_helloworld-9.c
 * gconf-listener

GConf Basics
GConf is a system for GNOME applications to store settings into a database system in a centralized manner. The aim of the GConf library is to provide applications a consistent view of the database access functions, as well as to provide tools for system administrators to enable them to distribute software settings in a centralized manner (across multiple computers).

The GConf "database" may consist of multiple databases (configured by the system administrator), but normally there will be at least one database engine that uses XML to store settings. This keeps the database still in a human readable form (as opposed to binary), and allows some consistency checks via schema verifications.

The interface for the client (program that uses GConf to store its settings) is always the same, irrespective of the database back-end (the client does not see this).

What makes GConf interesting, is its capability of notifying running clients that their settings have been changed by some other process than themselves. This allows for the clients to react soon (not quite real-time), and this leads to a situation where a user will change, for example, the GNOME HTTP proxy settings, and clients that are interested in that setting will get a notification (via a callback function) that the setting has changed. The clients will then read the new setting and modify their data structures to take the new setting into account.

Using GConf
The GConf model consists of two parts: the GConf client library (which will be used here) and the GConf server daemon that is the guardian and reader/writer of the back-end databases. In a regular GNOME environment, the client communicates with the server either by using the Bonobo library (lightweight object IPC mechanism) or D-Bus.

As Bonobo is not used in maemo (it is quite heavy, even if lightweight), the client will communicate with the server using D-Bus. This also allows the daemon to be started on demand, when there is at least one client wanting to use that service (this is a feature of D-Bus). The communication mechanism is encapsulated by the GConf client library, and as such, will be transparent.

In order to read or write the preference database, it is necessary to decide on the key to use to access the application values. The database namespace is hierarchical, and uses the '/'-character to implement this hierarchy, starting from a root location similar to UNIX file system namespace.

Each application will use its own "directory" under /apps/Maemo/appname/. N.B. Even when the word "directory" is seen in connection to GConf, one has to be careful to distinguish real directories from preference namespaces inside the GConf namespace. The /apps/Maemo/appname/ above is in the GConf namespace, so there will not actually be a physical directory called /apps/ on a system.

The keys should be named according to the platform guidelines. The current guideline is that each application should store its configuration keys under /apps/Maemo/appname/, where appname is the name of the application. There is no central registry on the names in use currently, so names should be selected carefully. Key names should all be lowercase, with underscore used to separate multiple words. Also, ASCII should be used, since GConf does not support localization for key names (it does for key values, but that is not covered in this material).

GConf values are typed, which means that it is necessary to select the type for the data that the key is supposed to hold.

The following types are supported for values in GConf:


 * gint (32-bit signed)
 * gboolean
 * gchar (ASCII/ISO 8859-1/UTF-8 C string)
 * gfloat (with the limitation that the resolution is not guaranteed nor specified by GConf because of portability issues)
 * a list of values of one type
 * a pair of values, each having their own type (useful for storing "mapping" data)

What is missing from the above list is storing binary data (for a good reason). The type system is also fairly limited. This is on purpose, so that complex configurations (like the Apache HTTP daemon uses, or Samba) are not attempted using GConf.

There is a diagnostic and administration tool called gconftool-2 that is also available in the SDK. It can be used to set and unset keys, as well as display their current contents.

Some examples of using gconftool-2 (on the SDK):


 * Displaying the contents of all keys stored under /apps/ (listing cut for brevity)

[sbox-DIABLO_X86: ~] &gt; run-standalone.sh gconftool-2 -R /apps /apps/osso: /apps/osso/inputmethod: launch_finger_kb_on_select = true input_method_plugin = himExample_vkb available_languages = [en_GB] use_finger_kb = true /apps/osso/inputmethod/hildon-im-languages: language-0 = en_GB current = 0 language-1 = list = [] /apps/osso/fontconfig: font_scaling_factor = Schema (type: `float' list_type:    '*invalid*' car_type: '*invalid*' cdr_type: '*invalid*'     locale: `C') /apps/osso/apps: /apps/osso/apps/controlpanel: groups = [copa_ia_general,copa_ia_connectivity, copa_ia_personalisation] icon_size = false group_ids = [general,connectivity,personalisation] /apps/osso/osso: /apps/osso/osso/thumbnailers: /apps/osso/osso/thumbnailers/audio@x-mp3: command = /usr/bin/hildon-thumb-libid3 /apps/osso/osso/thumbnailers/audio@x-m4a: command = /usr/bin/hildon-thumb-libid3 /apps/osso/osso/thumbnailers/audio@mp3: command = /usr/bin/hildon-thumb-libid3 /apps/osso/osso/thumbnailers/audio@x-mp2: command = /usr/bin/hildon-thumb-libid3


 * Creating and setting the value to a new key.

[sbox-DIABLO_X86: ~] &gt; run-standalone.sh gconftool-2 \ --set /apps/Maemo/testing/testkey --type=int 5


 * Listing all keys under the namespace /apps/Maemo/testing.

[sbox-DIABLO_X86: ~] &gt; run-standalone.sh gconftool-2 \ -R /apps/Maemo/testing testkey = 5


 * Removing the last key will also remove the key directory.

[sbox-DIABLO_X86: ~] &gt; run-standalone.sh gconftool-2 \ --unset /apps/Maemo/testing/testkey [sbox-DIABLO_X86: ~] &gt; run-standalone.sh gconftool-2 \ -R /apps/Maemo/testing


 * Removing whole key hierarchies is also possible.

[sbox-DIABLO_X86: ~] &gt; run-standalone.sh gconftool-2 \ --recursive-unset /apps/Maemo/testing

For more detailed information, please see Gconf API documentation.

Using GConf to read and write preferences
Section Application Settings of chapter Application Development in Maemo Reference Manual presents a short introductory example of gconf usage. The following example is a bit more complicated.

The example is required to:


 * Store the color that the user selects when the color button (in the toolbar) is used.
 * Load the color preference on application startup.

Even if GConf concepts seem to be logical, it can be seen that using GConf will require one to learn some new things (e.g. the GError-object). Since the GConf client code is in its own library, the relevant compiler flags and library options need to be added again. The pkg-config package name is gconf-2.0 hildon_helloworld-9.c

  /**    * hildon_helloworld-9.c    *    * This maemo code example is licensed under a MIT-style license,    * that can be found in the file called "License" in the same  ''  * directory as this file.    * Copyright (c) 2007-2008 Nokia Corporation. All rights reserved.    * ''   * We'll store the color that the user selects into a GConf  ''  * preference. In fact, we'll have three settings, one for each '' ''  * channel of the color (red, green and blue).    * '' ''  * Look for lines with "NEW" or "MODIFIED" in them.    */ ''   #include  <font color="#FF0000">&lt;stdlib.h&gt;  <font color="#000080"> #include  <font color="#FF0000">&lt;hildon/hildon-program.h&gt;  <font color="#000080"> #include  <font color="#FF0000">&lt;hildon/hildon-color-button.h&gt;  <font color="#000080"> #include  <font color="#FF0000">&lt;hildon/hildon-find-toolbar.h&gt;  <font color="#000080"> #include  <font color="#FF0000">&lt;hildon/hildon-file-chooser-dialog.h&gt;  <font color="#000080"> #include  <font color="#FF0000">&lt;hildon/hildon-banner.h&gt;  <font color="#000080"> #include  <font color="#FF0000">&lt;libgnomevfs/gnome-vfs.h&gt; '' <font color="#9A1900">/* Include the prototypes for GConf client functions (NEW). */ ''  <font color="#000080"> #include   <font color="#FF0000">&lt;gconf/gconf-client.h&gt; '' <font color="#9A1900">/* The application name -part of the GConf namespace (NEW). */ ''  <font color="#000080"> #define  APP_NAME <font color="#FF0000">"hildon_hello" '' <font color="#9A1900">/* This will be the root "directory" for our preferences (NEW). */ ''  <font color="#000080"> #define  GC_ROOT  <font color="#FF0000">"/apps/Maemo/" APP_NAME <font color="#FF0000">"/" '' <font color="#9A1900">/*... Listing cut for brevity ...*/ ''  <font color="#9A1900">/**   <font color="#9A1900"> * NEW   <font color="#9A1900"> *   <font color="#9A1900"> * Utility function to store the given color into our application  '' <font color="#9A1900"> * preferences. We could use a list of integers as well, but we'll ''  <font color="#9A1900"> * settle for three separate properties; one for each of RGB  '' <font color="#9A1900"> * channels.   <font color="#9A1900"> * '' '' <font color="#9A1900"> * The config keys that will be used are 'red', 'green' and 'blue'.   <font color="#9A1900"> * ''  <font color="#9A1900"> * NOTE:  '' <font color="#9A1900"> *  We're doing things very non-optimally. If our application would ''  <font color="#9A1900"> *  have multiple preference settings, and we would like to know   <font color="#9A1900"> *  when someone will change them (external program, another    <font color="#9A1900"> *   instance of our program, etc), we'd have to keep a reference to  '' <font color="#9A1900"> *  the GConf client connection. Listening for changes in ''  <font color="#9A1900"> *  preferences would also require a callback registration, but this  '' <font color="#9A1900"> *  is covered in the "maemo Platform Development" material.   <font color="#9A1900"> */ ''  <font color="#0000FF">static  <font color="#009900">void   <font color="#000000">confStoreColor   <font color="#990000">(   <font color="#0000FF">const  GdkColor <font color="#990000"> * color <font color="#990000">)  <font color="#FF0000">{ '' <font color="#9A1900">/* We'll store the pointer to the GConf connection here. */ ''   GConfClient <font color="#990000"> * gcClient <font color="#990000"> = NULL <font color="#990000"> ; '' <font color="#9A1900">/* Make sure that no NULLs are passed for the color. GdkColor is '' '' <font color="#9A1900">    not a proper GObject, so there is no GDK_IS_COLOR macro. */ ''    <font color="#000000">g_assert   <font color="#990000">( color <font color="#990000">);  <font color="#000000">g_print  <font color="#990000">(  <font color="#FF0000">"confStoreColor: invoked  <font color="#CC33CC">\n  <font color="#FF0000">"  <font color="#990000">); '' <font color="#9A1900">/* Open a connection to gconfd-2 (via D-Bus in maemo). The GConf ''  <font color="#9A1900">    API doesn't say whether this function can ever return NULL or  '' <font color="#9A1900">    how it will behave in error conditions. */ ''   gcClient <font color="#990000"> =   <font color="#000000">gconf_client_get_default   <font color="#990000">; '' <font color="#9A1900">/* We make sure that it's a valid GConf-client object. */ ''    <font color="#000000">g_assert   <font color="#990000">(   <font color="#000000">GCONF_IS_CLIENT   <font color="#990000">( gcClient <font color="#990000">)); '' <font color="#9A1900">/* Store the values. */ ''    <font color="#0000FF">if   <font color="#990000">(!   <font color="#000000">gconf_client_set_int   <font color="#990000">( gcClient <font color="#990000">, GC_ROOT <font color="#FF0000">"red"  <font color="#990000">, color <font color="#990000">-&gt; red <font color="#990000">, NULL <font color="#990000">)) <font color="#FF0000">{  <font color="#000000">g_warning  <font color="#990000">(  <font color="#FF0000">" failed to set %s/red to %d  <font color="#CC33CC">\n  <font color="#FF0000">"  <font color="#990000">, GC_ROOT <font color="#990000">, color <font color="#990000">-&gt; red <font color="#990000">); <font color="#FF0000">}  <font color="#0000FF">if  <font color="#990000">(!   <font color="#000000">gconf_client_set_int   <font color="#990000">( gcClient <font color="#990000">, GC_ROOT <font color="#FF0000">"green"  <font color="#990000">, color <font color="#990000">-&gt; green <font color="#990000">, NULL <font color="#990000">)) <font color="#FF0000">{  <font color="#000000">g_warning  <font color="#990000">(  <font color="#FF0000">" failed to set %s/green to %d  <font color="#CC33CC">\n  <font color="#FF0000">"  <font color="#990000">, GC_ROOT <font color="#990000">,                color <font color="#990000">-&gt; green <font color="#990000">); <font color="#FF0000">}  <font color="#0000FF">if  <font color="#990000">(!   <font color="#000000">gconf_client_set_int   <font color="#990000">( gcClient <font color="#990000">, GC_ROOT <font color="#FF0000">"blue"  <font color="#990000">, color <font color="#990000">-&gt; blue <font color="#990000">, NULL <font color="#990000">)) <font color="#FF0000">{  <font color="#000000">g_warning  <font color="#990000">(  <font color="#FF0000">" failed to set %s/blue to %d  <font color="#CC33CC">\n  <font color="#FF0000">"  <font color="#990000">, GC_ROOT <font color="#990000">,                color <font color="#990000">-&gt; blue <font color="#990000">); <font color="#FF0000">} '' <font color="#9A1900">/* Release the GConf client object (with GObject-unref). */ ''    <font color="#000000">g_object_unref   <font color="#990000">( gcClient <font color="#990000">); gcClient <font color="#990000"> = NULL <font color="#990000"> ; <font color="#FF0000">}  <font color="#9A1900">/**   <font color="#9A1900"> * NEW   <font color="#9A1900"> *   <font color="#9A1900"> * A utility function to get an integer but also return the status  '' <font color="#9A1900"> * whether the requested key existed or not.   <font color="#9A1900"> * ''  <font color="#9A1900"> * NOTE:   <font color="#9A1900"> *  It's also possible to use gconf_client_get_int, but it's not   <font color="#9A1900"> *  possible to then know whether they key existed or not, because   <font color="#9A1900"> *  the function will return 0 if the key doesn't exist (and if the   '' <font color="#9A1900"> *   value is 0, how could you tell these two conditions apart?).   <font color="#9A1900"> * ''  <font color="#9A1900"> * Parameters:   <font color="#9A1900"> * - GConfClient: the client object to use   <font color="#9A1900"> * - const gchar*: the key   <font color="#9A1900"> * - gint*: the address to store the integer to if the key exists   <font color="#9A1900"> *   <font color="#9A1900"> * Returns:  '' <font color="#9A1900"> * - TRUE: if integer has been updated with a value from GConf.   <font color="#9A1900"> *   FALSE: there was no such key or it wasn't an integer.   <font color="#9A1900"> */ ''  <font color="#0000FF">static  gboolean  <font color="#000000">confGetInt  <font color="#990000">( GConfClient <font color="#990000"> * gcClient <font color="#990000">,   <font color="#0000FF">const  gchar <font color="#990000"> * key <font color="#990000">,                             gint <font color="#990000"> * number <font color="#990000">)  <font color="#FF0000">{ '' <font color="#9A1900">/* This will hold the type/value pair at some point. */ ''   GConfValue <font color="#990000"> * val <font color="#990000"> = NULL <font color="#990000"> ;  <font color="#9A1900">/* Return flag (tells the caller whether this function wrote behind  '' <font color="#9A1900">     the 'number' pointer or not). */ ''   gboolean hasChanged <font color="#990000"> = FALSE <font color="#990000"> ; '' <font color="#9A1900">/* Try to get the type/value from the GConf DB.   <font color="#9A1900">     NOTE: ''  <font color="#9A1900">      We're using a version of the getter that will not return any  '' <font color="#9A1900">      defaults (if a schema would specify one). Instead, it will '' '' <font color="#9A1900">      return the value if one has been set (or NULL).   <font color="#9A1900">     We're not really interested in errors as this will return a NULL ''  <font color="#9A1900">    in case of missing keys or errors and that is quite enough for  '' <font color="#9A1900">    us. */ ''   val <font color="#990000"> =   <font color="#000000">gconf_client_get_without_default   <font color="#990000">( gcClient <font color="#990000">, key <font color="#990000">, NULL <font color="#990000">);  <font color="#0000FF">if  <font color="#990000">( val <font color="#990000"> == NULL <font color="#990000">)  <font color="#FF0000">{ '' <font color="#9A1900">/* Key wasn't found, no need to touch anything. */ ''      <font color="#000000">g_warning   <font color="#990000">(  <font color="#FF0000">"confGetInt: key %s not found  <font color="#CC33CC">\n  <font color="#FF0000">"  <font color="#990000">, key <font color="#990000">);  <font color="#0000FF">return  FALSE <font color="#990000"> ; <font color="#FF0000">} '' <font color="#9A1900">/* Check whether the value stored behind the key is an integer. If '' '' <font color="#9A1900">    it is not, we issue a warning, but return normally. */ ''    <font color="#0000FF">if   <font color="#990000">( val <font color="#990000">-&gt; type <font color="#990000"> == GCONF_VALUE_INT <font color="#990000">)  <font color="#FF0000">{ '' <font color="#9A1900">/* It's an integer, get it and store. */ ''     <font color="#990000"> * number <font color="#990000"> =   <font color="#000000">gconf_value_get_int   <font color="#990000">( val <font color="#990000">); '' <font color="#9A1900">/* Mark that we've changed the integer behind 'number'. */ ''     hasChanged <font color="#990000"> = TRUE <font color="#990000"> ; <font color="#FF0000">}  <font color="#0000FF">else   <font color="#FF0000">{  <font color="#000000">g_warning  <font color="#990000">(  <font color="#FF0000">"confGetInt: key %s is not an integer  <font color="#CC33CC">\n  <font color="#FF0000">"  <font color="#990000">, key <font color="#990000">); <font color="#FF0000">} '' <font color="#9A1900">/* Free the type/value-pair. */ ''    <font color="#000000">gconf_value_free   <font color="#990000">( val <font color="#990000">); val <font color="#990000"> = NULL <font color="#990000"> ;  <font color="#0000FF">return  hasChanged <font color="#990000"> ; <font color="#FF0000">}  <font color="#9A1900">/**   <font color="#9A1900"> * NEW   <font color="#9A1900"> *   <font color="#9A1900"> * Utility function to change the given color into the one that is  '' <font color="#9A1900"> * specified in application preferences.   <font color="#9A1900"> * '' '' <font color="#9A1900"> * If some key is missing, that channel is left untouched. The ''  <font color="#9A1900"> * function also checks for proper values for the channels so that  '' <font color="#9A1900"> * invalid values are not accepted (guint16 range of GdkColor).   <font color="#9A1900"> * ''  <font color="#9A1900"> * Parameters:  '' <font color="#9A1900"> * - GdkColor*: the color structure to modify if changed from prefs.   <font color="#9A1900"> * ''  <font color="#9A1900"> * Returns:  '' <font color="#9A1900"> * - TRUE if the color was been changed by this routine.   <font color="#9A1900"> *   FALSE if the color wasn't changed (there was an error or the    <font color="#9A1900"> *   color was already exactly the same as in the preferences).   <font color="#9A1900"> */ ''  <font color="#0000FF">static  gboolean  <font color="#000000">confLoadCurrentColor  <font color="#990000">( GdkColor <font color="#990000"> * color <font color="#990000">)  <font color="#FF0000">{ GConfClient <font color="#990000"> * gcClient <font color="#990000"> = NULL <font color="#990000"> ; '' <font color="#9A1900">/* Temporary holders for the pref values. */ ''   gint red <font color="#990000"> =  <font color="#990000">-  <font color="#993399">1  <font color="#990000"> ; gint green <font color="#990000"> = <font color="#990000">-  <font color="#993399">1  <font color="#990000"> ; gint blue <font color="#990000"> = <font color="#990000">-  <font color="#993399">1  <font color="#990000"> ; '' <font color="#9A1900">/* Temp variable to hold whether the color has changed. */ ''   gboolean hasChanged <font color="#990000"> = FALSE <font color="#990000"> ;  <font color="#000000">g_assert  <font color="#990000">( color <font color="#990000">);  <font color="#000000">g_print  <font color="#990000">(  <font color="#FF0000">"confLoadCurrentColor: invoked  <font color="#CC33CC">\n  <font color="#FF0000">"  <font color="#990000">); '' <font color="#9A1900">/* Open a connection to gconfd-2 (via d-bus). */ ''   gcClient <font color="#990000"> =   <font color="#000000">gconf_client_get_default   <font color="#990000">; '' <font color="#9A1900">/* Make sure that it's a valid GConf-client object. */ ''    <font color="#000000">g_assert   <font color="#990000">(   <font color="#000000">GCONF_IS_CLIENT   <font color="#990000">( gcClient <font color="#990000">));  <font color="#0000FF">if  <font color="#990000">(   <font color="#000000">confGetInt   <font color="#990000">( gcClient <font color="#990000">, GC_ROOT <font color="#FF0000">"red"  <font color="#990000">,  <font color="#990000">&amp; red <font color="#990000">))  <font color="#FF0000">{ '' <font color="#9A1900">/* We got the value successfully, now clamp it. */ ''      <font color="#000000">g_print   <font color="#990000">(  <font color="#FF0000">" got red = %d, "  <font color="#990000">, red <font color="#990000">);  <font color="#9A1900">/* We got a value, so let's limit it between 0 and 65535 (the  '' <font color="#9A1900">       legal range for guint16). We use the CLAMP macro from GLib for '' '' <font color="#9A1900">      this. */ ''     red <font color="#990000"> =   <font color="#000000">CLAMP   <font color="#990000">( red <font color="#990000">,  <font color="#993399">0  <font color="#990000">, G_MAXUINT16 <font color="#990000">);  <font color="#000000">g_print  <font color="#990000">(  <font color="#FF0000">"after clamping = %d  <font color="#CC33CC">\n  <font color="#FF0000">"  <font color="#990000">, red <font color="#990000">); '' <font color="#9A1900">/* Update &amp; mark that at least this component changed. */ ''     color <font color="#990000">-&gt; red <font color="#990000"> =  <font color="#990000">( guint16 <font color="#990000">) red <font color="#990000"> ; hasChanged <font color="#990000"> = TRUE <font color="#990000"> ; <font color="#FF0000">} '' <font color="#9A1900">/* Repeat the same logic for the green component. */ ''    <font color="#0000FF">if   <font color="#990000">(   <font color="#000000">confGetInt   <font color="#990000">( gcClient <font color="#990000">, GC_ROOT <font color="#FF0000">"green"  <font color="#990000">,  <font color="#990000">&amp; green <font color="#990000">))  <font color="#FF0000">{  <font color="#000000">g_print  <font color="#990000">(  <font color="#FF0000">" got green = %d, "  <font color="#990000">, green <font color="#990000">); green <font color="#990000"> =  <font color="#000000">CLAMP   <font color="#990000">( green <font color="#990000">,  <font color="#993399">0  <font color="#990000">, G_MAXUINT16 <font color="#990000">);  <font color="#000000">g_print  <font color="#990000">(  <font color="#FF0000">"after clamping = %d  <font color="#CC33CC">\n  <font color="#FF0000">"  <font color="#990000">, green <font color="#990000">); color <font color="#990000">-&gt; green <font color="#990000"> = <font color="#990000">( guint16 <font color="#990000">) green <font color="#990000"> ; hasChanged <font color="#990000"> = TRUE <font color="#990000"> ; <font color="#FF0000">} '' <font color="#9A1900">/* Repeat the same logic for the last component (blue). */ ''    <font color="#0000FF">if   <font color="#990000">(   <font color="#000000">confGetInt   <font color="#990000">( gcClient <font color="#990000">, GC_ROOT <font color="#FF0000">"blue"  <font color="#990000">,  <font color="#990000">&amp; blue <font color="#990000">))  <font color="#FF0000">{  <font color="#000000">g_print  <font color="#990000">(  <font color="#FF0000">" got blue = %d, "  <font color="#990000">, blue <font color="#990000">); blue <font color="#990000"> =  <font color="#000000">CLAMP   <font color="#990000">( blue <font color="#990000">,  <font color="#993399">0  <font color="#990000">, G_MAXUINT16 <font color="#990000">);  <font color="#000000">g_print  <font color="#990000">(  <font color="#FF0000">"after clamping = %d  <font color="#CC33CC">\n  <font color="#FF0000">"  <font color="#990000">, blue <font color="#990000">); color <font color="#990000">-&gt; blue <font color="#990000"> = <font color="#990000">( guint16 <font color="#990000">) blue <font color="#990000"> ; hasChanged <font color="#990000"> = TRUE <font color="#990000"> ; <font color="#FF0000">} '' <font color="#9A1900">/* Release the client object (with GObject-unref). */ ''    <font color="#000000">g_object_unref   <font color="#990000">( gcClient <font color="#990000">); gcClient <font color="#990000"> = NULL <font color="#990000"> ; '' <font color="#9A1900">/* Return status if the color was been changed by this routine. */ ''    <font color="#0000FF">return  hasChanged <font color="#990000"> ; <font color="#FF0000">}  <font color="#9A1900">/**   <font color="#9A1900"> * MODIFIED   <font color="#9A1900"> *  '' <font color="#9A1900"> * Invoked when the user selects a color (or will cancel the dialog).   <font color="#9A1900"> * ''  <font color="#9A1900"> * Will also write the color to preferences (GConf) each time the  '' <font color="#9A1900"> * color changes. We'll compare whether it has really changed (to   <font color="#9A1900"> * avoid writing to GConf is nothing really changed).   <font color="#9A1900"> */ ''  <font color="#0000FF">static  <font color="#009900">void   <font color="#000000">cbActionColorChanged   <font color="#990000">( HildonColorButton <font color="#990000"> * colorButton <font color="#990000">,                                   ApplicationState <font color="#990000"> * app <font color="#990000">)  <font color="#FF0000">{ '' <font color="#9A1900">/* Local variables that we'll need to handle the change (NEW). */ ''   gboolean hasChanged <font color="#990000"> = FALSE <font color="#990000"> ; GdkColor newColor <font color="#990000"> = <font color="#FF0000">{}  <font color="#990000"> ; GdkColor <font color="#990000"> * curColor <font color="#990000"> = NULL <font color="#990000"> ;  <font color="#000000">g_assert  <font color="#990000">( app <font color="#990000"> != NULL <font color="#990000">);  <font color="#000000">g_print  <font color="#990000">(  <font color="#FF0000">"cbActionColorChanged invoked  <font color="#CC33CC">\n  <font color="#FF0000">"  <font color="#990000">); '' <font color="#9A1900">/* Retrieve the new color from the color button (NEW). */ ''    <font color="#000000">hildon_color_button_get_color   <font color="#990000">( colorButton <font color="#990000">,  <font color="#990000">&amp; newColor <font color="#990000">);  <font color="#9A1900">/* Just an alias to save some typing (could also use  '' <font color="#9A1900">     app-&gt;currentColor) (NEW). */ ''   curColor <font color="#990000"> =  <font color="#990000">&amp; app <font color="#990000">-&gt; currentColor <font color="#990000"> ; '' <font color="#9A1900">/* Check whether the color really changed (NEW). */ ''    <font color="#0000FF">if   <font color="#990000">(( newColor <font color="#990000">. red  <font color="#990000"> != curColor <font color="#990000">-&gt; red <font color="#990000">)  <font color="#990000"> ||        <font color="#990000">( newColor <font color="#990000">. green <font color="#990000"> != curColor <font color="#990000">-&gt; green <font color="#990000">) <font color="#990000"> ||        <font color="#990000">( newColor <font color="#990000">. blue <font color="#990000"> != curColor <font color="#990000">-&gt; blue <font color="#990000">))  <font color="#FF0000">{ hasChanged <font color="#990000"> = TRUE <font color="#990000"> ; <font color="#FF0000">}  <font color="#0000FF">if  <font color="#990000">(! hasChanged <font color="#990000">)  <font color="#FF0000">{  <font color="#000000">g_print  <font color="#990000">(  <font color="#FF0000">" color not really changed  <font color="#CC33CC">\n  <font color="#FF0000">"  <font color="#990000">);  <font color="#0000FF">return  <font color="#990000"> ; <font color="#FF0000">} '' <font color="#9A1900">/* Color really changed, store to preferences (NEW). */ ''    <font color="#000000">g_print   <font color="#990000">(  <font color="#FF0000">" color changed, storing into preferences..  <font color="#CC33CC">\n  <font color="#FF0000">"  <font color="#990000">);  <font color="#000000">confStoreColor  <font color="#990000">(&amp; newColor <font color="#990000">);  <font color="#000000">g_print  <font color="#990000">(  <font color="#FF0000">" done.  <font color="#CC33CC">\n  <font color="#FF0000">"  <font color="#990000">); '' <font color="#9A1900">/* Update the changed color into the application state. */ ''   app <font color="#990000">-&gt; currentColor <font color="#990000"> = newColor <font color="#990000"> ; <font color="#FF0000">} '' <font color="#9A1900">/*... Listing cut for brevity ...*/ ''  <font color="#9A1900">/**   <font color="#9A1900"> * MODIFIED   <font color="#9A1900"> *   <font color="#9A1900"> * The color of the color button will be loaded from the application  '' <font color="#9A1900"> * preferences (or keep the default if preferences have no setting).   <font color="#9A1900"> */ ''  <font color="#0000FF">static  GtkWidget <font color="#990000"> *  <font color="#000000">buildToolbar   <font color="#990000">( ApplicationState <font color="#990000"> * app <font color="#990000">)  <font color="#FF0000">{ GtkToolbar <font color="#990000"> * toolbar <font color="#990000"> = NULL <font color="#990000"> ; GtkToolItem <font color="#990000"> * tbOpen <font color="#990000"> = NULL <font color="#990000"> ; GtkToolItem <font color="#990000"> * tbSave <font color="#990000"> = NULL <font color="#990000"> ; GtkToolItem <font color="#990000"> * tbSep <font color="#990000"> = NULL <font color="#990000"> ; GtkToolItem <font color="#990000"> * tbFind <font color="#990000"> = NULL <font color="#990000"> ; GtkToolItem <font color="#990000"> * tbColorButton <font color="#990000"> = NULL <font color="#990000"> ; GtkWidget <font color="#990000"> *  colorButton <font color="#990000"> = NULL <font color="#990000"> ;  <font color="#000000">g_assert  <font color="#990000">( app <font color="#990000"> != NULL <font color="#990000">); tbOpen <font color="#990000"> =  <font color="#000000">gtk_tool_button_new_from_stock   <font color="#990000">( GTK_STOCK_OPEN <font color="#990000">); tbSave <font color="#990000"> =  <font color="#000000">gtk_tool_button_new_from_stock   <font color="#990000">( GTK_STOCK_SAVE <font color="#990000">); tbSep <font color="#990000"> =   <font color="#000000">gtk_separator_tool_item_new   <font color="#990000">; tbFind <font color="#990000"> =  <font color="#000000">gtk_tool_button_new_from_stock   <font color="#990000">( GTK_STOCK_FIND <font color="#990000">); tbColorButton <font color="#990000"> =  <font color="#000000">gtk_tool_item_new   <font color="#990000">; colorButton <font color="#990000"> =  <font color="#000000">hildon_color_button_new   <font color="#990000">; '' <font color="#9A1900">/* Copy the color from the color button into the application state.   <font color="#9A1900">     This is done to detect whether the color in preferences matches '' '' <font color="#9A1900">    the default color or not (NEW). */ ''    <font color="#000000">hildon_color_button_get_color   <font color="#990000">(   <font color="#000000">HILDON_COLOR_BUTTON   <font color="#990000">( colorButton <font color="#990000">),                                  <font color="#990000">&amp; app <font color="#990000">-&gt; currentColor <font color="#990000">); '' <font color="#9A1900">/* Load preferences and change the color if necessary. */ ''    <font color="#000000">g_print   <font color="#990000">(  <font color="#FF0000">"buildToolbar: loading color pref.  <font color="#CC33CC">\n  <font color="#FF0000">"  <font color="#990000">);  <font color="#0000FF">if  <font color="#990000">(   <font color="#000000">confLoadCurrentColor   <font color="#990000">(&amp; app <font color="#990000">-&gt; currentColor <font color="#990000">))  <font color="#FF0000">{  <font color="#000000">g_print  <font color="#990000">(  <font color="#FF0000">" color not same as default one  <font color="#CC33CC">\n  <font color="#FF0000">"  <font color="#990000">);  <font color="#000000">hildon_color_button_set_color  <font color="#990000">(   <font color="#000000">HILDON_COLOR_BUTTON   <font color="#990000">( colorButton <font color="#990000">),                                    <font color="#990000">&amp; app <font color="#990000">-&gt; currentColor <font color="#990000">); <font color="#FF0000">}  <font color="#0000FF">else   <font color="#FF0000">{  <font color="#000000">g_print  <font color="#990000">(  <font color="#FF0000">" loaded color same as default  <font color="#CC33CC">\n  <font color="#FF0000">"  <font color="#990000">); <font color="#FF0000">}  <font color="#000000">gtk_container_add  <font color="#990000">(   <font color="#000000">GTK_CONTAINER   <font color="#990000">( tbColorButton <font color="#990000">), colorButton <font color="#990000">); '' <font color="#9A1900">/*... Listing cut for brevity ...*/ '' <font color="#FF0000">} </tt>

Since the graphical appearance of the program does not change (except that the ColorButton will display the correct initial color), a look will be taken at the stdout display of the program.

[sbox-DIABLO_X86: ~/appdev] &gt; run-standalone.sh ./hildon_helloworld-9 buildToolbar: loading color pref. confLoadCurrentColor: invoked hildon_helloworld-9[19840]: GLIB WARNING ** default - confGetInt: key /apps/Maemo/hildon_hello/red not found hildon_helloworld-9[19840]: GLIB WARNING ** default - confGetInt: key /apps/Maemo/hildon_hello/green not found hildon_helloworld-9[19840]: GLIB WARNING ** default - confGetInt: key /apps/Maemo/hildon_hello/blue not found loaded color same as default main: calling gtk_main cbActionMainToolbarToggle invoked cbActionColorChanged invoked color changed, storing into preferences.. confStoreColor: invoked done. main: returned from gtk_main and exiting with success

When running the program for the first time, warnings about the missing keys can be expected (since the values were not present in GConf).

Run the program again and exit:

[sbox-DIABLO_X86: ~/appdev] &gt; run-standalone.sh ./hildon_helloworld-9 buildToolbar: loading color pref. confLoadCurrentColor: invoked got red = 65535, after clamping = 65535 got green = 65535, after clamping = 65535 got blue = 0, after clamping = 0 color not same as default one main: calling gtk_main main: returned from gtk_main and exiting with success

The next step is to remove one key (red), and run the program again (this is to test and verify that the logic works): [sbox-DIABLO_X86: ~/appdev] &gt; run-standalone.sh gconftool-2 \ --unset /apps/Maemo/hildon_hello/red [sbox-DIABLO_X86: ~/appdev] &gt; run-standalone.sh gconftool-2 \ -R /apps/Maemo/hildon_hello green = 65535 blue = 0 [sbox-DIABLO_X86: ~/appdev] &gt; run-standalone.sh ./hildon_helloworld-9 buildToolbar: loading color pref. confLoadCurrentColor: invoked hildon_helloworld-9[19924]: GLIB WARNING ** default - confGetInt: key /apps/Maemo/hildon_hello/red not found got green = 65535, after clamping = 65535 got blue = 0, after clamping = 0 color not same as default one main: calling gtk_main main: returned from gtk_main and exiting with success

Listening to changes in GConf
Now it is time to see how to extend GConf to be more suitable in asynchronous work, and especially when implementing services.

When the configuration needs for the service are simple, and reacting to configuration changes in "realtime" is desired, it is advisable to use GConf. Also, people tend to use GConf when they are too lazy to write their own configuration file parsers (although there is a simple one in GLib), or too lazy to write the GUI part to change the settings. This example program will simulate the first case, and react to changes in a subset of GConf configuration name space when the changes happen.

The application will be interested in two string values; one to set the device to use for communication (connection), and the other to set the communication parameters for the device (connectionparams). Since this example will be concentrating on just the change notifications, the program logic is simplified by omitting the proper set-up code in the program. This means that it is necessary to set up some values to the GConf keys prior to running the program. For this, gconftool-2 will be used, and a target has been prepared in the Makefile just for this (see section GNU Make and Makefiles if necessary): gconf-listener/Makefile

<tt>  <font color="#9A1900"> # Define a variable for this so that the GConf root may be changed  gconf_root <font color="#990000"> := /apps/Maemo/platdev_ex '' <font color="#9A1900"> # ... Listing cut for brevity ...   <font color="#9A1900"> # This will setup the keys into default values.   <font color="#9A1900"> # It will first do a clear to remove any existing keys. '' primekeys <font color="#990000"> : clearkeys gconftool- <font color="#993399">2 -set -type string <font color="#990000">\ <font color="#009900">$(gconf_root) /connection btcomm0 gconftool- <font color="#993399">2 -set -type string <font color="#990000">\ <font color="#009900">$(gconf_root) /connectionparams <font color="#993399">9600 <font color="#990000">,  <font color="#993399">8  <font color="#990000">, N <font color="#990000">,  <font color="#993399">1  <font color="#9A1900"> # Remove all application keys  clearkeys <font color="#990000"> : @gconftool- <font color="#993399">2 -recursive-unset <font color="#009900">$(gconf_root)  <font color="#9A1900"> # Dump all application keys  dumpkeys <font color="#990000"> : @echo Keys under <font color="#009900">$(gconf_root) <font color="#990000"> : @gconftool- <font color="#993399">2 -recursive-list <font color="#009900">$(gconf_root) </tt>

The next step is to prepare the keyspace by running the primekeys target, and to verify that it succeeds by running the dumpkeys target:

[sbox-DIABLO_X86: ~/gconf-listener] &gt; make primekeys gconftool-2 --set --type string \ /apps/Maemo/platdev_ex/connection btcomm0 gconftool-2 --set --type string \ /apps/Maemo/platdev_ex/connectionparams 9600,8,N,1 [sbox-DIABLO_X86: ~/gconf-listener] &gt; make dumpkeys Keys under /apps/Maemo/platdev_ex: connectionparams = 9600,8,N,1 connection = btcomm0

Implementing Notifications on Changes in GConf
The first step here is to take care of the necessary header information. The GConf namespace settings have been all implemented using cpp macros, so that one can easily change the prefix of the name space if required later on. gconf-listener/gconf-key-watch.c

<tt>  <font color="#000080"> #include  <font color="#FF0000">&lt;glib.h&gt;  <font color="#000080"> #include  <font color="#FF0000">&lt;gconf/gconf-client.h&gt;  <font color="#000080"> #include  <font color="#FF0000">&lt;string.h&gt;   <font color="#9A1900">/* strcmp */   <font color="#9A1900">/* As per maemo Coding Style and Guidelines document, we use the  '' <font color="#9A1900">  /apps/Maemo/ -prefix.   <font color="#9A1900">   NOTE: There is no central registry (as of this moment) that you ''  <font color="#9A1900">        could check that your application name doesn't collide with  '' <font color="#9A1900">        other application names, so caution is advised! */ ''  <font color="#000080"> #define  SERVICE_GCONF_ROOT <font color="#FF0000">"/apps/Maemo/platdev_ex"  <font color="#9A1900">/* We define the names of the keys symbolically so that we may change   <font color="#9A1900">  them later if necessary, and so that the GConf "root directory" for  '' <font color="#9A1900">  our application will be automatically prefixed to the paths. */ ''  <font color="#000080"> #define  SERVICE_KEY_CONNECTION <font color="#990000">\ SERVICE_GCONF_ROOT <font color="#FF0000">"/connection"  <font color="#000080"> #define  SERVICE_KEY_CONNECTIONPARAMS <font color="#990000">\ SERVICE_GCONF_ROOT <font color="#FF0000">"/connectionparams" </tt>

The main starts innocently enough, by creating a GConf client object (that encapsulates the connection to the GConf daemon), and then displays the two values on output: gconf-listener/gconf-key-watch.c

<tt> <font color="#009900">int  <font color="#000000">main   <font color="#990000">(  <font color="#009900">int argc <font color="#990000">,  <font color="#009900">char  <font color="#990000"> ** argv <font color="#990000">)  <font color="#FF0000">{ '' <font color="#9A1900">/* Will hold reference to the GConfClient object. */ ''   GConfClient <font color="#990000"> * client <font color="#990000"> = NULL <font color="#990000"> ;  <font color="#9A1900">/* Initialize this to NULL so that we'll know whether an error   <font color="#9A1900">    occurred or not (and we don't have an existing GError object   '' <font color="#9A1900">     anyway at this point). */ ''   GError <font color="#990000"> * error <font color="#990000"> = NULL <font color="#990000"> ; '' <font color="#9A1900">/* This will hold a reference to the mainloop object. */ ''   GMainLoop <font color="#990000"> * mainloop <font color="#990000"> = NULL <font color="#990000"> ;  <font color="#000000">g_print  <font color="#990000">( PROGNAME <font color="#FF0000">":main Starting.  <font color="#CC33CC">\n  <font color="#FF0000">"  <font color="#990000">); '' <font color="#9A1900">/* Must be called to initialize GType system. The API reference for '' '' <font color="#9A1900">    gconf_client_get_default insists.   <font color="#9A1900">     NOTE: Using gconf_init is deprecated! */ ''    <font color="#000000">g_type_init   <font color="#990000">; '' <font color="#9A1900">/* Create a new mainloop structure that we'll use. Use default '' '' <font color="#9A1900">    context (NULL) and set the 'running' flag to FALSE. */ ''   mainloop <font color="#990000"> =   <font color="#000000">g_main_loop_new   <font color="#990000">( NULL <font color="#990000">, FALSE <font color="#990000">);  <font color="#0000FF">if  <font color="#990000">( mainloop <font color="#990000"> == NULL <font color="#990000">)  <font color="#FF0000">{  <font color="#000000">g_error  <font color="#990000">( PROGNAME <font color="#FF0000">": Failed to create mainloop!  <font color="#CC33CC">\n  <font color="#FF0000">"  <font color="#990000">); <font color="#FF0000">} '' <font color="#9A1900">/* Create a new GConfClient object using the default settings. */ ''   client <font color="#990000"> =   <font color="#000000">gconf_client_get_default   <font color="#990000">;  <font color="#0000FF">if  <font color="#990000">( client <font color="#990000"> == NULL <font color="#990000">)  <font color="#FF0000">{  <font color="#000000">g_error  <font color="#990000">( PROGNAME <font color="#FF0000">": Failed to create GConfClient!  <font color="#CC33CC">\n  <font color="#FF0000">"  <font color="#990000">); <font color="#FF0000">}  <font color="#000000">g_print  <font color="#990000">( PROGNAME <font color="#FF0000">":main GType and GConfClient initialized.  <font color="#CC33CC">\n  <font color="#FF0000">"  <font color="#990000">); '' <font color="#9A1900">/* Display the starting values for the two keys. */ ''    <font color="#000000">dispStringKey   <font color="#990000">( client <font color="#990000">, SERVICE_KEY_CONNECTION <font color="#990000">);  <font color="#000000">dispStringKey  <font color="#990000">( client <font color="#990000">, SERVICE_KEY_CONNECTIONPARAMS <font color="#990000">); </tt>

The dispStringKey utility is rather simple as well, building on the GConf material that was covered in the previous section [localhost#sec:using_gconf_to_read_write_pref 9.3.2] : gconf-listener/gconf-key-watch.c

<tt>  <font color="#9A1900">/**  '' <font color="#9A1900"> * Utility to retrieve a string key and display it.   <font color="#9A1900"> * (Just as a small refresher on the API.) ''  <font color="#9A1900"> */   <font color="#0000FF">static  <font color="#009900">void   <font color="#000000">dispStringKey   <font color="#990000">( GConfClient <font color="#990000"> * client <font color="#990000">,                             <font color="#0000FF">const  gchar <font color="#990000"> * keyname <font color="#990000">)  <font color="#FF0000">{ '' <font color="#9A1900">/* This will hold the string value of the key. It will be ''  <font color="#9A1900">    dynamically allocated for us, so we need to release it ourselves  '' <font color="#9A1900">    when done (before returning). */ ''   gchar <font color="#990000"> * valueStr <font color="#990000"> = NULL <font color="#990000"> ;  <font color="#9A1900">/* We're not interested in the errors themselves (the last   <font color="#9A1900">     parameter), but the function will return NULL if there is one,  '' <font color="#9A1900">    so we just end in that case. */ ''   valueStr <font color="#990000"> =   <font color="#000000">gconf_client_get_string   <font color="#990000">( client <font color="#990000">, keyname <font color="#990000">, NULL <font color="#990000">);  <font color="#0000FF">if  <font color="#990000">( valueStr <font color="#990000"> == NULL <font color="#990000">)  <font color="#FF0000">{  <font color="#000000">g_error  <font color="#990000">( PROGNAME <font color="#FF0000">": No string value for %s. Quitting  <font color="#CC33CC">\n  <font color="#FF0000">"  <font color="#990000">, keyname <font color="#990000">); '' <font color="#9A1900">/* Application terminates. */ ''   <font color="#FF0000">}  <font color="#000000">g_print  <font color="#990000">( PROGNAME <font color="#FF0000">": Value for key '%s' is set to '%s'  <font color="#CC33CC">\n  <font color="#FF0000">"  <font color="#990000">,            keyname <font color="#990000">, valueStr <font color="#990000">);  <font color="#9A1900">/* Normally one would want to use the value for something beyond   <font color="#9A1900">    just displaying it, but since this code doesn't, we release the  '' <font color="#9A1900">    allocated value string. */ ''    <font color="#000000">g_free   <font color="#990000">( valueStr <font color="#990000">); <font color="#FF0000">} </tt>

Next, the GConf client is told to attach itself to a specific name space part that this example is going to operate with: gconf-listener/gconf-key-watch.c

<tt>  <font color="#9A1900">/**  '' <font color="#9A1900">  * Register directory to watch for changes. This will then tell ''  <font color="#9A1900">  * GConf to watch for changes in this namespace, and cause the  '' <font color="#9A1900">  * "value-changed"-signal to be emitted. We won't be using that ''  <font color="#9A1900">  * mechanism, but will opt to a more modern (and potentially more   '' <font color="#9A1900">   * scalable solution). The directory needs to be added to the '' '' <font color="#9A1900">  * watch list in either case.   <font color="#9A1900">   * ''  <font color="#9A1900">  * When adding directories, you can sometimes optimize your program   <font color="#9A1900">  * performance by asking GConfClient to preload some (or all) keys  '' <font color="#9A1900">  * under a specific directory. This is done via the preload_type '' '' <font color="#9A1900">  * parameter (we use GCONF_CLIENT_PRELOAD_NONE below). Since our ''  <font color="#9A1900">  * program will only listen for changes, we don't want to use extra  '' <font color="#9A1900">  * memory to keep the keys cached.   <font color="#9A1900">   * ''  <font color="#9A1900">  * Parameters:   <font color="#9A1900">  * - client: GConf-client object   <font color="#9A1900">  * - SERVICEPATH: the name of the GConf namespace to follow   <font color="#9A1900">  * - GCONF_CLIENT_PRELOAD_NONE: do not preload any of contents   <font color="#9A1900">  * - error: where to store the pointer to allocated GError on  '' <font color="#9A1900">  *          errors.   <font color="#9A1900">   */ ''  <font color="#000000">gconf_client_add_dir  <font color="#990000">( client <font color="#990000">,                         SERVICE_GCONF_ROOT <font color="#990000">,                         GCONF_CLIENT_PRELOAD_NONE <font color="#990000">,                         <font color="#990000">&amp; error <font color="#990000">);  <font color="#0000FF">if  <font color="#990000">( error <font color="#990000"> != NULL <font color="#990000">)  <font color="#FF0000">{  <font color="#000000">g_error  <font color="#990000">( PROGNAME <font color="#FF0000">": Failed to add a watch to GCClient: %s  <font color="#CC33CC">\n  <font color="#FF0000">"  <font color="#990000">,              error <font color="#990000">-&gt; message <font color="#990000">);  <font color="#9A1900">/* Normally we'd also release the allocated GError, but since  '' <font color="#9A1900">      this program will terminate on g_error, we won't do that.   <font color="#9A1900">       Hence the next line is commented. */       <font color="#9A1900">/* g_error_free(error); */ ''  <font color="#9A1900">/* When you want to release the error if it has been allocated,   <font color="#9A1900">      or just continue if not, use g_clear_error(&amp;error); */  <font color="#FF0000">}  <font color="#000000">g_print  <font color="#990000">( PROGNAME <font color="#FF0000">":main Added " SERVICE_GCONF_ROOT <font color="#FF0000">".  <font color="#CC33CC">\n  <font color="#FF0000">"  <font color="#990000">); </tt>

Proceeding with the callback function registration, we have: gconf-listener/gconf-key-watch.c

<tt>  <font color="#9A1900">/* Register our interest (in the form of a callback function) for  '' <font color="#9A1900">    any changes under the namespace that we just added.   <font color="#9A1900">     Parameters: '' '' <font color="#9A1900">    - client: GConfClient object.   <font color="#9A1900">     - SERVICEPATH: namespace under which we can get notified for '' '' <font color="#9A1900">                   changes.   <font color="#9A1900">     - gconf_notify_func: callback that will be called on changes.   <font color="#9A1900">     - NULL: user-data pointer (not used here).   <font color="#9A1900">     - NULL: function to call on user-data when notify is removed or '' '' <font color="#9A1900">            GConfClient destroyed. NULL for none (since we don't   <font color="#9A1900">             have user-data anyway).   <font color="#9A1900">     - error: return location for an allocated GError.   <font color="#9A1900">     Returns: ''  <font color="#9A1900">    guint: an ID for this notification so that we could remove it  '' <font color="#9A1900">           later with gconf_client_notify_remove. We're not going '' '' <font color="#9A1900">           to use it so we don't store it anywhere. */ ''    <font color="#000000">gconf_client_notify_add   <font color="#990000">( client <font color="#990000">, SERVICE_GCONF_ROOT <font color="#990000">,                            keyChangeCallback <font color="#990000">, NULL <font color="#990000">, NULL <font color="#990000">,  <font color="#990000">&amp; error <font color="#990000">);  <font color="#0000FF">if  <font color="#990000">( error <font color="#990000"> != NULL <font color="#990000">)  <font color="#FF0000">{  <font color="#000000">g_error  <font color="#990000">( PROGNAME <font color="#FF0000">": Failed to add register the callback: %s  <font color="#CC33CC">\n  <font color="#FF0000">"  <font color="#990000">,              error <font color="#990000">-&gt; message <font color="#990000">); '' <font color="#9A1900">/* Program terminates. */ ''   <font color="#FF0000">}  <font color="#000000">g_print  <font color="#990000">( PROGNAME <font color="#FF0000">":main CB registered &amp; starting main loop  <font color="#CC33CC">\n  <font color="#FF0000">"  <font color="#990000">); </tt>

When dealing with regular desktop software, one could use multiple callback functions; one for each key to track. However, this would require implementing multiple callback functions, and this runs a risk of enlarging the size of the code. For this reason, the example code uses one callback function, which will internally multiplex between the two keys (by using strcmp): gconf-listener/gconf-key-watch.c

<tt>  <font color="#9A1900">/**  '' <font color="#9A1900"> * Callback called when a key in watched directory changes.   <font color="#9A1900"> * Prototype for the callback must be compatible with '' '' <font color="#9A1900"> * GConfClientNotifyFunc (for ref).   <font color="#9A1900"> * ''  <font color="#9A1900"> * It will find out which key changed (using strcmp, since the same   <font color="#9A1900"> * callback is used to track both keys) and the display the new value  '' <font color="#9A1900"> * of the key.   <font color="#9A1900"> * ''  <font color="#9A1900"> * The changed key/value pair will be communicated in the entry  '' <font color="#9A1900"> * parameter. userData will be NULL (can be set on notify_add [in   <font color="#9A1900"> * main]). Normally the application state would be carried within the ''  <font color="#9A1900"> * userData parameter, so that this callback could then modify the  '' <font color="#9A1900"> * view based on the change. Since this program does not have a state, ''  <font color="#9A1900"> * there is little that we can do within the function (it will abort  '' <font color="#9A1900"> * the program on errors though).   <font color="#9A1900"> */ ''  <font color="#0000FF">static  <font color="#009900">void   <font color="#000000">keyChangeCallback   <font color="#990000">( GConfClient <font color="#990000"> * client <font color="#990000">,                                guint        cnxn_id <font color="#990000">,                                GConfEntry <font color="#990000"> *  entry <font color="#990000">,                                gpointer     userData <font color="#990000">)  <font color="#FF0000">{ '' <font color="#9A1900">/* This will hold the pointer to the value. */ ''    <font color="#0000FF">const  GConfValue <font color="#990000"> * value <font color="#990000"> = NULL <font color="#990000"> ; '' <font color="#9A1900">/* This will hold a pointer to the name of the key that changed. */ ''    <font color="#0000FF">const  gchar <font color="#990000"> * keyname <font color="#990000"> = NULL <font color="#990000"> ;  <font color="#9A1900">/* This will hold a dynamically allocated human-readable  '' <font color="#9A1900">    representation of the changed value. */ ''   gchar <font color="#990000"> * strValue <font color="#990000"> = NULL <font color="#990000"> ;  <font color="#000000">g_print  <font color="#990000">( PROGNAME <font color="#FF0000">": keyChangeCallback invoked.  <font color="#CC33CC">\n  <font color="#FF0000">"  <font color="#990000">); '' <font color="#9A1900">/* Get a pointer to the key (this is not a copy). */ ''   keyname <font color="#990000"> =   <font color="#000000">gconf_entry_get_key   <font color="#990000">( entry <font color="#990000">);  <font color="#9A1900">/* It will be quite fatal if after change we cannot retrieve even  '' <font color="#9A1900">    the name for the gconf entry, so we error out here. */ ''    <font color="#0000FF">if   <font color="#990000">( keyname <font color="#990000"> == NULL <font color="#990000">)  <font color="#FF0000">{  <font color="#000000">g_error  <font color="#990000">( PROGNAME <font color="#FF0000">": Couldn't get the key name!  <font color="#CC33CC">\n  <font color="#FF0000">"  <font color="#990000">); '' <font color="#9A1900">/* Application terminates. */ ''   <font color="#FF0000">} '' <font color="#9A1900">/* Get a pointer to the value from changed entry. */ ''   value <font color="#990000"> =   <font color="#000000">gconf_entry_get_value   <font color="#990000">( entry <font color="#990000">);  <font color="#9A1900">/* If we get a NULL as the value, it means that the value either has  '' <font color="#9A1900">    not been set, or is at default. As a precaution we assume that '' '' <font color="#9A1900">    this cannot ever happen, and will abort if it does.   <font color="#9A1900">     NOTE: A real program should be more resilient in this case, but '' '' <font color="#9A1900">          the problem is: what is the correct action in this case?   <font color="#9A1900">           This is not always simple to decide.   <font color="#9A1900">     NOTE: You can trip this assert with 'make primekeys', since that ''  <font color="#9A1900">          will first remove all the keys (which causes the CB to   '' <font color="#9A1900">           be invoked, and abort here). */ ''    <font color="#000000">g_assert   <font color="#990000">( value <font color="#990000"> != NULL <font color="#990000">); '' <font color="#9A1900">/* Check that it looks like a valid type for the value. */ ''    <font color="#0000FF">if   <font color="#990000">(!   <font color="#000000">GCONF_VALUE_TYPE_VALID   <font color="#990000">( value <font color="#990000">-&gt; type <font color="#990000">))  <font color="#FF0000">{  <font color="#000000">g_error  <font color="#990000">( PROGNAME <font color="#FF0000">": Invalid type for gconfvalue!  <font color="#CC33CC">\n  <font color="#FF0000">"  <font color="#990000">); <font color="#FF0000">} '' <font color="#9A1900">/* Create a human readable representation of the value. Since this ''  <font color="#9A1900">    will be a new string created just for us, we'll need to be  '' <font color="#9A1900">    careful and free it later. */ ''   strValue <font color="#990000"> =   <font color="#000000">gconf_value_to_string   <font color="#990000">( value <font color="#990000">);  <font color="#9A1900">/* Print out a message (depending on which of the tracked keys   <font color="#9A1900">     change. */      <font color="#0000FF">if   <font color="#990000">(   <font color="#000000">strcmp   <font color="#990000">( keyname <font color="#990000">, SERVICE_KEY_CONNECTION <font color="#990000">)  <font color="#990000"> ==  <font color="#993399">0  <font color="#990000">)  <font color="#FF0000">{       <font color="#000000">g_print   <font color="#990000">( PROGNAME <font color="#FF0000">": Connection type setting changed: [%s]  <font color="#CC33CC">\n  <font color="#FF0000">"  <font color="#990000">, strValue <font color="#990000">);   <font color="#FF0000">}   <font color="#0000FF">else    <font color="#0000FF">if   <font color="#990000">(   <font color="#000000">strcmp   <font color="#990000">( keyname <font color="#990000">, SERVICE_KEY_CONNECTIONPARAMS <font color="#990000">)  <font color="#990000"> ==  <font color="#993399">0  <font color="#990000">)  <font color="#FF0000">{       <font color="#000000">g_print   <font color="#990000">( PROGNAME <font color="#FF0000">": Connection params setting changed: [%s]  <font color="#CC33CC">\n  <font color="#FF0000">"  <font color="#990000">, strValue <font color="#990000">);   <font color="#FF0000">}   <font color="#0000FF">else   <font color="#FF0000">{       <font color="#000000">g_print   <font color="#990000">( PROGNAME <font color="#FF0000">":Unknown key: %s (value: [%s])  <font color="#CC33CC">\n  <font color="#FF0000">"  <font color="#990000">, keyname <font color="#990000">, strValue <font color="#990000">);   <font color="#FF0000">}     <font color="#9A1900">/* Free the string representation of the value. */      <font color="#000000">g_free   <font color="#990000">( strValue <font color="#990000">);     <font color="#000000">g_print   <font color="#990000">( PROGNAME <font color="#FF0000">": keyChangeCallback done.  <font color="#CC33CC">\n  <font color="#FF0000">"  <font color="#990000">);  <font color="#FF0000">} </tt>

The complications in the above code rise from the fact that GConf communicates values using a GValue structure, which can carry values of any simple type. Since GConf (or the user for that matter) cannot be completely trusted to return the correct type for the value, it is necessary to be extra careful, and not assume that it will always be a string. GConf also supports "default" values, which are communicated to the application using NULLs, so it is also necessary to guard against that. Especially since the application does not provide a schema for the configuration space that would contain the default values.

The next step is to build and test the program. The program will be started on the background, so that gconftool-2 can be used to see how the program reacts to changing parameters:

[sbox-DIABLO_X86: ~/gconf-listener] &gt; make cc -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include -I/usr/include/gconf/2 \ -I/usr/include/dbus-1.0 -I/usr/lib/dbus-1.0/include -Wall -g \ -DPROGNAME=\"gconf-key-watch\" gconf-key-watch.c -o gconf-key-watch \ -lgconf-2 -ldbus-glib-1 -ldbus-1 -lgobject-2.0 -lglib-2.0 [sbox-DIABLO_X86: ~/gconf-listener] &gt; run-standalone.sh ./gconf-key-watch &amp; [2] 21385 gconf-key-watch:main Starting. gconf-key-watch:main GType and GConfClient initialized. gconf-key-watch: Value for key '/apps/Maemo/platdev_ex/connection' is set to 'btcomm0' gconf-key-watch: Value for key '/apps/Maemo/platdev_ex/connectionparams' is set to '9600,8,N,1' gconf-key-watch:main Added /apps/Maemo/platdev_ex. gconf-key-watch:main CB registered &amp; starting main loop [sbox-DIABLO_X86: ~/gconf-listener] &gt; gconftool-2 --set --type string \ /apps/Maemo/platdev_ex/connection ttyS0 gconf-key-watch: keyChangeCallback invoked. gconf-key-watch: Connection type setting changed: [ttyS0] gconf-key-watch: keyChangeCallback done. [sbox-DIABLO_X86: ~/gconf-listener] &gt; gconftool-2 --set --type string \ /apps/Maemo/platdev_ex/connectionparams '' gconf-key-watch: keyChangeCallback invoked. gconf-key-watch: Connection params setting changed: [] gconf-key-watch: keyChangeCallback done.

The latter change is somewhat problematic (which the code needs to deal with as well). It is necessary to decide how to react to values that are of the correct type, but with senseless values. GConf in itself does not provide syntax checking for the values, or any semantic checking support. It is recommended that configuration changes will only be reacted to when they pass some internal (to the application) logic that will check their validity, both at syntax level and also at semantic level.

One option would also be resetting the value back to a valid value, whenever the program detects an invalid value set attempt. This will lead to a lot of problems, if the value is set programmatically from another program that will obey the same rule, so it is not advisable. Quitting the program on invalid values is also an option that should not be used, since the restricted environment does not provide many ways to inform the user that the program has quit.

An additional possible problem is having multiple keys that are all "related" to a single setting or action. It is necessary to decide, how to deal with changes across multiple GConf keys that are related, yet changed separately. The two key example code demonstrates the inherent problem: should the server re-initialize the (theoretic) connection, when the connection key is changed, or when the connectionparams key is changed? If the connection is re-initialized when either of the keys is changed, then the connection will be re-initialized twice when both are changed "simultaneously" (user presses "Apply" on a settings dialog, or gconftool-2 is run and sets both keys). It is easy to see how this might be an even larger problem, if instead of two keys, there were five per connection. GConf and the GConfClient GObject wrapper that has been used here do not support "configuration set transactions", which would allow setting and processing multiple related keys using an atomic model. The example program ignores this issue completely.

The next step is to test how the program (which is still running) reacts to other problematic situations:

[sbox-DIABLO_X86: ~/gconf-listener] &gt; gconftool-2 --set --type int \ /apps/Maemo/platdev_ex/connectionparams 5 gconf-key-watch: keyChangeCallback invoked. gconf-key-watch: Connection params setting changed: [5] gconf-key-watch: keyChangeCallback done. [sbox-DIABLO_X86: ~/gconf-listener] &gt; gconftool-2 --set --type boolean \ /apps/Maemo/platdev_ex/connectionparams true gconf-key-watch: keyChangeCallback invoked. gconf-key-watch: Connection params setting changed: [true] gconf-key-watch: keyChangeCallback done.

The next example removes the configuration keys, while the program is still running:

[sbox-DIABLO_X86: ~/gconf-listener] &gt; make clearkeys gconf-key-watch: keyChangeCallback invoked. gconf-key-watch[21403]: GLIB ERROR ** default - file gconf-key-watch.c: line 129 (keyChangeCallback): assertion failed: (value != NULL) aborting... /usr/bin/run-standalone.sh: line 11: 21403 Aborted (core dumped) "$@" [1]+ Exit 134 run-standalone.sh ./gconf-key-watch

Since the code (in the callback function) contains an assert that checks for non-NULL values, it will abort when the key is removed, and that causes the value to go to NULL. So the abortion in the above case is expected.