/** * @file mce-setting.c * Runtime setting handling code for the Mode Control Entity *

* Copyright © 2004-2009 Nokia Corporation and/or its subsidiary(-ies). * Copyright © 2012-2016 Jolla Ltd. *

* @author David Weinehall * @author Simo Piiroinen * * mce is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * mce is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with mce. If not, see . */ #include "mce-setting.h" #include "mce-log.h" #include /** Pointer to the GConf client */ static GConfClient *gconf_client = NULL; /** Is GConf disabled on purpose */ static gboolean gconf_disabled = FALSE; /** List of GConf notifiers */ static GSList *gconf_notifiers = NULL; /** Check if gconf-key exists * * @param key Name of value * * @return TRUE if value exits, FALSE otherwise */ gboolean mce_setting_has_key(const gchar *const key) { gboolean res = FALSE; GConfValue *val = 0; GError *err = 0; if( gconf_disabled ) goto EXIT; val = gconf_client_get(gconf_client, key, &err); res = (val != 0); EXIT: g_clear_error(&err); gconf_value_free(val); return res; } /**Set an boolean GConf key to the specified value * * @param key The GConf key to set the value of * @param value The value to set the key to * * @return TRUE on success, FALSE on failure */ gboolean mce_setting_set_bool(const gchar *const key, const gboolean value) { gboolean status = FALSE; if( gconf_disabled ) { mce_log(LL_DEBUG, "blocked %s = %d", key, value); goto EXIT; } if( !gconf_client_set_bool(gconf_client, key, value, NULL) ) { mce_log(LL_WARN, "Failed to write %s to GConf", key); goto EXIT; } /* synchronise if possible, ignore errors */ gconf_client_suggest_sync(gconf_client, NULL); status = TRUE; EXIT: return status; } /** * Set an integer GConf key to the specified value * * @param key The GConf key to set the value of * @param value The value to set the key to * @return TRUE on success, FALSE on failure */ gboolean mce_setting_set_int(const gchar *const key, const gint value) { gboolean status = FALSE; if( gconf_disabled ) { mce_log(LL_DEBUG, "blocked %s = %d", key, value); goto EXIT; } if (gconf_client_set_int(gconf_client, key, value, NULL) == FALSE) { mce_log(LL_WARN, "Failed to write %s to GConf", key); goto EXIT; } /* synchronise if possible, ignore errors */ gconf_client_suggest_sync(gconf_client, NULL); status = TRUE; EXIT: return status; } /** * Set an string GConf key to the specified value * * @param key The GConf key to set the value of * @param value The value to set the key to * @return TRUE on success, FALSE on failure */ gboolean mce_setting_set_string(const gchar *const key, const gchar *const value) { gboolean status = FALSE; if( gconf_disabled ) { mce_log(LL_DEBUG, "blocked %s = \"%s\"", key, value); goto EXIT; } if (gconf_client_set_string(gconf_client, key, value, NULL) == FALSE) { mce_log(LL_WARN, "Failed to write %s to GConf", key); goto EXIT; } /* synchronise if possible, ignore errors */ gconf_client_suggest_sync(gconf_client, NULL); status = TRUE; EXIT: return status; } /** * Return a boolean from the specified GConf key * * @param key The GConf key to get the value from * @param[out] value Will contain the value on return, if successful * @return TRUE on success, FALSE on failure */ gboolean mce_setting_get_bool(const gchar *const key, gboolean *value) { gboolean status = FALSE; GError *error = NULL; GConfValue *gcv = 0; if( gconf_disabled ) { mce_log(LL_DEBUG, "blocked %s query", key); goto EXIT; } gcv = gconf_client_get(gconf_client, key, &error); if (gcv == NULL) { mce_log((error != NULL) ? LL_WARN : LL_INFO, "Could not retrieve %s from GConf; %s", key, (error != NULL) ? error->message : "Key not set"); goto EXIT; } if (gcv->type != GCONF_VALUE_BOOL) { mce_log(LL_ERR, "GConf key %s should have type: %d, but has type: %d", key, GCONF_VALUE_BOOL, gcv->type); goto EXIT; } *value = gconf_value_get_bool(gcv); status = TRUE; EXIT: if( gcv ) gconf_value_free(gcv); g_clear_error(&error); return status; } /** * Return an integer from the specified GConf key * * @param key The GConf key to get the value from * @param[out] value Will contain the value on return * @return TRUE on success, FALSE on failure */ gboolean mce_setting_get_int(const gchar *const key, gint *value) { gboolean status = FALSE; GError *error = NULL; GConfValue *gcv = 0; if( gconf_disabled ) { mce_log(LL_DEBUG, "blocked %s query", key); goto EXIT; } gcv = gconf_client_get(gconf_client, key, &error); if (gcv == NULL) { mce_log((error != NULL) ? LL_WARN : LL_INFO, "Could not retrieve %s from GConf; %s", key, (error != NULL) ? error->message : "Key not set"); goto EXIT; } if (gcv->type != GCONF_VALUE_INT) { mce_log(LL_ERR, "GConf key %s should have type: %d, but has type: %d", key, GCONF_VALUE_INT, gcv->type); goto EXIT; } *value = gconf_value_get_int(gcv); status = TRUE; EXIT: if( gcv ) gconf_value_free(gcv); g_clear_error(&error); return status; } /** * Return an integer list from the specified GConf key * * @param key The GConf key to get the values from * @param[out] values Will contain an GSList with the values on return * @return TRUE on success, FALSE on failure */ gboolean mce_setting_get_int_list(const gchar *const key, GSList **values) { gboolean status = FALSE; GError *error = NULL; GConfValue *gcv = 0; GConfValue *gcv2; GSList *list; gint i; if( gconf_disabled ) { mce_log(LL_DEBUG, "blocked %s query", key); goto EXIT; } gcv = gconf_client_get(gconf_client, key, &error); if (gcv == NULL) { mce_log((error != NULL) ? LL_WARN : LL_INFO, "Could not retrieve %s from GConf; %s", key, (error != NULL) ? error->message : "Key not set"); goto EXIT; } if ((gcv->type != GCONF_VALUE_LIST) || (gconf_value_get_list_type(gcv) != GCONF_VALUE_INT)) { mce_log(LL_ERR, "GConf key %s should have type: %d<%d>, but has type: %d<%d>", key, GCONF_VALUE_LIST, GCONF_VALUE_INT, gcv->type, gconf_value_get_list_type(gcv)); goto EXIT; } list = gconf_value_get_list(gcv); for (i = 0; (gcv2 = g_slist_nth_data(list, i)) != NULL; i++) { gint data; data = gconf_value_get_int(gcv2); /* Prepend is more efficient than append */ *values = g_slist_prepend(*values, GINT_TO_POINTER(data)); } /* Reverse the list, since we want the entries in the right order */ *values = g_slist_reverse(*values); status = TRUE; EXIT: if( gcv ) gconf_value_free(gcv); g_clear_error(&error); return status; } /** * Return an string from the specified GConf key * * @param key The GConf key to get the values from * @param[out] value Will contain a newly allocated string with the value * @return TRUE on success, FALSE on failure */ gboolean mce_setting_get_string(const gchar *const key, gchar **value) { gboolean status = FALSE; GError *error = NULL; GConfValue *gcv = 0; if( gconf_disabled ) { mce_log(LL_DEBUG, "blocked %s query", key); goto EXIT; } gcv = gconf_client_get(gconf_client, key, &error); if (gcv == NULL) { mce_log((error != NULL) ? LL_WARN : LL_INFO, "Could not retrieve %s from GConf; %s", key, (error != NULL) ? error->message : "Key not set"); goto EXIT; } if ((gcv->type != GCONF_VALUE_STRING)) { mce_log(LL_ERR, "GConf key %s should have type: %d, but has type: %d", key, GCONF_VALUE_STRING, gcv->type); goto EXIT; } *value = g_strdup(gconf_value_get_string(gcv)); status = TRUE; EXIT: if( gcv ) gconf_value_free(gcv); g_clear_error(&error); return status; } /** * Add a GConf notifier * * @param path The GConf directory to watch * @param key The GConf key to add the notifier for * @param callback The callback function * @param[out] cb_id Will contain the callback ID or zero on return * * @return TRUE on success, FALSE on failure */ gboolean mce_setting_notifier_add(const gchar *path, const gchar *key, const GConfClientNotifyFunc callback, guint *cb_id) { GError *error = NULL; gboolean status = FALSE; guint id = 0; if( gconf_disabled ) { mce_log(LL_DEBUG, "blocked %s notifier", key); /* Returning failure could result in termination * of mce process -> return bogus success if we * have disabled gconf on purpose. */ status = TRUE; goto EXIT; } gconf_client_add_dir(gconf_client, path, GCONF_CLIENT_PRELOAD_NONE, &error); if (error != NULL) { mce_log(LL_WARN, "Could not add %s to directories watched by " "GConf client setting from GConf; %s", path, error->message); goto EXIT; } id = gconf_client_notify_add(gconf_client, key, callback, NULL, NULL, &error); if (error != NULL) { mce_log(LL_WARN, "Could not register notifier for %s; %s", key, error->message); goto EXIT; } if( !id ) goto EXIT; gconf_notifiers = g_slist_prepend(gconf_notifiers, GINT_TO_POINTER(id)); status = TRUE; EXIT: g_clear_error(&error); return *cb_id = id, status; } /** * Remove a GConf notifier * * Calling with zero id is allowed and does nothing * * @param id The ID of the notifier to remove, or zero */ void mce_setting_notifier_remove(guint id) { if( gconf_disabled ) goto EXIT; if( !id ) goto EXIT; gconf_client_notify_remove(gconf_client, id); gconf_notifiers = g_slist_remove(gconf_notifiers, GINT_TO_POINTER(id)); EXIT: return; } /** Helper callback for removing GConf notifiers with g_slist_foreach * * @param cb_id The ID of the notifier to remove * @param user_data Unused */ void mce_setting_notifier_remove_cb(gpointer cb_id, gpointer user_data) { (void)user_data; mce_setting_notifier_remove(GPOINTER_TO_INT(cb_id)); } /** Helper for getting path of gconf key */ static gchar *mce_setting_get_path(const gchar *key) { gchar *res = 0; const gchar *end = strrchr(key, '/'); if( end ) res = g_strndup(key, end - key); return res; } /** Get initial value of integer setting and start tracking changes * * @param key key name * @param val where to store the initial value * @param def default value to use if getting key value fails; * or -1 to leave *val unmodified * @param cb change notification callback * @param cb_id where to store notification callback id */ void mce_setting_track_int(const gchar *key, gint *val, gint def, GConfClientNotifyFunc cb, guint *cb_id) { gchar *path = mce_setting_get_path(key); if( path && cb && cb_id ) mce_setting_notifier_add(path, key, cb, cb_id); if( !mce_setting_get_int(key, val) && def != -1 ) *val = def; g_free(path); } /** Get initial value of boolean setting and start tracking changes * * @param key key name * @param val where to store the initial value * @param def default value to use if getting key value fails; * or -1 to leave *val unmodified * @param cb change notification callback * @param cb_id where to store notification callback id */ void mce_setting_track_bool(const gchar *key, gboolean *val, gint def, GConfClientNotifyFunc cb, guint *cb_id) { gchar *path = mce_setting_get_path(key); if( path && cb && cb_id ) mce_setting_notifier_add(path, key, cb, cb_id); if( !mce_setting_get_bool(key, val) && def != -1 ) *val = (def != 0); g_free(path); } /** Get initial value of integer setting and start tracking changes * * Note: Caller must release returned string with g_free() when it * is no longer needed. * * @param key key name * @param val where to store the initial value * @param def default value to use if getting key value fails; * or NULL to leave *val unmodified * @param cb change notification callback * @param cb_id where to store notification callback id */ void mce_setting_track_string(const gchar *key, gchar **val, const gchar *def, GConfClientNotifyFunc cb, guint *cb_id) { gchar *path = mce_setting_get_path(key); if( path && cb && cb_id ) mce_setting_notifier_add(path, key, cb, cb_id); if( !mce_setting_get_string(key, val) && def != 0 ) *val = g_strdup(def); g_free(path); } /** * Init function for the mce-gconf component * * @return TRUE on success, FALSE on failure */ gboolean mce_setting_init(void) { gboolean status = FALSE; /* Get the default builtin-gconf client */ if( !(gconf_client = gconf_client_get_default()) ) { mce_log(LL_CRIT, "Could not get default builtin-gconf client"); goto EXIT; } status = TRUE; EXIT: return status; } /** * Exit function for the mce-gconf component */ void mce_setting_exit(void) { if( gconf_client ) { /* Free the list of GConf notifiers */ g_slist_foreach(gconf_notifiers, mce_setting_notifier_remove_cb, 0); gconf_notifiers = 0; /* Forget builtin-gconf client reference */ gconf_client = 0; } return; }