| <chapter> |
| <title>Migrating from GConf to GSettings</title> |
| |
| <section> |
| <title>Before you start</title> |
| |
| <para> |
| Converting individual applications and their settings from GConf to |
| GSettings can be done at will. But desktop-wide settings like font or |
| theme settings often have consumers in multiple modules. Therefore, |
| some consideration has to go into making sure that all users of a setting |
| are converted to GSettings at the same time or that the program |
| responsible for configuring that setting continues to update the value in |
| both places. |
| </para> |
| <para> |
| It is always a good idea to have a look at how others have handled |
| similar problems before. |
| </para> |
| </section> |
| |
| <section> |
| <title>Conceptual differences</title> |
| |
| <para> |
| Conceptually, GConf and GSettings are fairly similar. Both |
| have a concept of pluggable backends. Both keep information |
| about keys and their types in schemas. Both have a concept of |
| mandatory values, which lets you implement lock-down. |
| </para> |
| <para> |
| There are some differences in the approach to schemas. GConf |
| installs the schemas into the database and has API to handle |
| schema information (gconf_client_get_default_from_schema(), |
| gconf_value_get_schema(), etc). GSettings on the other hand |
| assumes that an application knows its own schemas, and does |
| not provide API to handle schema information at runtime. |
| GSettings is also more strict about requiring a schema whenever |
| you want to read or write a key. To deal with more free-form |
| information that would appear in schema-less entries in GConf, |
| GSettings allows for schemas to be 'relocatable'. |
| </para> |
| <para> |
| One difference in the way applications interact with their |
| settings is that with GConf you interact with a tree of |
| settings (ie the keys you pass to functions when reading |
| or writing values are actually paths with the actual name |
| of the key as the last element. With GSettings, you create |
| a GSettings object which has an implicit prefix that determines |
| where the settings get stored in the global tree of settings, |
| but the keys you pass when reading or writing values are just |
| the key names, not the full path. |
| </para> |
| </section> |
| |
| <section> |
| <title>GConfClient (and GConfBridge) API conversion</title> |
| |
| <para> |
| Most people use GConf via the high-level #GConfClient API. |
| The corresponding API is the #GSettings object. While not |
| every GConfClient function has a direct GSettings equivalent, |
| many do: |
| <table id="gconf-client-vs-gsettings"> |
| <tgroup cols="2"> |
| <thead> |
| <row><entry>GConfClient</entry><entry>GSettings</entry></row> |
| </thead> |
| <tbody> |
| <row><entry>gconf_client_get_default()</entry><entry>no direct equivalent, |
| instead you call g_settings_new() for the schemas you use</entry></row> |
| <row><entry>gconf_client_set()</entry><entry>g_settings_set()</entry></row> |
| <row><entry>gconf_client_get()</entry><entry>g_settings_get()</entry></row> |
| <row><entry>gconf_client_get_bool()</entry><entry>g_settings_get_boolean()</entry></row> |
| <row><entry>gconf_client_set_bool()</entry><entry>g_settings_set_boolean()</entry></row> |
| <row><entry>gconf_client_get_int()</entry><entry>g_settings_get_int()</entry></row> |
| <row><entry>gconf_client_set_int()</entry><entry>g_settings_set_int()</entry></row> |
| <row><entry>gconf_client_get_float()</entry><entry>g_settings_get_double()</entry></row> |
| <row><entry>gconf_client_set_float()</entry><entry>g_settings_set_double()</entry></row> |
| <row><entry>gconf_client_get_string()</entry><entry>g_settings_get_string()</entry></row> |
| <row><entry>gconf_client_set_string()</entry><entry>g_settings_set_string()</entry></row> |
| <row><entry>gconf_client_get_list()</entry><entry>for string lists, see g_settings_get_strv(), else see g_settings_get_value() and #GVariant API</entry></row> |
| <row><entry>gconf_client_set_list()</entry><entry>for string lists, see g_settings_set_strv(), else see g_settings_set_value() and #GVariant API</entry></row> |
| <row><entry>gconf_entry_get_is_writable()</entry><entry>g_settings_is_writable()</entry></row> |
| <row><entry>gconf_client_notify_add()</entry><entry>not required, the #GSettings::changed signal is emitted automatically</entry></row> |
| <row><entry>gconf_client_add_dir()</entry><entry>not required, each GSettings instance automatically watches all keys in its path</entry></row> |
| <row><entry>#GConfChangeSet</entry><entry>g_settings_delay(), g_settings_apply()</entry></row> |
| <row><entry>gconf_client_get_default_from_schema()</entry><entry>no equivalent, applications are expected to know their schema</entry></row> |
| <row><entry>gconf_client_all_entries()</entry><entry>no equivalent, applications are expected to know their schema, and GSettings does not allow schema-less entries</entry></row> |
| <row><entry>gconf_client_get_without_default()</entry><entry>no equivalent</entry></row> |
| <row><entry>gconf_bridge_bind_property()</entry><entry>g_settings_bind()</entry></row> |
| <row><entry>gconf_bridge_bind_property_full()</entry><entry>g_settings_bind_with_mapping()</entry></row> |
| </tbody> |
| </tgroup> |
| </table> |
| </para> |
| <para> |
| GConfBridge was a third-party library that used GConf to bind an object property |
| to a particular configuration key. GSettings offers this service itself. |
| </para> |
| <para> |
| There is a pattern that is sometimes used for GConf, where a setting can have |
| explicit 'value A', explicit 'value B' or 'use the system default'. With GConf, |
| 'use the system default' is sometimes implemented by unsetting the user value. |
| </para> |
| <para> |
| This is not possible in GSettings, since it does not have API to determine if a value |
| is the default and does not let you unset values. The recommended way (and much |
| clearer) way in which this can be implemented in GSettings is to have a separate |
| 'use-system-default' boolean setting. |
| </para> |
| </section> |
| |
| <section> |
| <title>Change notification</title> |
| |
| <para> |
| GConf requires you to call gconf_client_add_dir() and |
| gconf_client_notify_add() to get change notification. With |
| GSettings, this is not necessary; signals get emitted automatically |
| for every change. |
| </para> |
| <para> |
| The #GSettings::changed signal is emitted for each changed key. |
| There is also a #GSettings::change-event signal that you can handle |
| if you need to see groups of keys that get changed at the same time. |
| </para> |
| <para> |
| GSettings also notifies you about changes in writability of keys, |
| with the #GSettings::writable-changed signal (and the |
| #GSettings::writable-change-event signal). |
| </para> |
| </section> |
| |
| <section><title>Change sets</title> |
| <para> |
| GConf has a a concept of a set of changes which can be applied or reverted |
| at once: #GConfChangeSet (GConf doesn't actually apply changes atomically, |
| which is one of its shortcomings). |
| </para> |
| <para> |
| Instead of a separate object to represent a change set, GSettings has a |
| 'delayed-apply' mode, which can be turned on for a GSettings object by |
| calling g_settings_delay(). In this mode, changes done to the GSettings |
| object are not applied - they are still visible when calling g_settings_get() |
| <emphasis>on the same object</emphasis>, but not to other GSettings instances |
| or even other processes. |
| </para> |
| <para> |
| To apply the pending changes all at once (GSettings <emphasis>does</emphasis> |
| atomicity here), call g_settings_apply(). To revert the pending changes, |
| call g_settings_revert() or just drop the reference to the #GSettings object. |
| </para> |
| </section> |
| |
| <section> |
| <title>Schema conversion</title> |
| |
| <para> |
| If you are porting your application from GConf, most likely you already |
| have a GConf schema. GConf comes with a commandline tool |
| gsettings-schema-convert that can help with the task of converting |
| a GConf schema into an equivalent GSettings schema. The tool is not |
| perfect and may need assistence in some cases. |
| </para> |
| <example><title>An example for using gsettings-schema-convert</title> |
| <para>Running <userinput>gsettings-schema-convert --gconf --xml --schema-id "org.gnome.font-rendering" --output org.gnome.font-rendering.gschema.xml destop_gnome_font_rendering.schemas</userinput> on the following <filename>desktop_gnome_font_rendering.schemas</filename> file: |
| <programlisting> |
| <![CDATA[ |
| <?xml version="1.0"?> |
| <gconfschemafile> |
| <schemalist> |
| <schema> |
| <key>/schemas/desktop/gnome/font_rendering/dpi</key> |
| <applyto>/desktop/gnome/font_rendering/dpi</applyto> |
| <owner>gnome</owner> |
| <type>int</type> |
| <default>96</default> |
| <locale name="C"> |
| <short>DPI</short> |
| <long>The resolution used for converting font sizes to pixel sizes, in dots per inch.</long> |
| </locale> |
| </schema> |
| </schemalist> |
| </gconfschemafile> |
| ]]> |
| </programlisting> |
| produces a <filename>org.gnome.font-rendering.gschema.xml</filename> file with the following content: |
| <programlisting> |
| <![CDATA[ |
| <schemalist> |
| <schema id="org.gnome.font-rendering" path="/desktop/gnome/font_rendering/"> |
| <key name="dpi" type="i"> |
| <default>96</default> |
| <summary>DPI</summary> |
| <description>The resolution used for converting font sizes to pixel sizes, in dots per inch.</description> |
| </key> |
| </schema> |
| </schemalist> |
| ]]> |
| </programlisting> |
| </para> |
| </example> |
| |
| <para> |
| GSettings schemas are identified at runtime by their id (as specified |
| in the XML source file). It is recommended to use a dotted name as schema |
| id, similar in style to a D-Bus bus name, e.g. "org.gnome.SessionManager". |
| In cases where the settings are general and not specific to one application, |
| the id should not use StudlyCaps, e.g. "org.gnome.font-rendering". |
| The filename used for the XML schema source is immaterial, but |
| schema compiler expects the files to have the extension |
| <filename>.gschema.xml</filename>. It is recommended to simply |
| use the schema id as the filename, followed by this extension, |
| e.g. <filename>org.gnome.SessionManager.gschema.xml</filename>. |
| </para> |
| |
| <para> |
| The XML source file for your GSettings schema needs to get installed |
| into <filename>$datadir/glib-2.0/schemas</filename>, and needs to be |
| compiled into a binary form. At runtime, GSettings looks for compiled |
| schemas in the <filename>glib-2.0/schemas</filename> subdirectories |
| of all <envar>XDG_DATA_DIRS</envar> directories, so if you install |
| your schema in a different location, you need to set the |
| <envar>XDG_DATA_DIRS</envar> environment variable appropriately. |
| </para> |
| <para> |
| Schemas are compiled into binary form by the |
| <link linkend="glib-compile-schemas">glib-compile-schemas</link> utility. |
| GIO provides a <literal>glib_compile_schemas</literal> |
| variable for the schema compiler. |
| </para> |
| <para> |
| You can ignore all of this by using the provided m4 macros. To |
| do this, add to your <filename>configure.ac</filename>: |
| <programlisting> |
| GLIB_GSETTINGS |
| </programlisting> |
| The corresponding <filename>Makefile.am</filename> fragment looks like |
| this: |
| <programlisting> |
| # gsettings_SCHEMAS is a list of all the schemas you want to install |
| gsettings_SCHEMAS = my.app.gschema.xml |
| |
| # include the appropriate makefile rules for schema handling |
| @GSETTINGS_RULES@ |
| </programlisting> |
| </para> |
| |
| <para> |
| This is not sufficient on its own. You need to mention what the source |
| of the <filename>my.app.gschema.xml</filename> file is. If the schema |
| file is distributed directly with your project's tarball then a mention |
| in <varname>EXTRA_DIST</varname> is appropriate. If the schema file is |
| generated from another source then you will need the appropriate rule |
| for that, plus probably an item in <varname>EXTRA_DIST</varname> for the |
| source files used by that rule. |
| </para> |
| |
| <para> |
| One possible pitfall in doing schema conversion is that the default |
| values in GSettings schemas are parsed by the #GVariant parser. |
| This means that strings need to include quotes in the XML. Also note |
| that the types are now specified as #GVariant type strings. |
| <programlisting> |
| <![CDATA[ |
| <type>string</type> |
| <default>rgb</default> |
| ]]> |
| </programlisting> |
| becomes |
| <programlisting> |
| <![CDATA[ |
| <key name="rgba-order" type="s"> |
| <default>'rgb'</default> <!-- note quotes --> |
| </key> |
| ]]> |
| </programlisting> |
| </para> |
| <para> |
| Another possible complication is that GConf specifies full paths |
| for each key, while a GSettings schema has a 'path' attribute that |
| contains the prefix for all the keys in the schema, and individual |
| keys just have a simple name. So |
| <programlisting> |
| <![CDATA[ |
| <key>/schemas/desktop/gnome/font_rendering/antialiasing</key> |
| ]]> |
| </programlisting> |
| becomes |
| <programlisting> |
| <![CDATA[ |
| <schema id="org.gnome.font" path="/desktop/gnome/font_rendering/"> |
| <key name="antialiasing" type="s"> |
| ]]> |
| </programlisting> |
| </para> |
| <para> |
| Default values can be localized in both GConf and GSettings schemas, |
| but GSettings uses gettext for the localization. You can specify |
| the gettext domain to use in the <tag class="attribute">gettext-domain</tag> |
| attribute. Therefore, when converting localized defaults in GConf, |
| <programlisting> |
| <![CDATA[ |
| <key>/schemas/apps/my_app/font_size</key> |
| <locale name="C"> |
| <default>18</default> |
| </locale> |
| <locale name="be"> |
| <default>24</default> |
| </locale> |
| </key> |
| ]]> |
| </programlisting> |
| becomes |
| <programlisting> |
| <![CDATA[ |
| <schema id="..." gettext-domain="your-domain"> |
| ... |
| <key name="font-size" type="i"> |
| <default l10n="messages" context="font_size">18</default> |
| </key> |
| ]]> |
| </programlisting> |
| </para> |
| <para> |
| GSettings uses gettext for translation of default values. |
| The string that is translated is exactly the string that appears |
| inside of the <tag class='starttag'>default</tag> element. This |
| includes the quotation marks that appear around strings. |
| Default values must be marked with the <varname>l10n</varname> |
| attribute in the <tag class='starttag'>default</tag> tag, which |
| should be set as equal to <literal>'messages'</literal> or |
| <literal>'time'</literal> depending on the desired category. An |
| optional translation context can also be specified with the |
| <varname>context</varname> attribute, as in the example. This |
| is usually recommended, since the string "<literal>18</literal>" |
| is not particularly easy to translate without context. The |
| translated version of the default value should be stored in the |
| specified <varname>gettext-domain</varname>. Care must be taken |
| during translation to ensure that all translated values remain |
| syntactically valid; mistakes here will cause runtime errors. |
| </para> |
| <para> |
| GSettings schemas have optional <tag class="starttag">summary</tag> and |
| <tag class="starttag">description</tag> elements for each key which |
| correspond to the <tag class="starttag">short</tag> and |
| <tag class="starttag">long</tag> elements in the GConf schema and |
| will be used in similar ways by a future gsettings-editor, so you |
| should use the same conventions for them: The summary is just a short |
| label with no punctuation, the description can be one or more complete |
| sentences. If multiple paragraphs are desired for the description, the |
| paragraphs should be separated by a completely empty line. |
| </para> |
| <para> |
| Translations for these strings will also be handled |
| via gettext, so you should arrange for these strings to be |
| extracted into your gettext catalog. One way to do that is to use |
| intltool. Since intltool 0.50.1, schema files are |
| supported, so all you have to do is to add your .gschema.xml |
| files to <filename>POTFILES.in</filename> with a line like |
| <programlisting> |
| [type: gettext/gsettings]data/org.foo.MyApp.gschema.xml |
| </programlisting> |
| </para> |
| <para> |
| GSettings is a bit more restrictive about key names than GConf. Key |
| names in GSettings can be at most 32 characters long, and must only |
| consist of lowercase characters, numbers and dashes, with no |
| consecutive dashes. The first character must not be a number or dash, |
| and the last character cannot be '-'. |
| </para> |
| <para> |
| If you are using the GConf backend for GSettings during the |
| transition, you may want to keep your key names the same they |
| were in GConf, so that existing settings in the users GConf |
| database are preserved. You can achieve this by using the |
| <option>--allow-any-name</option> with the |
| <link linkend="glib-compile-schemas">glib-compile-schemas</link> schema |
| compiler. Note that this option is only meant |
| to ease the process of porting your application, allowing parts |
| of your application to continue to access GConf and parts to use |
| GSettings. By the time you have finished porting your application |
| you must ensure that all key names are valid. |
| </para> |
| </section> |
| |
| <section><title>Data conversion</title> |
| <para> |
| GConf comes with a GSettings backend that can be used to |
| facility the transition to the GSettings API until you are |
| ready to make the jump to a different backend (most likely |
| dconf). To use it, you need to set the <envar>GSETTINGS_BACKEND</envar> |
| to 'gconf', e.g. by using |
| <programlisting> |
| g_setenv ("GSETTINGS_BACKEND", "gconf", TRUE); |
| </programlisting> |
| early on in your program. Note that this backend is meant purely |
| as a transition tool, and should not be used in production. |
| </para> |
| <para> |
| GConf also comes with a utility called |
| <command>gsettings-data-convert</command>, which is designed to help |
| with the task of migrating user settings from GConf into another |
| GSettings backend. It can be run manually, but it is designed to be |
| executed automatically, every time a user logs in. It keeps track of |
| the data migrations that it has already done, and it is harmless to |
| run it more than once. |
| </para> |
| <para> |
| To make use of this utility, you must install a keyfile in the |
| directory <filename>/usr/share/GConf/gsettings</filename> which |
| lists the GSettings keys and GConf paths to map to each other, for |
| each schema that you want to migrate user data for. |
| </para> |
| <para> |
| Here is an example: |
| <programlisting> |
| <![CDATA[ |
| [org.gnome.fonts] |
| antialiasing = /desktop/gnome/font_rendering/antialiasing |
| dpi = /desktop/gnome/font_rendering/dpi |
| hinting = /desktop/gnome/font_rendering/hinting |
| rgba-order = /desktop/gnome/font_rendering/rgba_order |
| |
| [apps.myapp:/path/to/myapps/] |
| some-odd-key1 = /apps/myapp/some_ODD-key1 |
| ]]> |
| </programlisting> |
| The last key demonstrates that it may be necessary to modify the key |
| name to comply with stricter GSettings key name rules. Of course, |
| that means your application must use the new key names when looking |
| up settings in GSettings. |
| </para> |
| <para> |
| The last group in the example also shows how to handle the case |
| of 'relocatable' schemas, which don't have a fixed path. You can |
| specify the path to use in the group name, separated by a colon. |
| </para> |
| <para> |
| There are some limitations: <command>gsettings-data-convert</command> |
| does not do any transformation of the values. And it does not handle |
| complex GConf types other than lists of strings or integers. |
| </para> |
| <para> |
| Don't forget to require GConf 2.31.1 or newer in your configure |
| script if you are making use of the GConf backend or the conversion |
| utility. |
| </para> |
| |
| <para> |
| If, as an application developer, you are interested in manually |
| ensuring that <command>gsettings-data-convert</command> has been |
| invoked (for example, to deal with the case where the user is |
| logged in during a distribution upgrade or for non-XDG desktop |
| environments which do not run the command as an autostart) you |
| may invoke it manually during your program initialisation. This |
| is not recommended for all application authors -- it is your |
| choice if this use case concerns you enough. |
| </para> |
| <para> |
| Internally, <command>gsettings-data-convert</command> uses a |
| keyfile to track which settings have been migrated. The |
| following code fragment will check that keyfile to see if your |
| data conversion script has been run yet and, if not, will |
| attempt to invoke the tool to run it. You should adapt it to |
| your application as you see fit. |
| </para> |
| <para> |
| <programlisting> |
| <![CDATA[ |
| static void |
| ensure_migrated (const gchar *name) |
| { |
| gboolean needed = TRUE; |
| GKeyFile *kf; |
| gchar **list; |
| gsize i, n; |
| |
| kf = g_key_file_new (); |
| |
| g_key_file_load_from_data_dirs (kf, "gsettings-data-convert", |
| NULL, G_KEY_FILE_NONE, NULL); |
| list = g_key_file_get_string_list (kf, "State", "converted", &n, NULL); |
| |
| if (list) |
| { |
| for (i = 0; i < n; i++) |
| if (strcmp (list[i], name) == 0) |
| { |
| needed = FALSE; |
| break; |
| } |
| |
| g_strfreev (list); |
| } |
| |
| g_key_file_free (kf); |
| |
| if (needed) |
| g_spawn_command_line_sync ("gsettings-data-convert", |
| NULL, NULL, NULL, NULL); |
| } |
| |
| ]]> |
| </programlisting> |
| </para> |
| <para> |
| Although there is the possibility that the |
| <command>gsettings-data-convert</command> script will end up |
| running multiple times concurrently with this approach, it is |
| believed that this is safe. |
| </para> |
| </section> |
| </chapter> |