GConf Configuration System Concepts for Developers

Natan Yellin provides an overview for GNOME developers on how the GConf Configuation System works and how it can be used in developing GNOME applications.

Introduction

The GConf Configuration System is a system used by the GNOME desktop to handle user preferences. The motivation to use GConf is to provide system administrators more manageability when deploying desktops and to provide application developers a centralized location for user preferences. This article will explain the fundamental concepts of how GConf works and will demonstrate how developers can use GConf in their applications. The examples in this article are all written in C, however it can be accessed with any supported language that we use in GNOME like C++, Perl, Java, and Python.

Before we begin, there are a few details about GConf that are worth noting:

  • GConf should not be used for crucial application settings (e.g. an address book’s contacts). It should only be used for user preferences ( e.g. the address book’s font).
  • The GConf database is not guaranteed to be constantly running. As we’ll see later on, values fetched from GConf should be checked to make sure that they exist.
  • GConf values are (by definition) user writable. Applications should verify data stored in GConf when necessary.
  • GConf applies settings instantly. In other words, when a setting changes, the change applies immediately without waiting for the user to press an “apply” button.
  • GConfClient wraps the GConfEngine functions in a friendlier object that descends from GObject. For the most part, you won’t want to use GConfEngine directly.

GConf settings are stored in a key-value format. The database itself is stored in set of xml files located under ~/.gconf. You should never read or write to these files directly. If you’d like to manually edit the database without using the GConf API from a program, you can do so graphically using the gconf-editor application. You can also get and set keys from the command line with the gconftool-2 command line tool.

Create a new Gconf Client

Lets begin by looking at some some code to create a new GConfClient:

GConfClient* gconf_client_get_default (void);

The returned engine has a reference count of 1. It should be unreferenced when you’re done using it with g_object_unref().

We can set a key with the following functions:

gboolean gconf_client_set_float (GConfClient *client,
const gchar *key,
gdouble val,
GError **err);

  • client is the GConfClient that we created earlier.
  • key is the name of the key to set.
  • val is the value to set for key.
  • err is the return location for an error or NULL to ignore errors.

Similar functions exist for manipulating integers, booleans, and strings.

gboolean gconf_client_set_int (GConfClient *client,
const gchar *key,
gint val,
GError **err);

gboolean gconf_client_set_bool (GConfClient *client,
const gchar *key,
gboolean val,
GError **err);

gboolean gconf_client_set_string (GConfClient *client,
const gchar *key,
const gchar *val,
GError **err);

There are three additional functions for setting schemas, lists, and pairs. They’re a bit more complicated and much less common, so we’ll leave them for another time.

To get a key’s value use gchar * gconf_client_get_string (GConfClient *client, const gchar *key, GError **err) or the corresponding function for any of the other data types.

The following example is heavily commented, and will explain the bare basics of gconf:

#include <gconf/gconf-client.h>

#include <gtk/gtk.h> // this callback is called whenever the button is pressed void pressed_cb (GtkWidget *widget, GConfClient *config) {

  // gconf_client_get_int () returns 0 if the key does not exist. In
this case, we don't need to bother checking for errors.
  gint clicks = gconf_client_get_int (config, "/apps/gconf-test/clicks", NULL);
  clicks++;
  // set the value so that it will exist next time this function is called
  gconf_client_set_int (config, "/apps/gconf-test/clicks", clicks, NULL);
  g_print ("%d\n", clicks);
}
int main (int argc, char **argv)
{
  GtkWidget *window;
  GtkWidget *button;
  GConfClient *config;
  // common gtk init
  gtk_init (&argc, &argv);
  // create the GConfClient
  config = gconf_client_get_default ();
  // create the window and make it quit when we want it to
  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK
(gtk_main_quit), NULL);
  // and the button...
  button = gtk_button_new_with_label ("Press me");
  // when it is pressed call our callback
  g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK
(pressed_cb), config);
  gtk_container_add (GTK_CONTAINER (window), button);
  // some more common stuff
  gtk_widget_show_all (window);
  gtk_main ();
  return 0;
}

Compile

You can compile the file with:

gcc -Wall -g gconf-test.c -o gconf-test `pkg-config --cflags gtk+-2.0 gconf-2.0` \ `pkg-config --libs gtk+-2.0 gconf-2.0`

Test it out and play around with it. You’ll notice that the program will “remember” the number of times that it was clicked the last time that it was run.

Associate a GObject property

When you want to associate a particular GObject property (e.g. a window’s opacity) with a gconf key, then things begin to get complicated. The usual method of doing so is as follows:

1) The application starts up and sets the default value from GConf.
2) The application tells gconf that it wants to be notified when the key changes.
3) The user opens the app ’s preferences window and changes a key.
4) A callback gets called to set the GConf value.
5) Setting the GConf value triggers another callback that reads in the key and applies the setting.

For example:


  /* At application startup */
  gconf_client_notify_add(client, key,
update_application_to_reflect_setting, data);
  set_initial_app_state(client);

  /* From preferences dialog */
  gconf_client_set(client, key, value);

However, you should never do the following: gconf_client_set(client, key, value); application_update_to_reflect_setting();

That will break if the user changes a GConf key from out outside of your app.

There are two important functions for notifications. The first is:

void gconf_client_add_dir (GConfClient *client,
const gchar *dir,
GConfClientPreloadType preload,
GError **err);

  • dir should be the directory to watch (e.g. ”/apps/gconf-test”).

