/** * @file builtin-gconf.c * GConf compatibility module - for dynamic mce settings *

* Copyright (C) 2012-2019 Jolla Ltd. *

* @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 . */ /* ------------------------------------------------------------------------- * * NOTE: This module implements just enough of the GConf API to allow us * to detach mce from D-Bus session bus. By no means is this even meant * to be a complete GConf replacement. * * CAVEATS include *at least*: * - the keys have no hierarchy * - anything related to directories is simply ignored * - gconf_client_get() returns the real thing, not deep copied duplicate * - pair values are not supported * - adding new values is not supported * - value types can't be changed * ------------------------------------------------------------------------- */ #include "mce-log.h" #include "mce-io.h" #include "mce-dbus.h" #include "mce-setting.h" #include "powerkey.h" #include "tklock.h" #include "event-input.h" #include "modules/memnotify.h" #include "modules/display.h" #include "modules/proximity.h" #include "modules/powersavemode.h" #include "modules/doubletap.h" #include "modules/led.h" #include "modules/inactivity.h" #include #include #include #include /* ========================================================================= * * * CONFIGURATION * * ========================================================================= */ /** Enable error logging to stderr via gconf_log_error() */ #define GCONF_ENABLE_ERROR_LOGGING 01 /** Enable debug logging to stderr via gconf_log_debug() */ #define GCONF_ENABLE_DEBUG_LOGGING 01 /** Path to persistent storage file */ #define VALUES_PATH G_STRINGIFY(MCE_VAR_DIR)"/builtin-gconf.values" /* ========================================================================= * * * MACROS * * ========================================================================= */ /** Tag unused parameters as such to avoid compilation warnings */ #define unused(variable) (void)&variable /* ========================================================================= * * * FUNCTION PROTOTYPES * * ========================================================================= */ static GQuark gconf_error_quark(void); static void gconf_set_error(GError **err, gint code, const gchar *fmt, ...); static char *gconf_strip_string(char *str); static int gconf_parse_int(const char *str); static double gconf_parse_float(const char *str); static gboolean gconf_parse_bool(const char *str); static GConfValueType gconf_parse_type(int chr); static const char *gconf_type_repr(GConfValueType type); static gboolean gconf_require_type(const char *key, const GConfValue *value, GConfValueType type, GError **err); static gboolean gconf_require_list_type(const char *key, const GConfValue *value, GConfValueType type, GError **err); gchar *gconf_concat_dir_and_key(const gchar *dir, const gchar *key); #if GCONF_ENABLE_DEBUG_LOGGING static char *gconf_value_repr(const char *key, GConfValue *self); #endif static char *gconf_value_str(GConfValue *self); static void gconf_value_unset(GConfValue *self); static gboolean gconf_value_list_validata(GSList *src, GConfValueType type); static GSList *gconf_value_list_copy(GSList *src); static GSList *gconf_value_list_free(GSList *list); static void gconf_value_set_from_string(GConfValue *self, const char *data); static GConfValue *gconf_value_init(GConfValueType type, GConfValueType list_type, const char *data); GConfValue *gconf_value_copy(const GConfValue *src); GConfValue *gconf_value_new(GConfValueType type); void gconf_value_free(GConfValue *self); gboolean gconf_value_get_bool(const GConfValue *self); bool gconf_value_set_bool(GConfValue *self, gboolean val); int gconf_value_get_int(const GConfValue *self); bool gconf_value_set_int(GConfValue *self, gint val); double gconf_value_get_float(const GConfValue *self); bool gconf_value_set_float(GConfValue *self, double val); const char *gconf_value_get_string(const GConfValue *self); bool gconf_value_set_string(GConfValue *self, const char *val); GConfValueType gconf_value_get_list_type(const GConfValue *self); void gconf_value_set_list_type(GConfValue *self, GConfValueType list_type); GSList *gconf_value_get_list(const GConfValue *self); bool gconf_value_set_list(GConfValue *self, GSList *list); static GConfEntry *gconf_entry_init(const char *key, const char *type, const char *data); static void gconf_entry_free(GConfEntry *self); static void gconf_entry_free_cb(gpointer self); const char *gconf_entry_get_key(const GConfEntry *entry); GConfValue *gconf_entry_get_value(const GConfEntry *entry); #if GCONF_ENABLE_DEBUG_LOGGING static void gconf_client_debug(GConfClient *self); #endif GConfClient *gconf_client_get_default(void); static void gconf_client_free_default(void); void gconf_client_add_dir(GConfClient *client, const gchar *dir, GConfClientPreloadType preload, GError **err); static GConfEntry *gconf_client_find_entry(GConfClient *self, const gchar *key, GError **err); static GConfValue *gconf_client_find_value(GConfClient *self, const gchar *key, GError **err); GConfValue *gconf_client_get(GConfClient *self, const gchar *key, GError **err); static void gconf_client_notify_free(GConfClientNotify *self); static void gconf_client_notify_free_cb(gpointer self); static GConfClientNotify *gconf_client_notify_new(const gchar *namespace_section, GConfClientNotifyFunc func, gpointer user_data, GFreeFunc destroy_notify); static void gconf_client_notify_change(GConfClient *client, const gchar *namespace_section); guint gconf_client_notify_add(GConfClient *client, const gchar *namespace_section, GConfClientNotifyFunc func, gpointer user_data, GFreeFunc destroy_notify, GError **err); void gconf_client_notify_remove(GConfClient *client, guint cnxn); gboolean gconf_client_set_bool(GConfClient *client, const gchar *key, gboolean val, GError **err); gboolean gconf_client_set_int(GConfClient *client, const gchar *key, gint val, GError **err); gboolean gconf_client_set_float(GConfClient *client, const gchar *key, double val, GError **err); gboolean gconf_client_set_string(GConfClient *client, const gchar *key, const gchar *val, GError **err); gboolean gconf_client_set_list(GConfClient *client, const gchar *key, GConfValueType list_type, GSList *list, GError **err); void gconf_client_suggest_sync(GConfClient *client, GError **err); /* ========================================================================= * * * MISCELLANEOUS UTILITIES * * ========================================================================= */ /* Null tolerant string equality predicate * * @param s1 string * @param s2 string * * @return true if both s1 and s2 are null or same string, false otherwise */ static inline bool eq(const char *s1, const char *s2) { return (s1 && s2) ? !strcmp(s1, s2) : (s1 == s2); } /* ========================================================================= * * * ERRORS & LOGGING * * ========================================================================= */ /** Our custom error quark */ static GQuark gconf_error_quark (void) { return g_quark_from_static_string ("gconf-setting-error-quark"); } #define GCONF_ERROR gconf_error_quark() /** Error logging function */ #if GCONF_ENABLE_ERROR_LOGGING # define gconf_log_error(FMT, ARG...) mce_log_file(LL_WARN, __FILE__, __FUNCTION__, FMT , ##ARG) #else # define gconf_log_error(FMT, ARG...) do{}while(0) #endif /** Debug logging function */ #if GCONF_ENABLE_DEBUG_LOGGING # define gconf_log_debug_p() mce_log_p(LL_DEBUG) # define gconf_log_debug(FMT, ARG...) mce_log_file(LL_DEBUG, __FILE__, __FUNCTION__, FMT , ##ARG) #else # define gconf_log_debug_p() 0 # define gconf_log_debug(FMT, ARG...) do{}while(0) #endif /** Set GError helper */ static void gconf_set_error(GError **err, gint code, const gchar *fmt, ...) __attribute__((format(printf, 3, 4))); static void gconf_set_error(GError **err, gint code, const gchar *fmt, ...) { char *msg = 0; va_list va; va_start(va, fmt); if( vasprintf(&msg, fmt, va) < 0 ) { msg = 0; } va_end(va); /* Assume caller will report error in appropriate context, * log from builtin-gconf only in debug verbosity level */ mce_log(LL_DEBUG, "%s", msg ?: "unknown"); g_set_error_literal(err, GCONF_ERROR, code, msg ?: "unknown"); free(msg); } /* ========================================================================= * * * STRING PARSING * * ========================================================================= */ /** ASCII white character predicate */ static inline gboolean gconf_white_p(int c) { return (c > 0) && (c <= 32); } /** ASCII non-white character predicate */ static inline gboolean gconf_black_p(int c) { return (c < 0) || (c > 32); } /** Strip excess whitespace from string */ static char *gconf_strip_string(char *str) { if( str ) { char *s = str; char *d = str; while( gconf_white_p(*s) ) ++s; for( ;; ) { while( gconf_black_p(*s) ) *d++ = *s++; while( gconf_white_p(*s) ) ++s; if( !*s ) break; *d++ = ' '; } *d = 0; } return str; } /** Array of strings we accept as: boolean true */ static const char * const gconf_true_lut[] = { "true", "t", "yes", "y", 0 }; /** Array of strings we accept as: boolean false */ static const char * const gconf_false_lut[] = { "false", "f", "no", "n", 0 }; /** String to int helper */ static int gconf_parse_int(const char *str) { char *end = 0; int res = strtol(str, &end, 0); if( (end <= str) || *end ) { gconf_log_error("'%s': is not fully qualified integer", str); } return res; } /** String to double helper */ static double gconf_parse_float(const char *str) { char *end = 0; double res = strtod(str, &end); if( (end <= str) || *end ) { gconf_log_error("'%s': is not fully qualified float", str); } return res; } /** String to bool helper */ static gboolean gconf_parse_bool(const char *str) { for( size_t i = 0; gconf_true_lut[i]; ++i ) { if( !strcmp(gconf_true_lut[i], str) ) return TRUE; } for( size_t i = 0; gconf_false_lut[i]; ++i ) { if( !strcmp(gconf_false_lut[i], str) ) return FALSE; } int res = gconf_parse_int(str); if( res < 0 || res > 1 ) { gconf_log_error("'%s': is not fully qualified bool", str); } return res; } /** Char to GConfValueType helper */ static GConfValueType gconf_parse_type(int chr) { GConfValueType res = GCONF_VALUE_INVALID; switch( chr ) { case 'b': res = GCONF_VALUE_BOOL; break; case 'i': res = GCONF_VALUE_INT; break; case 'f': res = GCONF_VALUE_FLOAT; break; case 's': res = GCONF_VALUE_STRING; break; case 'a': res = GCONF_VALUE_LIST; break; default: gconf_log_error("unknown type '%c'", chr); break; } return res; } /* ========================================================================= * * * MISCELLANEOUS * * ========================================================================= */ #if GCONF_ENABLE_DEBUG_LOGGING /** Boolean to text helper */ static const char * gconf_bool_repr(gboolean value) { return value ? *gconf_true_lut : *gconf_false_lut; } #endif /** GConfValueType to text helper */ static const char * gconf_type_repr(GConfValueType type) { const char *res = "unknown"; switch( type ) { case GCONF_VALUE_INVALID: res = "invalid"; break; case GCONF_VALUE_STRING: res = "string"; break; case GCONF_VALUE_INT: res = "int"; break; case GCONF_VALUE_FLOAT: res = "float"; break; case GCONF_VALUE_BOOL: res = "bool"; break; case GCONF_VALUE_SCHEMA: res = "schema"; break; case GCONF_VALUE_LIST: res = "list"; break; case GCONF_VALUE_PAIR: res = "pair"; break; default: break; } return res; } /** Type checking helper */ static gboolean gconf_require_type(const char *key, const GConfValue *value, GConfValueType type, GError **err) { if( value->type == type ) { return TRUE; } gconf_set_error(err, GCONF_ERROR_TYPE_MISMATCH, "%s: is %s, not %s", key, gconf_type_repr(value->type), gconf_type_repr(type)); return FALSE; } /** List type checking helper */ static gboolean gconf_require_list_type(const char *key, const GConfValue *value, GConfValueType type, GError **err) { if( !gconf_require_type(key, value, GCONF_VALUE_LIST, err) ) { return FALSE; } if( value->list_type == type ) { return TRUE; } gconf_set_error(err, GCONF_ERROR_TYPE_MISMATCH, "%s: is %s list, not %s list", key, gconf_type_repr(value->list_type), gconf_type_repr(type)); return FALSE; } /** See GConf API documentation */ gchar * gconf_concat_dir_and_key(const gchar *dir, const gchar *key) { gchar *res = g_strconcat(dir ?: "", "/", key ?: "", NULL); if( res != 0 ) { gchar *s = res; gchar *d = res; while( *s ) { /* copy other than '/' verbatim */ if( *s != '/' ) { *d++ = *s++; continue; } /* compress '///' into '/' */ while( *++s == '/' ) {} *d++ = '/'; } /* terminate */ *d = 0; } return res; } /* ========================================================================= * * * GConfValue * * ========================================================================= */ #if GCONF_ENABLE_DEBUG_LOGGING /** GConfValue to text helper * * The result is for human readable for debugging purposes */ static char * gconf_value_repr(const char *key, GConfValue *self) { char *data = 0; size_t size = 0; FILE *file = 0; if( !(file = open_memstream(&data, &size)) ) { goto cleanup; } fprintf(file, "'%s' %s", key, gconf_type_repr(self->type)); switch( self->type ) { case GCONF_VALUE_STRING: fprintf(file, " '%s'", self->data.s); break; case GCONF_VALUE_INT: fprintf(file, " %d", self->data.i); break; case GCONF_VALUE_FLOAT: fprintf(file, " %g", self->data.f); break; case GCONF_VALUE_BOOL: fprintf(file, " %s", gconf_bool_repr(self->data.b)); break; case GCONF_VALUE_SCHEMA: fprintf(file, " %g", self->data.f); break; case GCONF_VALUE_LIST: fprintf(file, " of %s [", gconf_type_repr(self->list_type)); for( GSList *v_iter = self->list_head; v_iter; v_iter = v_iter->next ) { switch( (self = v_iter->data)->type ) { case GCONF_VALUE_STRING: fprintf(file, " '%s'", self->data.s); break; case GCONF_VALUE_INT: fprintf(file, " %d", self->data.i); break; case GCONF_VALUE_FLOAT: fprintf(file, " %g", self->data.f); break; case GCONF_VALUE_BOOL: fprintf(file, " %s", gconf_bool_repr(self->data.b)); break; default: fprintf(file, " ???"); break; } } fprintf(file, " ]"); break; case GCONF_VALUE_PAIR: fprintf(file, "pair ("); fprintf(file, " )"); break; case GCONF_VALUE_INVALID: default: break; } cleanup: if( file ) fclose(file); return data; } #endif /** GConfValue to text helper * * The result is compatible with gconf_value_set_from_string() */ static char * gconf_value_str(GConfValue *self) { char *data = 0; size_t size = 0; FILE *file = 0; if( !(file = open_memstream(&data, &size)) ) { goto cleanup; } switch( self->type ) { case GCONF_VALUE_STRING: fprintf(file, "%s", self->data.s); break; case GCONF_VALUE_INT: fprintf(file, "%d", self->data.i); break; case GCONF_VALUE_FLOAT: fprintf(file, "%g", self->data.f); break; case GCONF_VALUE_BOOL: fprintf(file, "%s", gconf_bool_repr(self->data.b)); break; case GCONF_VALUE_SCHEMA: fprintf(file, "%g", self->data.f); break; case GCONF_VALUE_LIST: for( GSList *v_iter = self->list_head; v_iter; v_iter = v_iter->next ) { switch( (self = v_iter->data)->type ) { case GCONF_VALUE_STRING: fprintf(file, "%s", self->data.s); break; case GCONF_VALUE_INT: fprintf(file, "%d", self->data.i); break; case GCONF_VALUE_FLOAT: fprintf(file, "%g", self->data.f); break; case GCONF_VALUE_BOOL: fprintf(file, "%s", gconf_bool_repr(self->data.b)); break; default: fprintf(file, "???"); break; } if( v_iter->next ) { fprintf(file, ","); } } break; case GCONF_VALUE_PAIR: fprintf(file, "???"); break; case GCONF_VALUE_INVALID: default: break; } cleanup: if( file ) fclose(file); return data; } /** Release dynamic resources of value, but not the value itself */ static void gconf_value_unset(GConfValue *self) { // clear the list of values for( GSList *elem = self->list_head; elem; elem = elem->next ) { gconf_value_free(elem->data); } g_slist_free(self->list_head), self->list_head = 0; // free any dynamically allocated data switch( self->type ) { case GCONF_VALUE_STRING: free(self->data.s), self->data.s = 0; break; default: break; } } /** Helper for type checking a list of values */ static gboolean gconf_value_list_validata(GSList *src, GConfValueType type) { for( ; src; src = src->next ) { GConfValue *val = src->data; if( !val ) { gconf_log_error("list has NULL value"); return FALSE; } if( val->type != type ) { gconf_log_error("list has %s value, expected %s\n", gconf_type_repr(val->type), gconf_type_repr(type)); return FALSE; } } return TRUE; } /** Helper for deep copying list of values */ static GSList * gconf_value_list_copy(GSList *src) { GSList *res = 0; for( ; src; src = src->next ) { res = g_slist_prepend(res, gconf_value_copy(src->data)); } res = g_slist_reverse(res); return res; } /** Helper for recursively releasing a list of values */ static GSList * gconf_value_list_free(GSList *list) { g_slist_free_full(list, (GDestroyNotify)gconf_value_free); return 0; } /** Parse value content from text */ static void gconf_value_set_from_string(GConfValue *self, const char *data) { char *now, *zen, *tmp = 0; switch( self->type ) { case GCONF_VALUE_BOOL: self->data.b = gconf_parse_bool(data); break; case GCONF_VALUE_INT: self->data.i = gconf_parse_int(data); break; case GCONF_VALUE_FLOAT: self->data.f = gconf_parse_float(data); break; case GCONF_VALUE_STRING: free(self->data.s); self->data.s = strdup(data); break; case GCONF_VALUE_LIST: switch( self->list_type ) { case GCONF_VALUE_BOOL: case GCONF_VALUE_INT: case GCONF_VALUE_FLOAT: case GCONF_VALUE_STRING: break; default: self->list_type = GCONF_VALUE_INVALID; self->type = GCONF_VALUE_INVALID; goto cleanup; } self->list_head = gconf_value_list_free(self->list_head); for( now = tmp = strdup(data); now; now = zen ) { if( (zen = strchr(now, ',')) ) { *zen++ = 0; } GConfValue *elem = gconf_value_init(self->list_type, GCONF_VALUE_INVALID, gconf_strip_string(now)); self->list_head = g_slist_prepend(self->list_head, elem); } self->list_head = g_slist_reverse(self->list_head); break; default: self->list_type = GCONF_VALUE_INVALID; self->type = GCONF_VALUE_INVALID; break; } cleanup: free(tmp); } /** Create and initialize a GConfValue */ static GConfValue * gconf_value_init(GConfValueType type, GConfValueType list_type, const char *data) { GConfValue *self = calloc(1, sizeof *self); self->refcount = 1; self->list_type = GCONF_VALUE_INVALID; self->type = type; if( self->type == GCONF_VALUE_LIST ) { self->list_type = list_type; } if( data != 0 ) { gconf_value_set_from_string(self, data); } return self; } /** See GConf API documentation */ GConfValue * gconf_value_copy(const GConfValue *src) { GConfValue *self = gconf_value_init(src->type, src->list_type, 0); if( self->type != src->type || self->list_type != src->list_type ) { goto cleanup; } switch( self->type ) { case GCONF_VALUE_BOOL: self->data.b = src->data.b; break; case GCONF_VALUE_INT: self->data.i = src->data.i; break; case GCONF_VALUE_FLOAT: self->data.f = src->data.f; break; case GCONF_VALUE_STRING: self->data.s = src->data.s ? strdup(src->data.s) : 0; break; case GCONF_VALUE_LIST: self->list_head = gconf_value_list_copy(src->list_head); break; default: self->list_type = GCONF_VALUE_INVALID; self->type = GCONF_VALUE_INVALID; break; } cleanup: return self; } /** See GConf API documentation */ GConfValue * gconf_value_new(GConfValueType type) { return gconf_value_init(type, GCONF_VALUE_INVALID, 0); } /** See GConf API documentation */ void gconf_value_free(GConfValue *self) { if( self != 0 && --self->refcount == 0 ) { /* get rid of dynamically allocated resources */ gconf_value_unset(self); /* mark the value as invalid to ease stale pointer debugging */ self->type = GCONF_VALUE_INVALID; self->list_type = GCONF_VALUE_INVALID; /* free the value itself too */ free(self); } } /** See GConf API documentation */ gboolean gconf_value_get_bool(const GConfValue *self) { return (self->type == GCONF_VALUE_BOOL) ? self->data.b : 0; } /** See GConf API documentation */ bool gconf_value_set_bool(GConfValue *self, gboolean val) { bool changed = false; if( self->type != GCONF_VALUE_BOOL ) { gconf_log_error("not a bool value"); } else if( self->data.b != val ) { self->data.b = val; changed = true; } return changed; } /** See GConf API documentation */ int gconf_value_get_int(const GConfValue *self) { return (self->type == GCONF_VALUE_INT) ? self->data.i : 0; } /** See GConf API documentation */ bool gconf_value_set_int(GConfValue *self, gint val) { bool changed = false; if( self->type != GCONF_VALUE_INT ) { gconf_log_error("not an int value"); } else if( self->data.i != val ) { self->data.i = val; changed = true; } return changed; } /** See GConf API documentation */ double gconf_value_get_float(const GConfValue *self) { return (self->type == GCONF_VALUE_FLOAT) ? self->data.f : 0; } /** See GConf API documentation */ bool gconf_value_set_float(GConfValue *self, double val) { bool changed = false; if( self->type != GCONF_VALUE_FLOAT ) { gconf_log_error("not a float value"); } else if( self->data.f != val ) { self->data.f = val; changed = true; } return changed; } /** See GConf API documentation */ const char * gconf_value_get_string(const GConfValue *self) { return (self->type == GCONF_VALUE_STRING) ? self->data.s : 0; } /** See GConf API documentation */ bool gconf_value_set_string(GConfValue *self, const char *val) { bool changed = false; if( self->type != GCONF_VALUE_STRING ) { gconf_log_error("not a string value"); } else if( !eq(self->data.s, val) ) { free(self->data.s), self->data.s = val ? strdup(val) : 0; changed = true; } return changed; } /** See GConf API documentation */ GConfValueType gconf_value_get_list_type(const GConfValue *self) { return self->list_type; } /** See GConf API documentation */ void gconf_value_set_list_type(GConfValue *self, GConfValueType list_type) { switch( list_type ) { case GCONF_VALUE_STRING: case GCONF_VALUE_INT: case GCONF_VALUE_FLOAT: case GCONF_VALUE_BOOL: break; default: gconf_log_error("list type can't be %s", gconf_type_repr(list_type)); goto cleanup; } if( self->type != GCONF_VALUE_LIST ) { gconf_log_error("not a list value"); goto cleanup; } if( self->list_type == GCONF_VALUE_INVALID ) { self->list_type = list_type; } if( self->list_type != list_type ) { gconf_log_error("list type already set"); goto cleanup; } cleanup: return; } /** See GConf API documentation */ GSList * gconf_value_get_list(const GConfValue *self) { return self->list_head; } /** See GConf API documentation */ bool gconf_value_set_list(GConfValue *self, GSList *list) { // TODO: proper list compare would be saner, but for now // list has changed if string representation changes ... char *prev = gconf_value_str(self); self->list_head = gconf_value_list_free(self->list_head); if( gconf_value_list_validata(list, self->list_type) ) { self->list_head = gconf_value_list_copy(list); } char *curr = gconf_value_str(self); bool changed = !eq(prev, curr); free(curr); free(prev); return changed; } /* ========================================================================= * * * GConfEntry * * ========================================================================= */ static void gconf_entry_free(GConfEntry *self) { if( self ) { gconf_value_free(self->value); free(self->key); free(self->def); free(self); } } static void gconf_entry_free_cb(gpointer self) { gconf_entry_free(self); } /** Create a GConfEntry object */ static GConfEntry * gconf_entry_init(const char *key, const char *type, const char *data) { GConfEntry *self = calloc(1, sizeof *self); self->key = strdup(key); self->def = data ? strdup(data) : 0; GConfValueType ltype = GCONF_VALUE_INVALID; GConfValueType vtype = gconf_parse_type(type[0]); if( vtype == GCONF_VALUE_LIST ) { ltype = gconf_parse_type(type[1]); } self->value = gconf_value_init(vtype, ltype, data); self->notify_entered = false; self->notify_changed = false; return self; } /** See GConf API documentation */ const char * gconf_entry_get_key(const GConfEntry *entry) { return entry ? entry->key : 0; } /** See GConf API documentation */ GConfValue * gconf_entry_get_value(const GConfEntry *entry) { return entry ? entry->value : 0; } /* ========================================================================= * * * DATABASE * * ========================================================================= */ /** Built-in set of supported keys and default values */ typedef struct { const char *key; const char *type; const char *def; } setting_t; /** Custom stringify macro for comma separated lists * * G_STRINGIFY() uses standard two phase expansion - which * does not work with comma separated lists. Using gcc * pre-prosessor extension allows also those to work. */ #define CUSTOM_STRINGIFY2(v...) #v #define CUSTOM_STRINGIFY(v) CUSTOM_STRINGIFY2(v) static const setting_t gconf_defaults[] = { { .key = MCE_SETTING_EM_ENABLE_PSM, .type = "b", .def = G_STRINGIFY(MCE_DEFAULT_EM_ENABLE_PSM), }, { .key = MCE_SETTING_EM_FORCED_PSM, .type = "b", .def = G_STRINGIFY(MCE_DEFAULT_EM_FORCED_PSM), }, { .key = MCE_SETTING_EM_PSM_THRESHOLD, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_EM_PSM_THRESHOLD), }, { .key = MCE_SETTING_EM_POSSIBLE_PSM_THRESHOLDS, .type = "ai", .def = CUSTOM_STRINGIFY(MCE_DEFAULT_EM_POSSIBLE_PSM_THRESHOLDS), }, { .key = MCE_SETTING_DISPLAY_ALS_ENABLED, .type = "b", .def = G_STRINGIFY(MCE_DEFAULT_DISPLAY_ALS_ENABLED), }, { .key = MCE_SETTING_DISPLAY_ALS_AUTOBRIGHTNESS, .type = "b", .def = G_STRINGIFY(MCE_DEFAULT_DISPLAY_ALS_AUTOBRIGHTNESS), }, { .key = MCE_SETTING_DISPLAY_ALS_INPUT_FILTER, .type = "s", .def = MCE_DEFAULT_DISPLAY_ALS_INPUT_FILTER, }, { .key = MCE_SETTING_DISPLAY_ALS_SAMPLE_TIME, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_DISPLAY_ALS_SAMPLE_TIME), }, { .key = MCE_SETTING_DISPLAY_COLOR_PROFILE, .type = "s", .def = "", }, { .key = MCE_SETTING_DISPLAY_DIM_TIMEOUT, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_DISPLAY_DIM_TIMEOUT), }, { .key = MCE_SETTING_DISPLAY_DIM_WITH_KEYBOARD_TIMEOUT, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_DISPLAY_DIM_WITH_KEYBOARD_TIMEOUT), }, { .key = MCE_SETTING_DISPLAY_BLANK_TIMEOUT, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_DISPLAY_BLANK_TIMEOUT), }, { .key = MCE_SETTING_DISPLAY_BLANK_FROM_LOCKSCREEN_TIMEOUT, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_DISPLAY_BLANK_FROM_LOCKSCREEN_TIMEOUT), }, { .key = MCE_SETTING_DISPLAY_BLANK_FROM_LPM_ON_TIMEOUT, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_DISPLAY_BLANK_FROM_LPM_ON_TIMEOUT), }, { .key = MCE_SETTING_DISPLAY_BLANK_FROM_LPM_OFF_TIMEOUT, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_DISPLAY_BLANK_FROM_LPM_OFF_TIMEOUT), }, { .key = MCE_SETTING_DISPLAY_NEVER_BLANK, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_DISPLAY_NEVER_BLANK), }, { .key = MCE_SETTING_DISPLAY_BRIGHTNESS, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_DISPLAY_BRIGHTNESS), }, { .key = MCE_SETTING_DISPLAY_BRIGHTNESS_LEVEL_SIZE, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_DISPLAY_BRIGHTNESS_LEVEL_SIZE), }, { .key = MCE_SETTING_DISPLAY_BRIGHTNESS_LEVEL_COUNT, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_DISPLAY_BRIGHTNESS_LEVEL_COUNT), }, { .key = MCE_SETTING_DISPLAY_DIM_STATIC_BRIGHTNESS, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_DISPLAY_DIM_STATIC_BRIGHTNESS) }, { .key = MCE_SETTING_DISPLAY_DIM_DYNAMIC_BRIGHTNESS, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_DISPLAY_DIM_DYNAMIC_BRIGHTNESS) }, { .key = MCE_SETTING_DISPLAY_DIM_COMPOSITOR_LO, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_DISPLAY_DIM_COMPOSITOR_LO) }, { .key = MCE_SETTING_DISPLAY_DIM_COMPOSITOR_HI, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_DISPLAY_DIM_COMPOSITOR_HI) }, { .key = MCE_SETTING_DISPLAY_DIM_TIMEOUT_LIST, .type = "ai", .def = CUSTOM_STRINGIFY(MCE_DEFAULT_DISPLAY_DIM_TIMEOUT_LIST), }, { .key = MCE_SETTING_ORIENTATION_SENSOR_ENABLED, .type = "b", .def = G_STRINGIFY(MCE_DEFAULT_ORIENTATION_SENSOR_ENABLED), }, { .key = MCE_SETTING_FLIPOVER_GESTURE_ENABLED, .type = "b", .def = G_STRINGIFY(MCE_DEFAULT_FLIPOVER_GESTURE_ENABLED), }, { .key = MCE_SETTING_ORIENTATION_CHANGE_IS_ACTIVITY, .type = "b", .def = G_STRINGIFY(MCE_DEFAULT_ORIENTATION_CHANGE_IS_ACTIVITY), }, { .key = MCE_SETTING_DISPLAY_BLANKING_PAUSE_MODE, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_DISPLAY_BLANKING_PAUSE_MODE), }, { .key = MCE_SETTING_DISPLAY_BLANK_TIMEOUT_LIST, .type = "ai", .def = CUSTOM_STRINGIFY(MCE_DEFAULT_DISPLAY_BLANK_TIMEOUT_LIST), }, { .key = MCE_SETTING_DISPLAY_ADAPTIVE_DIMMING, .type = "b", .def = G_STRINGIFY(MCE_DEFAULT_DISPLAY_ADAPTIVE_DIMMING), }, { .key = MCE_SETTING_DISPLAY_ADAPTIVE_DIM_THRESHOLD, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_DISPLAY_ADAPTIVE_DIM_THRESHOLD), }, { .key = MCE_SETTING_USE_LOW_POWER_MODE, .type = "b", .def = G_STRINGIFY(MCE_DEFAULT_USE_LOW_POWER_MODE), }, { .key = MCE_SETTING_TK_AUTOLOCK_ENABLED, .type = "b", .def = G_STRINGIFY(MCE_DEFAULT_TK_AUTOLOCK_ENABLED), }, { .key = MCE_SETTING_TK_INPUT_POLICY_ENABLED, .type = "b", .def = G_STRINGIFY(MCE_DEFAULT_TK_INPUT_POLICY_ENABLED), }, { .key = MCE_SETTING_TK_VOLKEY_POLICY, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_TK_VOLKEY_POLICY), }, { .key = MCE_SETTING_TK_LPMUI_TRIGGERING, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_TK_LPMUI_TRIGGERING), }, { .key = MCE_SETTING_TK_PROXIMITY_BLOCKS_TOUCH, .type = "b", .def = G_STRINGIFY(MCE_DEFAULT_TK_PROXIMITY_BLOCKS_TOUCH), }, { .key = MCE_SETTING_TK_DEVICELOCK_IN_LOCKSCREEN, .type = "b", .def = G_STRINGIFY(MCE_DEFAULT_TK_DEVICELOCK_IN_LOCKSCREEN), }, { .key = MCE_SETTING_TK_LID_SENSOR_ENABLED, .type = "b", .def = G_STRINGIFY(MCE_DEFAULT_TK_LID_SENSOR_ENABLED), }, { .key = MCE_SETTING_TK_FILTER_LID_WITH_ALS, .type = "b", .def = G_STRINGIFY(MCE_DEFAULT_TK_FILTER_LID_WITH_ALS), }, { .key = MCE_SETTING_TK_FILTER_LID_ALS_LIMIT, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_TK_FILTER_LID_ALS_LIMIT), }, { .key = MCE_SETTING_TK_LID_OPEN_ACTIONS, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_TK_LID_OPEN_ACTIONS), }, { .key = MCE_SETTING_TK_LID_CLOSE_ACTIONS, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_TK_LID_CLOSE_ACTIONS), }, { .key = MCE_SETTING_TK_KBD_OPEN_TRIGGER, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_TK_KBD_OPEN_TRIGGER), }, { .key = MCE_SETTING_TK_KBD_OPEN_ACTIONS, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_TK_KBD_OPEN_ACTIONS), }, { .key = MCE_SETTING_TK_KBD_CLOSE_TRIGGER, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_TK_KBD_CLOSE_TRIGGER), }, { .key = MCE_SETTING_TK_KBD_CLOSE_ACTIONS, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_TK_KBD_CLOSE_ACTIONS), }, { .key = MCE_SETTING_TK_AUTOLOCK_DELAY, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_TK_AUTOLOCK_DELAY), }, { .key = MCE_SETTING_BLANKING_INHIBIT_MODE, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_BLANKING_INHIBIT_MODE), }, { .key = MCE_SETTING_KBD_SLIDE_INHIBIT, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_KBD_SLIDE_INHIBIT), }, { .key = MCE_SETTING_USE_AUTOSUSPEND, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_USE_AUTOSUSPEND), }, { .key = MCE_SETTING_CPU_SCALING_GOVERNOR, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_CPU_SCALING_GOVERNOR), }, { .key = MCE_SETTING_LIPSTICK_CORE_DELAY, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_LIPSTICK_CORE_DELAY), }, { .key = MCE_SETTING_BRIGHTNESS_FADE_DEFAULT_MS, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_BRIGHTNESS_FADE_DEFAULT_MS), }, { .key = MCE_SETTING_BRIGHTNESS_FADE_DIMMING_MS, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_BRIGHTNESS_FADE_DIMMING_MS), }, { .key = MCE_SETTING_BRIGHTNESS_FADE_ALS_MS, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_BRIGHTNESS_FADE_ALS_MS), }, { .key = MCE_SETTING_BRIGHTNESS_FADE_BLANK_MS, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_BRIGHTNESS_FADE_BLANK_MS), }, { .key = MCE_SETTING_BRIGHTNESS_FADE_UNBLANK_MS, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_BRIGHTNESS_FADE_UNBLANK_MS), }, { .key = MCE_SETTING_DISPLAY_OFF_OVERRIDE, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_DISPLAY_OFF_OVERRIDE), }, { .key = MCE_SETTING_TK_AUTO_BLANK_DISABLE, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_TK_AUTO_BLANK_DISABLE), }, #ifdef ENABLE_DOUBLETAP_EMULATION { .key = MCE_SETTING_USE_FAKE_DOUBLETAP, .type = "b", .def = G_STRINGIFY(MCE_DEFAULT_USE_FAKE_DOUBLETAP), }, #endif { .key = MCE_SETTING_TOUCH_UNBLOCK_DELAY, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_TOUCH_UNBLOCK_DELAY), }, { .key = MCE_SETTING_INPUT_GRAB_ALLOWED, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_INPUT_GRAB_ALLOWED), }, { .key = MCE_SETTING_LED_PATH"/"MCE_LED_PATTERN_BATTERY_CHARGING, .type = "b", .def = G_STRINGIFY(MCE_DEFAULT_LED_PATTERN_ENABLED), }, { .key = MCE_SETTING_LED_PATH"/"MCE_LED_PATTERN_BATTERY_FULL, .type = "b", .def = G_STRINGIFY(MCE_DEFAULT_LED_PATTERN_ENABLED), }, { .key = MCE_SETTING_LED_PATH"/"MCE_LED_PATTERN_COMMUNICATION_EVENT, .type = "b", .def = G_STRINGIFY(MCE_DEFAULT_LED_PATTERN_ENABLED), }, { .key = MCE_SETTING_LED_PATH"/"MCE_LED_PATTERN_POWER_OFF, .type = "b", .def = G_STRINGIFY(MCE_DEFAULT_LED_PATTERN_ENABLED), }, { .key = MCE_SETTING_LED_PATH"/"MCE_LED_PATTERN_POWER_ON, .type = "b", .def = G_STRINGIFY(MCE_DEFAULT_LED_PATTERN_ENABLED), }, { .key = MCE_SETTING_LED_PATH"/"MCE_LED_PATTERN_CAMERA, .type = "b", .def = G_STRINGIFY(MCE_DEFAULT_LED_PATTERN_ENABLED), }, { .key = MCE_SETTING_LED_PATH"/"MCE_LED_PATTERN_DEVICE_ON, .type = "b", .def = G_STRINGIFY(MCE_DEFAULT_LED_PATTERN_ENABLED), }, { .key = MCE_SETTING_LED_PATH"/"MCE_LED_PATTERN_BATTERY_LOW, .type = "b", .def = G_STRINGIFY(MCE_DEFAULT_LED_PATTERN_ENABLED), }, { .key = MCE_SETTING_LED_PATH"/"MCE_LED_PATTERN_COMMUNICATION_EVENT_BATTERY_FULL, .type = "b", .def = G_STRINGIFY(MCE_DEFAULT_LED_PATTERN_ENABLED), }, { .key = MCE_SETTING_LED_PATH"/"MCE_LED_PATTERN_BATTERY_CHARGING_FLAT, .type = "b", .def = G_STRINGIFY(MCE_DEFAULT_LED_PATTERN_ENABLED), }, { .key = MCE_SETTING_LED_PATH"/"MCE_LED_PATTERN_COMMON_NOTIFICATION, .type = "b", .def = G_STRINGIFY(MCE_DEFAULT_LED_PATTERN_ENABLED), }, { .key = MCE_SETTING_LED_PATH"/"MCE_LED_PATTERN_COMMUNICATION_CALL, .type = "b", .def = G_STRINGIFY(MCE_DEFAULT_LED_PATTERN_ENABLED), }, { .key = MCE_SETTING_LED_PATH"/"MCE_LED_PATTERN_COMMUNICATION_EMAIL, .type = "b", .def = G_STRINGIFY(MCE_DEFAULT_LED_PATTERN_ENABLED), }, { .key = MCE_SETTING_LED_PATH"/"MCE_LED_PATTERN_COMMUNICATION_IM, .type = "b", .def = G_STRINGIFY(MCE_DEFAULT_LED_PATTERN_ENABLED), }, { .key = MCE_SETTING_LED_PATH"/"MCE_LED_PATTERN_COMMUNICATION_SMS, .type = "b", .def = G_STRINGIFY(MCE_DEFAULT_LED_PATTERN_ENABLED), }, { .key = MCE_SETTING_LED_PATH"/"MCE_LED_PATTERN_CSD_WHITE, .type = "b", .def = G_STRINGIFY(MCE_DEFAULT_LED_PATTERN_ENABLED), }, { .key = MCE_SETTING_LED_PATH"/"MCE_LED_PATTERN_DISPLAY_BLANK_FAILED, .type = "b", .def = G_STRINGIFY(MCE_DEFAULT_LED_PATTERN_ENABLED), }, { .key = MCE_SETTING_LED_PATH"/"MCE_LED_PATTERN_DISPLAY_UNBLANK_FAILED, .type = "b", .def = G_STRINGIFY(MCE_DEFAULT_LED_PATTERN_ENABLED), }, { .key = MCE_SETTING_LED_PATH"/"MCE_LED_PATTERN_DISPLAY_SUSPEND_FAILED, .type = "b", .def = G_STRINGIFY(MCE_DEFAULT_LED_PATTERN_ENABLED), }, { .key = MCE_SETTING_LED_PATH"/"MCE_LED_PATTERN_DISPLAY_RESUME_FAILED, .type = "b", .def = G_STRINGIFY(MCE_DEFAULT_LED_PATTERN_ENABLED), }, { .key = MCE_SETTING_LED_PATH"/"MCE_LED_PATTERN_KILLING_LIPSTICK, .type = "b", .def = G_STRINGIFY(MCE_DEFAULT_LED_PATTERN_ENABLED), }, { .key = MCE_SETTING_LED_PATH"/"MCE_LED_PATTERN_TOUCH_INPUT_BLOCKED, .type = "b", .def = G_STRINGIFY(false), }, { .key = MCE_SETTING_LED_PATH"/"MCE_LED_PATTERN_DISPLAY_DIMMED, .type = "b", .def = G_STRINGIFY(false), }, { .key = MCE_SETTING_LED_PATH"/"MCE_LED_PATTERN_SCANNING_FINGERPRINT, .type = "b", .def = G_STRINGIFY(false), }, { .key = MCE_SETTING_LED_PATH"/"MCE_LED_PATTERN_FINGERPRINT_ACQUIRED, .type = "b", .def = G_STRINGIFY(false), }, { .key = MCE_SETTING_LED_PATH"/"MCE_LED_PATTERN_PROXIMITY_COVERED, .type = "b", .def = G_STRINGIFY(false), }, { .key = MCE_SETTING_LED_PATH"/"MCE_LED_PATTERN_PROXIMITY_UNCOVERING, .type = "b", .def = G_STRINGIFY(false), }, { .key = MCE_SETTING_LED_PATH"/"MCE_LED_PATTERN_PROXIMITY_UNCOVERED, .type = "b", .def = G_STRINGIFY(false), }, { .key = MCE_SETTING_LED_SW_BREATH_ENABLED, .type = "b", .def = G_STRINGIFY(MCE_DEFAULT_LED_SW_BREATH_ENABLED), }, { .key = MCE_SETTING_LED_SW_BREATH_BATTERY_LIMIT, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_LED_SW_BREATH_BATTERY_LIMIT), }, { .key = MCE_SETTING_PROXIMITY_PS_ENABLED, .type = "b", .def = G_STRINGIFY(MCE_DEFAULT_PROXIMITY_PS_ENABLED), }, { .key = MCE_SETTING_PROXIMITY_ON_DEMAND, .type = "b", .def = G_STRINGIFY(MCE_DEFAULT_PROXIMITY_ON_DEMAND), }, { .key = MCE_SETTING_PROXIMITY_PS_ACTS_AS_LID, .type = "b", .def = G_STRINGIFY(MCE_DEFAULT_PROXIMITY_PS_ACTS_AS_LID), }, { .key = MCE_SETTING_DOUBLETAP_MODE, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_DOUBLETAP_MODE), }, { .key = MCE_SETTING_FPWAKEUP_MODE, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_FPWAKEUP_MODE), }, { .key = MCE_SETTING_FPWAKEUP_ALLOW_DELAY, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_FPWAKEUP_ALLOW_DELAY), }, { .key = MCE_SETTING_FPWAKEUP_TRIGGER_DELAY, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_FPWAKEUP_TRIGGER_DELAY), }, { .key = MCE_SETTING_FPWAKEUP_THROTTLE_DELAY, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_FPWAKEUP_THROTTLE_DELAY), }, { .key = MCE_SETTING_POWERKEY_MODE, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_POWERKEY_MODE), }, { .key = MCE_SETTING_POWERKEY_BLANKING_MODE, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_POWERKEY_BLANKING_MODE), }, { .key = MCE_SETTING_POWERKEY_PS_OVERRIDE_COUNT, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_POWERKEY_PS_OVERRIDE_COUNT), }, { .key = MCE_SETTING_POWERKEY_PS_OVERRIDE_TIMEOUT, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_POWERKEY_PS_OVERRIDE_TIMEOUT), }, { .key = MCE_SETTING_POWERKEY_LONG_PRESS_DELAY, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_POWERKEY_LONG_PRESS_DELAY), }, { .key = MCE_SETTING_POWERKEY_DOUBLE_PRESS_DELAY, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_POWERKEY_DOUBLE_PRESS_DELAY), }, { .key = MCE_SETTING_POWERKEY_ACTIONS_SINGLE_ON, .type = "s", .def = MCE_DEFAULT_POWERKEY_ACTIONS_SINGLE_ON, }, { .key = MCE_SETTING_POWERKEY_ACTIONS_DOUBLE_ON, .type = "s", .def = MCE_DEFAULT_POWERKEY_ACTIONS_DOUBLE_ON, }, { .key = MCE_SETTING_POWERKEY_ACTIONS_LONG_ON, .type = "s", .def = MCE_DEFAULT_POWERKEY_ACTIONS_LONG_ON, }, { .key = MCE_SETTING_POWERKEY_ACTIONS_SINGLE_OFF, .type = "s", .def = MCE_DEFAULT_POWERKEY_ACTIONS_SINGLE_OFF, }, { .key = MCE_SETTING_POWERKEY_ACTIONS_DOUBLE_OFF, .type = "s", .def = MCE_DEFAULT_POWERKEY_ACTIONS_DOUBLE_OFF, }, { .key = MCE_SETTING_POWERKEY_ACTIONS_LONG_OFF, .type = "s", .def = MCE_DEFAULT_POWERKEY_ACTIONS_LONG_OFF, }, { .key = MCE_SETTING_POWERKEY_ACTIONS_GESTURE0, .type = "s", .def = MCE_DEFAULT_POWERKEY_ACTIONS_GESTURE0, }, { .key = MCE_SETTING_POWERKEY_ACTIONS_GESTURE1, .type = "s", .def = MCE_DEFAULT_POWERKEY_ACTIONS_GESTURE1, }, { .key = MCE_SETTING_POWERKEY_ACTIONS_GESTURE2, .type = "s", .def = MCE_DEFAULT_POWERKEY_ACTIONS_GESTURE2, }, { .key = MCE_SETTING_POWERKEY_ACTIONS_GESTURE3, .type = "s", .def = MCE_DEFAULT_POWERKEY_ACTIONS_GESTURE3, }, { .key = MCE_SETTING_POWERKEY_ACTIONS_GESTURE4, .type = "s", .def = MCE_DEFAULT_POWERKEY_ACTIONS_GESTURE4, }, { .key = MCE_SETTING_POWERKEY_ACTIONS_GESTURE5, .type = "s", .def = MCE_DEFAULT_POWERKEY_ACTIONS_GESTURE5, }, { .key = MCE_SETTING_POWERKEY_ACTIONS_GESTURE6, .type = "s", .def = MCE_DEFAULT_POWERKEY_ACTIONS_GESTURE6, }, { .key = MCE_SETTING_POWERKEY_ACTIONS_GESTURE7, .type = "s", .def = MCE_DEFAULT_POWERKEY_ACTIONS_GESTURE7, }, { .key = MCE_SETTING_POWERKEY_ACTIONS_GESTURE8, .type = "s", .def = MCE_DEFAULT_POWERKEY_ACTIONS_GESTURE8, }, { .key = MCE_SETTING_POWERKEY_ACTIONS_GESTURE9, .type = "s", .def = MCE_DEFAULT_POWERKEY_ACTIONS_GESTURE9, }, { .key = MCE_SETTING_POWERKEY_ACTIONS_GESTURE10, .type = "s", .def = MCE_DEFAULT_POWERKEY_ACTIONS_GESTURE10, }, { .key = MCE_SETTING_POWERKEY_ACTIONS_GESTURE11, .type = "s", .def = MCE_DEFAULT_POWERKEY_ACTIONS_GESTURE11, }, { .key = MCE_SETTING_POWERKEY_ACTIONS_GESTURE12, .type = "s", .def = MCE_DEFAULT_POWERKEY_ACTIONS_GESTURE12, }, { .key = MCE_SETTING_POWERKEY_ACTIONS_GESTURE13, .type = "s", .def = MCE_DEFAULT_POWERKEY_ACTIONS_GESTURE13, }, { .key = MCE_SETTING_POWERKEY_ACTIONS_GESTURE14, .type = "s", .def = MCE_DEFAULT_POWERKEY_ACTIONS_GESTURE14, }, { .key = MCE_SETTING_POWERKEY_ACTIONS_GESTURE15, .type = "s", .def = MCE_DEFAULT_POWERKEY_ACTIONS_GESTURE15, }, { .key = MCE_SETTING_POWERKEY_ACTIONS_GESTURE16, .type = "s", .def = MCE_DEFAULT_POWERKEY_ACTIONS_GESTURE16, }, { .key = MCE_SETTING_POWERKEY_ACTIONS_GESTURE17, .type = "s", .def = MCE_DEFAULT_POWERKEY_ACTIONS_GESTURE17, }, { .key = MCE_SETTING_POWERKEY_ACTIONS_GESTURE18, .type = "s", .def = MCE_DEFAULT_POWERKEY_ACTIONS_GESTURE18, }, { .key = MCE_SETTING_POWERKEY_ACTIONS_GESTURE19, .type = "s", .def = MCE_DEFAULT_POWERKEY_ACTIONS_GESTURE19, }, { .key = MCE_SETTING_POWERKEY_DBUS_ACTION1, .type = "s", .def = MCE_DEFAULT_POWERKEY_DBUS_ACTION1, }, { .key = MCE_SETTING_POWERKEY_DBUS_ACTION2, .type = "s", .def = MCE_DEFAULT_POWERKEY_DBUS_ACTION2, }, { .key = MCE_SETTING_POWERKEY_DBUS_ACTION3, .type = "s", .def = MCE_DEFAULT_POWERKEY_DBUS_ACTION3, }, { .key = MCE_SETTING_POWERKEY_DBUS_ACTION4, .type = "s", .def = MCE_DEFAULT_POWERKEY_DBUS_ACTION4, }, { .key = MCE_SETTING_POWERKEY_DBUS_ACTION5, .type = "s", .def = MCE_DEFAULT_POWERKEY_DBUS_ACTION5, }, { .key = MCE_SETTING_POWERKEY_DBUS_ACTION6, .type = "s", .def = MCE_DEFAULT_POWERKEY_DBUS_ACTION6, }, { .key = MCE_SETTING_POWERKEY_DBUS_ACTION7, .type = "s", .def = MCE_DEFAULT_POWERKEY_DBUS_ACTION7, }, { .key = MCE_SETTING_POWERKEY_DBUS_ACTION8, .type = "s", .def = MCE_DEFAULT_POWERKEY_DBUS_ACTION8, }, { .key = MCE_SETTING_POWERKEY_DBUS_ACTION9, .type = "s", .def = MCE_DEFAULT_POWERKEY_DBUS_ACTION9, }, { .key = MCE_SETTING_POWERKEY_DBUS_ACTION10, .type = "s", .def = MCE_DEFAULT_POWERKEY_DBUS_ACTION10, }, { .key = MCE_SETTING_MEMNOTIFY_WARNING_USED, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_MEMNOTIFY_WARNING_USED) }, { .key = MCE_SETTING_MEMNOTIFY_WARNING_ACTIVE, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_MEMNOTIFY_WARNING_ACTIVE) }, { .key = MCE_SETTING_MEMNOTIFY_CRITICAL_USED, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_MEMNOTIFY_CRITICAL_USED) }, { .key = MCE_SETTING_MEMNOTIFY_CRITICAL_ACTIVE, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_MEMNOTIFY_CRITICAL_ACTIVE) }, { .key = MCE_SETTING_TK_EXCEPT_LEN_CALL_IN, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_TK_EXCEPT_LEN_CALL_IN), }, { .key = MCE_SETTING_TK_EXCEPT_LEN_CALL_OUT, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_TK_EXCEPT_LEN_CALL_OUT), }, { .key = MCE_SETTING_TK_EXCEPT_LEN_ALARM, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_TK_EXCEPT_LEN_ALARM), }, { .key = MCE_SETTING_TK_EXCEPT_LEN_USB_CONNECT, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_TK_EXCEPT_LEN_USB_CONNECT), }, { .key = MCE_SETTING_TK_EXCEPT_LEN_USB_DIALOG, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_TK_EXCEPT_LEN_USB_DIALOG), }, { .key = MCE_SETTING_TK_EXCEPT_LEN_CHARGER, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_TK_EXCEPT_LEN_CHARGER), }, { .key = MCE_SETTING_TK_EXCEPT_LEN_BATTERY, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_TK_EXCEPT_LEN_BATTERY), }, { .key = MCE_SETTING_TK_EXCEPT_LEN_JACK_IN, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_TK_EXCEPT_LEN_JACK_IN), }, { .key = MCE_SETTING_TK_EXCEPT_LEN_JACK_OUT, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_TK_EXCEPT_LEN_JACK_OUT), }, { .key = MCE_SETTING_TK_EXCEPT_LEN_CAMERA, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_TK_EXCEPT_LEN_CAMERA), }, { .key = MCE_SETTING_TK_EXCEPT_LEN_VOLUME, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_TK_EXCEPT_LEN_VOLUME), }, { .key = MCE_SETTING_TK_EXCEPT_LEN_ACTIVITY, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_TK_EXCEPT_LEN_ACTIVITY), }, { .key = MCE_SETTING_TK_LOCKSCREEN_ANIM_ENABLED, .type = "b", .def = G_STRINGIFY(MCE_DEFAULT_TK_LOCKSCREEN_ANIM_ENABLED), }, { .key = MCE_SETTING_TK_PROXIMITY_DELAY_DEFAULT, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_TK_PROXIMITY_DELAY_DEFAULT), }, { .key = MCE_SETTING_TK_PROXIMITY_DELAY_INCALL, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_TK_PROXIMITY_DELAY_INCALL), }, { .key = MCE_SETTING_INACTIVITY_SHUTDOWN_DELAY, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_INACTIVITY_SHUTDOWN_DELAY), }, { .key = MCE_SETTING_BUTTONBACKLIGHT_OFF_DELAY, .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_BUTTONBACKLIGHT_OFF_DELAY), }, { .key = NULL, } }; /* ========================================================================= * * * GConfClient * * ========================================================================= */ /** The one and only GConfClient we expect to see */ static GConfClient *default_client = 0; /** Lookup table for latest change signals sent */ static GHashTable *gconf_signal_sent = 0; /** Lookup table for latest change notify made */ static GHashTable *gconf_notify_made = 0; /** Save values to persistent storage file */ static void gconf_client_save_values(GConfClient *self, const char *path) { char *data = 0; size_t size = 0; FILE *file = 0; mce_log(LL_INFO, "updating %s", path); if( !(file = open_memstream(&data, &size)) ) { goto cleanup; } for( GSList *e_iter = self->entries; e_iter; e_iter = e_iter->next ) { GConfEntry *entry = e_iter->data; char *str = gconf_value_str(entry->value); if( !str ) { mce_log(LL_WARN, "failed to serialize value of key %s", entry->key); continue; } /* Omit values that do not differ from defaults */ if( !entry->def || strcmp(entry->def, str) ) { fprintf(file, "%s=%s\n", entry->key, str); } free(str); } // the data pointer gets set at fclose() fclose(file), file = 0; if( data ) { mce_io_update_file_atomic(path, data, size, 0664, FALSE); } cleanup: if( file ) fclose(file); free(data); return; } /** Load values from persistent storage file */ static void gconf_client_load_values(GConfClient *self, const char *path) { FILE *file = 0; char *buff = 0; size_t size = 0; mce_log(LL_NOTICE, "loading %s", path); if( !(file = fopen(path, "r")) ) { if( errno != ENOENT ) { mce_log(LL_ERR, "fopen(%s): %m", path); } goto EXIT; } while( getline(&buff, &size, file) >= 0 ) { char *key = buff; char *val = strchr(key, '='); if( val ) { GError *err = 0; GConfEntry *entry; *val++ = 0; gconf_strip_string(key); gconf_strip_string(val); if( (entry = gconf_client_find_entry(self, key, &err)) ) { gconf_value_set_from_string(entry->value, val); } g_clear_error(&err); } } EXIT: free(buff); if( file ) fclose(file); return; } /** Verify that we get only expected GConfClient pointers */ static gboolean gconf_client_is_valid(GConfClient *self, GError **err) { if( self == 0 ) { gconf_set_error(err, GCONF_ERROR_FAILED, "NULL client passed"); return FALSE; } if( self != default_client ) { gconf_set_error(err, GCONF_ERROR_FAILED, "Non default client passed"); return FALSE; } return TRUE; } /** List client values [debugging] */ #if GCONF_ENABLE_DEBUG_LOGGING static void gconf_client_debug(GConfClient *self) { fprintf(stderr, "Values known to builtin-gconf:\n"); for( GSList *e_iter = self->entries; e_iter; e_iter = e_iter->next ) { GConfEntry *entry = e_iter->data; char *repr = gconf_value_repr(entry->key, entry->value); fprintf(stderr, "%s\n", repr); free(repr); } } #endif /** Callback function for logging errors within glob() * * @param path path to file/dir where error occurred * @param err errno that occurred * * @return 0 (= do not stop glob) */ static int gconf_client_glob_error_cb(const char *path, int err) { mce_log(LL_WARN, "%s: glob: %s", path, g_strerror(err)); return 0; } /** Process config data from /etc/mce/NN.xxx.conf files */ static void gconf_client_load_overrides(GConfClient *self) { static const char pattern[] = MCE_CONF_DIR"/[0-9][0-9]*.conf"; glob_t gb; memset(&gb, 0, sizeof gb); if( glob(pattern, 0, gconf_client_glob_error_cb, &gb) != 0 ) { mce_log(LL_NOTICE, "no mce config override files found"); goto cleanup; } for( size_t i = 0; i < gb.gl_pathc; ++i ) { const char *path = gb.gl_pathv[i]; gconf_client_load_values(self, path); } cleanup: globfree(&gb); } /** Set default value state based on the current data * * The gconf_client_save_values() function will write only * keys that have changes since this function was called. */ static void gconf_client_mark_defaults(GConfClient *self) { for( GSList *e_iter = self->entries; e_iter; e_iter = e_iter->next ) { GConfEntry *entry = e_iter->data; char *str = gconf_value_str(entry->value); if( !str ) { mce_log(LL_WARN, "failed to serialize value of key %s", entry->key); continue; } free(entry->def), entry->def = str, str = 0; } } /** Reset to configured default values */ int gconf_client_reset_defaults(GConfClient *self, const char *keyish) { int result = 0; GSList *changed = 0; /* Reset all values first */ for( GSList *e_iter = self->entries; e_iter; e_iter = e_iter->next ) { GConfEntry *entry = e_iter->data; if( keyish && !strstr(entry->key, keyish) ) { continue; } if( entry->def ) { char *str = gconf_value_str(entry->value); if( !str || strcmp(str, entry->def) ) { mce_log(LL_DEBUG, "%s: %s -> %s", entry->key, str, entry->def); gconf_value_set_from_string(entry->value, entry->def); changed = g_slist_prepend(changed, entry->key); ++result; } } } /* Then send change notifications */ changed = g_slist_reverse(changed); for( GSList *item = changed; item; item = item->next ) { const char *key = item->data; gconf_client_notify_change(self, key); } g_slist_free(changed); return result; } static void gconf_client_free_default(void) { if( default_client ) { g_slist_free_full(default_client->entries, gconf_entry_free_cb); g_slist_free_full(default_client->notify_list, gconf_client_notify_free_cb); free(default_client), default_client = 0; } if( gconf_signal_sent ) { g_hash_table_unref(gconf_signal_sent), gconf_signal_sent = 0; } if( gconf_notify_made ) { g_hash_table_unref(gconf_notify_made), gconf_notify_made = 0; } } /** See GConf API documentation */ GConfClient * gconf_client_get_default(void) { if( default_client == 0 ) { GConfClient *self = calloc(1, sizeof *self); // initialize to hard coded defaults for( const setting_t *elem = gconf_defaults; elem->key; ++elem ) { mce_log(LL_DEBUG, "%s = '%s' (%s)", elem->key, elem->def, elem->type); GConfEntry *add = gconf_entry_init(elem->key, elem->type, elem->def); self->entries = g_slist_prepend(self->entries, add); } self->entries = g_slist_reverse(self->entries); // let gconf_client_is_valid() know about this default_client = self; atexit(gconf_client_free_default); // override hard coded defaults via /etc/nn.*.conf gconf_client_load_overrides(self); // mark down what the state is after hardcoded + overrides gconf_client_mark_defaults(self); // load custom values gconf_client_load_values(self, VALUES_PATH); // save back - will be nop unless defaults have changed since last save gconf_client_save_values(self, VALUES_PATH); #if GCONF_ENABLE_DEBUG_LOGGING if( gconf_log_debug_p() ) { gconf_client_debug(self); } #endif } return default_client; } /** See GConf API documentation */ void gconf_client_add_dir(GConfClient *client, const gchar *dir, GConfClientPreloadType preload, GError **err) { unused(client), unused(err), unused(dir), unused(preload); // NOP - not needed to fullfill MCE requirements return; }; /** Locate GConfEntry by key */ static GConfEntry * gconf_client_find_entry(GConfClient *self, const gchar *key, GError **err) { GConfEntry *res = 0; if( !gconf_client_is_valid(self, err) ) { goto cleanup; } for( GSList *e_iter = self->entries; e_iter; e_iter = e_iter->next ) { GConfEntry *entry = e_iter->data; if( !strcmp(entry->key, key) ) { res = entry; break; } } if( !res ) { #if 0 /* missing key is ok, just return NULL - this is what real * gconf does, but ... */ gconf_log_debug("non-existing key '%s' requested", key); #else /* since we do not support dynamically adding new keys, it is * better to have visibility to accessing non-existing keys */ gconf_set_error(err, GCONF_ERROR_FAILED, "%s: does not exist", key); #endif } cleanup: return res; } /** Locate GConfValue by key */ static GConfValue * gconf_client_find_value(GConfClient *self, const gchar *key, GError **err) { GConfEntry *entry = gconf_client_find_entry(self, key, err); return entry ? entry->value : 0; } /** See GConf API documentation */ GConfValue * gconf_client_get(GConfClient *self, const gchar *key, GError **err) { // NOTE: should return a copy of value GConfValue *res = gconf_client_find_value(self, key, err); if( res ) { #if GCONF_ENABLE_DEBUG_LOGGING if( gconf_log_debug_p() ) { char *repr = gconf_value_repr(key, res); gconf_log_debug("GET %s", repr); free(repr); } #endif /* Since we know that MCE will not modify the GConfValue * we return -> skip deep copy and just increase refcount */ res->refcount += 1; } return res; } /** See GConf API documentation */ gboolean gconf_client_set_bool(GConfClient *client, const gchar *key, gboolean val, GError **err) { gboolean res = FALSE; GConfValue *value = gconf_client_find_value(client, key, err); if( value && gconf_require_type(key, value, GCONF_VALUE_BOOL, err) ) { bool changed = gconf_value_set_bool(value, val); res = TRUE; #if GCONF_ENABLE_DEBUG_LOGGING if( gconf_log_debug_p() ) { char *repr = gconf_value_repr(key, value); gconf_log_debug("SET %s", repr); free(repr); } #endif if( changed ) { gconf_client_notify_change(client, key); } else { mce_log(LL_DEBUG, "key %s - no change", key); } } return res; } /** See GConf API documentation */ gboolean gconf_client_set_int(GConfClient *client, const gchar *key, gint val, GError **err) { gboolean res = FALSE; GConfValue *value = gconf_client_find_value(client, key, err); if( value && gconf_require_type(key, value, GCONF_VALUE_INT, err) ) { bool changed = gconf_value_set_int(value, val); res = TRUE; #if GCONF_ENABLE_DEBUG_LOGGING if( gconf_log_debug_p() ) { char *repr = gconf_value_repr(key, value); gconf_log_debug("SET %s", repr); free(repr); } #endif if( changed ) { gconf_client_notify_change(client, key); } else { mce_log(LL_DEBUG, "key %s - no change", key); } } return res; } /** See GConf API documentation */ gboolean gconf_client_set_float(GConfClient *client, const gchar *key, double val, GError **err) { gboolean res = FALSE; GConfValue *value = gconf_client_find_value(client, key, err); if( value && gconf_require_type(key, value, GCONF_VALUE_FLOAT, err) ) { bool changed = gconf_value_set_float(value, val); res = TRUE; #if GCONF_ENABLE_DEBUG_LOGGING if( gconf_log_debug_p() ) { char *repr = gconf_value_repr(key, value); gconf_log_debug("SET %s", repr); free(repr); } #endif if( changed ) { gconf_client_notify_change(client, key); } else { mce_log(LL_DEBUG, "key %s - no change", key); } } return res; } /** See GConf API documentation */ gboolean gconf_client_set_string(GConfClient *client, const gchar *key, const gchar *val, GError **err) { gboolean res = FALSE; GConfValue *value = gconf_client_find_value(client, key, err); if( value && gconf_require_type(key, value, GCONF_VALUE_STRING, err) ) { bool changed = gconf_value_set_string(value, val); res = TRUE; #if GCONF_ENABLE_DEBUG_LOGGING if( gconf_log_debug_p() ) { char *repr = gconf_value_repr(key, value); gconf_log_debug("SET %s", repr); free(repr); } #endif if( changed ) { gconf_client_notify_change(client, key); } else { mce_log(LL_DEBUG, "key %s - no change", key); } } return res; } /** See GConf API documentation */ gboolean gconf_client_set_list(GConfClient *client, const gchar *key, GConfValueType list_type, GSList *list, GError **err) { gboolean res = FALSE; GConfValue *value = gconf_client_find_value(client, key, err); if( value && gconf_require_list_type(key, value, list_type, err) ) { bool changed = gconf_value_set_list(value, list); res = TRUE; #if GCONF_ENABLE_DEBUG_LOGGING if( gconf_log_debug_p() ) { char *repr = gconf_value_repr(key, value); gconf_log_debug("SET %s", repr); free(repr); } #endif if( changed ) { gconf_client_notify_change(client, key); } } return res; } /** See GConf API documentation */ void gconf_client_suggest_sync(GConfClient *client, GError **err) { if( gconf_client_is_valid(client, err) ) { // FIXME: do we need delayed save? gconf_client_save_values(client, VALUES_PATH); } } /* ========================================================================= * * * GConfClientNotify * * ========================================================================= */ /** Destroy GConfClientNotify object */ static void gconf_client_notify_free(GConfClientNotify *self) { // NOTE: explicitly allow gconf_client_notify_free(NULL) if( self ) { gconf_log_debug("id=%u, namespace=%s", self->id, self->namespace_section); if( self->destroy_notify ) { self->destroy_notify(self->user_data); } g_free(self->namespace_section); free(self); } } static void gconf_client_notify_free_cb(gpointer self) { gconf_client_notify_free(self); } /** Create GConfClientNotify object */ static GConfClientNotify * gconf_client_notify_new(const gchar *namespace_section, GConfClientNotifyFunc func, gpointer user_data, GFreeFunc destroy_notify) { static guint last_id = 0; GConfClientNotify *self = calloc(1, sizeof *self); self->id = ++last_id; self->namespace_section = g_strdup(namespace_section); self->func = func; self->user_data = user_data; self->destroy_notify = destroy_notify; gconf_log_debug("id=%u, namespace=%s", self->id, self->namespace_section); return self; } /** External change broadcast needed predicate */ static bool gconf_entry_signal_p(GConfEntry *entry) { bool changed = false; const char *prev = 0; char *curr = 0; if( !gconf_signal_sent ) { gconf_signal_sent = g_hash_table_new_full(g_str_hash, g_str_equal, free, free); } prev = g_hash_table_lookup(gconf_signal_sent, entry->key); curr = gconf_value_str(entry->value); changed = !eq(prev, curr); mce_log(LL_DEBUG, "%s: %s -> %s (%s)", entry->key, prev, curr, changed ? "BROADCAST" : "IGNORE"); if( changed ) { g_hash_table_insert(gconf_signal_sent, strdup(entry->key), curr), curr = 0; } free(curr); return changed; } /** Internal change notification needed predicate */ static bool gconf_entry_notify_p(GConfEntry *entry) { bool changed = false; const char *prev = 0; char *curr = 0; if( !gconf_notify_made ) { gconf_notify_made = g_hash_table_new_full(g_str_hash, g_str_equal, free, free); } prev = g_hash_table_lookup(gconf_notify_made, entry->key); curr = gconf_value_str(entry->value); changed = !eq(prev, curr); mce_log(LL_DEBUG, "%s: %s -> %s (%s)", entry->key, prev, curr, changed ? "NOTIFY" : "IGNORE"); if( changed ) { g_hash_table_insert(gconf_notify_made, strdup(entry->key), curr), curr = 0; } free(curr); return changed; } /** Dispatch change notifications via installed callbacks */ static void gconf_client_notify_change(GConfClient *client, const gchar *namespace_section) { GError *err = 0; GConfEntry *entry = gconf_client_find_entry(client, namespace_section, &err); if( !entry || !gconf_entry_notify_p(entry) ) { goto EXIT; } entry->notify_changed = true; if( entry->notify_entered ) { goto EXIT; } entry->notify_entered = true; bool broadcast = gconf_entry_signal_p(entry); while( entry->notify_changed ) { entry->notify_changed = false; /* handle internal notifications */ for( GSList *item = client->notify_list; item; item = item->next ) { GConfClientNotify *notify = item->data; if( notify->func == 0 ) { continue; } if( !strcmp(notify->namespace_section, namespace_section) ) { gconf_log_debug("id=%u, namespace=%s", notify->id, notify->namespace_section); notify->func(client, notify->id, entry, notify->user_data); } } if( gconf_entry_signal_p(entry) ) { broadcast = true; } } entry->notify_entered = false; /* broadcast change also on dbus */ if( broadcast ) { mce_dbus_send_config_notification(entry); } EXIT: g_clear_error(&err); } /** See GConf API documentation */ guint gconf_client_notify_add(GConfClient *client, const gchar *namespace_section, GConfClientNotifyFunc func, gpointer user_data, GFreeFunc destroy_notify, GError **err) { GConfClientNotify *notify = 0; if( !gconf_client_is_valid(client, err) ) { goto cleanup; } if( gconf_client_find_value(client, namespace_section, err) ) { notify = gconf_client_notify_new(namespace_section, func, user_data, destroy_notify); client->notify_list = g_slist_prepend(client->notify_list, notify); } cleanup: return notify ? notify->id : 0; } /** See GConf API documentation */ void gconf_client_notify_remove(GConfClient *client, guint cnxn) { if( !gconf_client_is_valid(client, 0) ) { goto cleanup; } for( GSList *item = client->notify_list; item; item = item->next ) { GConfClientNotify *notify = item->data; if( notify->id == cnxn ) { gconf_client_notify_free(notify); client->notify_list = g_slist_delete_link(client->notify_list, item); break; } } cleanup: return; }