GConf introduction
I played a little with GConf today. Here's some little introduction to it.
We'll be doing 3 very simple things today:
- Read out GConf key values
- Set key values
- Read out values, and register to value changes
Reading out a string from the GConf tree
Reading out a value (in our case a string) is very simple: first of all you need to know which string you want to read. In this sample we'll try to get the value of the default browser. Then you create a GConfClient, a proxy to the GConf system, you query gconfd using the proxy, get the value and do something with it.
Here's the code:
/* GConf test reader
* Compile using
* gcc `pkg-config --libs --cflags gconf-2.0` -o reader reader.c
*/
#include <glib.h>
#include <gconf/gconf-client.h>
/* Path to the key we'll be working with */
#define GCKEY "/desktop/gnome/applications/browser/exec"
gint main(gint argc, gchar *argv[]) {
/* Our proxy */
GConfClient *gclient = NULL;
/* String to store the GCKEY's value in */
gchar *val = NULL;
/* Initialize the GConf subsystem */
gconf_init(argc, argv, NULL);
g_print("Working with key \"%s\"\n", "" GCKEY);
/* Get the default GConf proxy */
gclient = gconf_client_get_default();
g_assert(gclient != NULL);
/* Get the value of GCKEY */
val = gconf_client_get_string(gclient, "" GCKEY, NULL);
if(val == NULL) {
/* Key was not set before */
val = g_strdup("<unset>");
}
g_assert(val != NULL);
g_print("Value: \"%s\"\n", val);
g_free(val);
return 0;
}
As you can see this is very straight-forward.
Now compile and run the code:
ikke@marslander ~/Projects/gconf $ gcc `pkg-config --libs --cflags gconf-2.0` -o reader reader.c
ikke@marslander ~/Projects/gconf $ ./reader
Working with key "/desktop/gnome/applications/browser/exec"
Value: "mozilla"
Setting a key value
To set a key, once more we just need a GConfClient proxy, and call a function to set the new key value:
/* GConf test writer
* Compile using
* gcc `pkg-config --libs --cflags gconf-2.0` -o setkey setkey.c
*/
#include <glib.h>
#include <gconf/gconf-client.h>
/* Path to the key we'll be working with */
#define GCKEY "/extra/test/key"
gint main(gint argc, gchar *argv[]) {
/* Our proxy */
GConfClient *gclient = NULL;
/* New value */
gchar *val = NULL;
/* Initialize the GConf subsystem */
gconf_init(argc, argv, NULL);
if(argc > 1) {
val = g_strdup(argv[1]);
}
else {
val = g_strdup("testval");
}
g_print("Working with key \"%s\"\n", "" GCKEY);
/* Get the default GConf proxy */
gclient = gconf_client_get_default();
g_assert(gclient != NULL);
g_assert(val != NULL);
gconf_client_set_string(gclient, "" GCKEY, val, NULL);
g_print("Value \"%s\" set\n", val);
g_free(val);
return 0;
}
Again, compile the code and play around with it:
ikke@marslander ~/Projects/gconf $ gcc `pkg-config --libs --cflags gconf-2.0` -o setkey setkey.c
ikke@marslander ~/Projects/gconf $ ./setkey
Working with key "/extra/test/key"
Value "testval" set
ikke@marslander ~/Projects/gconf $ ./setkey test2
Working with key "/extra/test/key"
Value "test2" set
Now you might want to alter reader.c and compile it again so it reads out this new key instead of the one we used before.
Using change notifications
At last we'll be using one of the more advanced features of GConf, change notifications. This means we can register a callback to a key change, so when the key's value is changed (in-process or by another process) we'll be notified of this.
One again this is very easy: get a GConfClient proxy, figure out to which key you want to subscribe, find out the container directory, tell gconfd you want to register to that "directory", add a key change event handler, and you're done. This might sound confusing, read the code sample to see how easy it is.
/* GConf test listener
* Compile using
* gcc `pkg-config --libs --cflags gconf-2.0` -o listener listener.c
*/
#include <glib.h>
#include <gconf/gconf-client.h>
/* The GConf PATH we'll want to monitor */
#define GCPATH "/extra/test"
/* Path to the key we'll be working with */
#define GCKEY "/extra/test/key"
/* This is the callback function for GCKEY changes */
void key_changed_cb(GConfClient* client, guint cnxn_id, GConfEntry *entry, gpointer user_data) {
/* Store the new key value */
gchar *val = NULL;
/* A local GConfValue */
GConfValue *value = NULL;
/* Get the value from the provided entry */
value = gconf_entry_get_value(entry);
/* This should not happen, but one never knows */
if(value == NULL) {
val = g_strdup("<unset>");
}
else {
/* Check value type, we want a string */
if(value->type == GCONF_VALUE_STRING) {
/* Get the key value
* We use g_strdup because we'll g_free(val) later */
val = g_strdup(gconf_value_get_string(value));
}
else {
val = g_strdup("<wrong type>");
}
}
g_assert(val != NULL);
g_print("Value changed: \"%s\"\n", val);
g_free(val);
}
gint main(gint argc, gchar *argv[]) {
/* Our proxy */
GConfClient *gclient = NULL;
/* String to store the GCKEY's value in */
gchar *val = NULL;
/* A simple mainloop */
GMainLoop *loop = NULL;
/* Initialize the GConf subsystem */
gconf_init(argc, argv, NULL);
g_print("Working with key \"%s\"\n", "" GCKEY);
/* Get the default GConf proxy */
gclient = gconf_client_get_default();
g_assert(gclient != NULL);
/* Get the value of GCKEY */
val = gconf_client_get_string(gclient, "" GCKEY, NULL);
if(val == NULL) {
/* Key was not set before */
val = g_strdup("<unset>");
}
g_assert(val != NULL);
g_print("Initial value: \"%s\"\n", val);
g_free(val);
/* Watch GCPATH */
gconf_client_add_dir(gclient, "" GCPATH, GCONF_CLIENT_PRELOAD_NONE, NULL);
/* And specify a callback function for GCKEY changes */
gconf_client_notify_add(gclient, "" GCKEY, key_changed_cb, NULL, NULL, NULL);
/* Create a mainloop and run it */
loop = g_main_loop_new(NULL, FALSE);
g_assert(loop != NULL);
g_main_loop_run(loop);
return 0;
}
50% of this code is equal to "reader.c". The only "special" things is adding a callback notifier in main(), and the callback function itself.
You should notice GConfValues got a type: in this sample we check whether the key's type equals to "GCONF_VALUE_STRING", otherwise we don't "process" the value.
Once more, compile and run this code:
ikke@marslander ~/Projects/gconf $ gcc `pkg-config --libs --cflags gconf-2.0` -o listener listener.c
ikke@marslander ~/Projects/gconf $ ./listener
Working with key "/extra/test/key"
Initial value: "test2"
At this point, the program just hangs due to our GMainLoop. The key value is the one last set with ./setkey
Now open a new console, go to the directory where setkey is located, and execute it giving some new value. Then watch what's going on at the terminal where "listener" is running:
ikke@marslander ~/Projects/gconf $ ./setkey "a longer test key"
Working with key "/extra/test/key"
Value "a longer test key" set
In the "listener" console, we see this:
Value changed: "a longer test key"
Jay, change notifications working fine :-)
In this sample we only worked with string values, GConf also supports floats, integers, booleans, lists and even more. Check out the API docs for more information.
If you want to read more about GConf, e.g. its internals, you might want to check this tutorial by the GConf author, Havoc Pennington. Notice this is an old document, so the code samples in there won't work anymore, the GConf API has changed quite a lot since then. The ideas explained in the article are still relevant though, so it's worth reading.