GConf can preload the values before you try to read them in order to save time. preload should be either GCONF_CLIENT_PRELOAD_NONE, GCONF_CLIENT_PRELOAD_ONELEVEL, or GCONF_CLIENT_PRELOAD_RECURSIVE.

Once we have added a directory, we can tell GConf which specific keys we want to monitor with:

 guint gconf_client_notify_add (GConfClient *client, const gchar *namespace_section, GConfClientNotifyFunc func, gpointer user_data, GFreeFunc destroy_notify, GError **err); 
  • client is a GConfClient.
  • namespace_section is a directory or key to listen to for changes.
  • func is the function to call when changes occur.
  • user_data is the data to pass to func.
  • destroy_notify is the function to call on user_data when the notify is removed or the GConfClient is destroyed, or NULL for none.
  • err is the return location for an allocated GError, or NULL to ignore errors.

The function return a connection id that can be used to remove the connection.

Finally, lets look at the GConfClientNotifyFunc function signature:

void (*GConfClientNotifyFunc) (GConfClient *client,
guint cnxn_id,
GConfEntry *entry,
gpointer user_data);

  • client is the GConfClient notifying us.
  • cnxn_id is connection ID from gconf_client_notify_add().
  • entry is a GConfEntry.
  • user_data is the user data that we specified with gconf_client_notify_add().

This can get pretty lengthy when you have a few keys to read on startup and then monitor for changes. If you’re going to use GConf for more than one or two prefereces, it’s recommended that you use a wrapper for GConf to reduce your application’s boilerplate code. libgconf-bridge is the most common library that is used to do so. (Note that libgconf-bridge may not be installed on all distributions.)

libgconf-bridge example

Lets take a look at an example with libgconf-bridge. As you’ll notice, by using the gconf_bridge_bind_property function, we can make the code more compact:

#include <gconf/gconf-client.h>

#include <gtk/gtk.h>

#include <libgconf-bridge/gconf-bridge.h>

int main (int argc, char **argv) {

 GtkWidget *window;
 GtkWidget *scale;
 gtk_init (&argc, &argv);
 window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK
(gtk_main_quit), NULL);
 gtk_window_set_default_size (GTK_WINDOW (window), 100, 30);
 scale = gtk_hscale_new_with_range (0, 1, 0.1);
 gtk_container_add (GTK_CONTAINER (window), scale);
 gconf_bridge_bind_property (gconf_bridge_get (),
   "/apps/gconf-test/scale",
   G_OBJECT (gtk_range_get_adjustment (GTK_RANGE (scale))), "value");
 gtk_widget_show_all (window);
 gtk_main ();
 return 0;
}

Compile the example (you need to add libgconf-bridge to pkg-config) and watch the value in gconf-editor as you move the slider around. As you’ll notice, the gconf_bridge_bind_property function “binds” a GConf key to a GObject property and automatically updates the key when the property changes.

Schemas

The last topic that we’ll cover is schemas. Schemas are xml files that are installed for all users and contain the default key:value properties for an app. When the app is run for the first time, gconf will set the default values from the schema. Here’s an example schema file:

 <?xml version="1.0" ?> <gconfschemafile>
 <schemalist>
   <schema>
     <key>/schemas/apps/avant-window-navigator/applets/awn-terminal/opacity</key>
     <applyto>/apps/avant-window-navigator/applets/awn-terminal/opacity</applyto>
     <owner>avant-window-navigator</owner>
     <type>float</type>
     < default>0.5</default>
     <locale name="C">
       <short>opacity</short>
       <long>The applet 's opacity level.</long>
     </locale>
   </schema>
   <schema>
     <key>/schemas/apps/avant-window-navigator/applets/awn-terminal/bg_img</key>
     <applyto>/apps/avant-window-navigator/applets/awn-terminal/bg_img</applyto>
     <owner>avant-window-navigator</owner>
     <type>string</type>
     <default>""</default>
     <locale name= "C">
       <short>Background</short>
       <long>The applet's background image.</long>
     </locale>
   </schema>
 </schemalist>
</gconfschemafile>

All of the keys and their values are contained inside and . Each individual key is defined by . Under , is the location of the actual schema and is the location of the key that the schema will set. is the app’s name, is the key’s type, and is the key’s default value. is used to provide a and description of the key’s purpose.

To install a schema with autotools, you need to add something like the following to your Makefile.am:

 schemasdir = GCONF_SCHEMA_FILE_DIR schemas_FILE = awn-terminal.schemas schemas_in_files = $(schemas_FILE).in schemas_DATA = $(schemas_in_files:.schemas.in=.schemas)

INTLTOOL_SCHEMAS_RULE

if GCONF_SCHEMAS_INSTALL install-data-local: $(schemas_DATA) GCONF_CONFIG_SOURCE=$(GCONF_SCHEMA_CONFIG_SOURCE) $(GCONFTOOL)—makefile-install-rule $(schemas_DATA) endif

Conclusion

That’s really all that there is to it. Now go out and add some preferences to your application!

About the Author

Natan Yellin is a high school student in Ra’anana, Israel. He first became involved with GNOME development through Google’s Highly Open Participation contest in 2007. Since then, he helped start GNOME Zeitgeist and several other open source projects. When he’s not coding an open source project or doing freelance development, Natan can be found at his school’s robotics lab, with friends, or practicing one of several styles of Karate. He usually tries to blog about what he’s working on at http://theesylum.com/.

Discuss this story with other readers on the GNOME forums.

Advertisements

Posted on April 30, 2009, in April 2009. Bookmark the permalink. Leave a comment.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: