From f5eee5aec7a87a3c8a1f2e658231eca4b04561dd Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 11 Oct 2011 14:42:31 +0100 Subject: [PATCH] Add a configuration file for ClutterSettings ClutterSettings should be able to load its initial state by using configuration files in SYSCONFDIR and XDG_CONFIG_HOME. This allows Clutter to have a system (and user) configuration on platforms that do not have XSETTINGS bridges. --- clutter/Makefile.am | 2 + clutter/clutter-main.c | 42 ++++++++ clutter/clutter-settings-private.h | 16 +++ clutter/clutter-settings.c | 166 ++++++++++++++++++++++++++--- 4 files changed, 214 insertions(+), 12 deletions(-) create mode 100644 clutter/clutter-settings-private.h diff --git a/clutter/Makefile.am b/clutter/Makefile.am index d9b81a8c5..688617d11 100644 --- a/clutter/Makefile.am +++ b/clutter/Makefile.am @@ -27,6 +27,7 @@ AM_CPPFLAGS = \ -DCLUTTER_LIBDIR=\""$(libdir)"\" \ -DCLUTTER_DATADIR=\""$(datadir)"\" \ -DCLUTTER_LOCALEDIR=\""$(localedir)"\" \ + -DCLUTTER_SYSCONFDIR=\""$(sysconfdir)"\" \ -DCLUTTER_COMPILATION=1 \ -DCOGL_ENABLE_EXPERIMENTAL_API \ -DG_LOG_DOMAIN=\"Clutter\" \ @@ -240,6 +241,7 @@ source_h_priv = \ $(srcdir)/clutter-private.h \ $(srcdir)/clutter-profile.h \ $(srcdir)/clutter-script-private.h \ + $(srcdir)/clutter-settings-private.h \ $(srcdir)/clutter-stage-manager-private.h \ $(srcdir)/clutter-stage-private.h \ $(srcdir)/clutter-timeout-interval.h \ diff --git a/clutter/clutter-main.c b/clutter/clutter-main.c index 4f0a6c89f..89929095c 100644 --- a/clutter/clutter-main.c +++ b/clutter/clutter-main.c @@ -99,6 +99,7 @@ #include "clutter-master-clock.h" #include "clutter-private.h" #include "clutter-profile.h" +#include "clutter-settings-private.h" #include "clutter-stage-manager.h" #include "clutter-stage-private.h" #include "clutter-version.h" /* For flavour define */ @@ -215,6 +216,35 @@ clutter_threads_init_default (void) clutter_threads_unlock = clutter_threads_impl_unlock; } +#define N_CONF_DIRS 2 + +static void +clutter_config_read (void) +{ + ClutterSettings *settings; + gchar *config_path; + + settings = clutter_settings_get_default (); + + config_path = g_build_filename (CLUTTER_SYSCONFDIR, + "clutter-1.0", + "settings.ini", + NULL); + if (g_file_test (config_path, G_FILE_TEST_EXISTS)) + _clutter_settings_read_from_file (settings, config_path); + + g_free (config_path); + + config_path = g_build_filename (g_get_user_config_dir (), + "clutter-1.0", + "settings.ini", + NULL); + if (g_file_test (config_path, G_FILE_TEST_EXISTS)) + _clutter_settings_read_from_file (settings, config_path); + + g_free (config_path); +} + /** * clutter_get_show_fps: * @@ -1105,6 +1135,12 @@ clutter_context_get_default_unlocked (void) ctx->is_initialized = FALSE; ctx->motion_events_per_actor = TRUE; + /* create the default settings object, and store a back pointer to + * the backend singleton + */ + ctx->settings = clutter_settings_get_default (); + _clutter_settings_set_backend (ctx->settings, ctx->backend); + #ifdef CLUTTER_ENABLE_DEBUG ctx->timer = g_timer_new (); g_timer_start (ctx->timer); @@ -1370,6 +1406,12 @@ pre_parse_hook (GOptionContext *context, g_warning ("Locale not supported by C library.\n" "Using the fallback 'C' locale."); + /* read the configuration file, if it exists; the configuration file + * determines the initial state of the settings, so that command line + * arguments can override them. + */ + clutter_config_read (); + clutter_context = _clutter_context_get_default (); clutter_context->id_pool = _clutter_id_pool_new (256); diff --git a/clutter/clutter-settings-private.h b/clutter/clutter-settings-private.h new file mode 100644 index 000000000..112f54236 --- /dev/null +++ b/clutter/clutter-settings-private.h @@ -0,0 +1,16 @@ +#ifndef __CLUTTER_SETTINGS_PRIVATE_H__ +#define __CLUTTER_SETTINGS_PRIVATE_H__ + +#include +#include + +G_BEGIN_DECLS + +void _clutter_settings_set_backend (ClutterSettings *settings, + ClutterBackend *backend); +void _clutter_settings_read_from_file (ClutterSettings *settings, + const gchar *file); + +G_END_DECLS + +#endif /* __CLUTTER_SETTINGS_PRIVATE_H__ */ diff --git a/clutter/clutter-settings.c b/clutter/clutter-settings.c index 0bc43d965..ffbc605a9 100644 --- a/clutter/clutter-settings.c +++ b/clutter/clutter-settings.c @@ -118,6 +118,9 @@ settings_update_font_options (ClutterSettings *self) cairo_subpixel_order_t subpixel_order = CAIRO_SUBPIXEL_ORDER_DEFAULT; cairo_font_options_t *options; + if (self->backend == NULL) + return; + options = cairo_font_options_create (); cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_ON); @@ -187,7 +190,8 @@ settings_update_font_name (ClutterSettings *self) { CLUTTER_NOTE (BACKEND, "New font-name: %s", self->font_name); - g_signal_emit_by_name (self->backend, "font-changed"); + if (self->backend != NULL) + g_signal_emit_by_name (self->backend, "font-changed"); } static void @@ -195,13 +199,17 @@ settings_update_resolution (ClutterSettings *self) { CLUTTER_NOTE (BACKEND, "New resolution: %.2f", self->resolution); - g_signal_emit_by_name (self->backend, "resolution-changed"); + if (self->backend != NULL) + g_signal_emit_by_name (self->backend, "resolution-changed"); } static void settings_update_fontmap (ClutterSettings *self, guint stamp) { + if (self->backend == NULL) + return; + #ifdef HAVE_PANGO_FT2 CLUTTER_NOTE (BACKEND, "Update fontmaps (stamp: %d)", stamp); @@ -391,7 +399,8 @@ clutter_settings_dispatch_properties_changed (GObject *gobject, klass->dispatch_properties_changed (gobject, n_pspecs, pspecs); /* emit settings-changed just once for multiple properties */ - g_signal_emit_by_name (self->backend, "settings-changed"); + if (self->backend != NULL) + g_signal_emit_by_name (self->backend, "settings-changed"); } static void @@ -405,13 +414,17 @@ clutter_settings_class_init (ClutterSettingsClass *klass) * A back pointer to the #ClutterBackend * * Since: 1.4 + * + * Deprecated: 1.10 */ obj_props[PROP_BACKEND] = g_param_spec_object ("backend", "Backend", "A pointer to the backend", CLUTTER_TYPE_BACKEND, - CLUTTER_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY); + CLUTTER_PARAM_WRITABLE | + G_PARAM_DEPRECATED | + G_PARAM_CONSTRUCT_ONLY); /** * ClutterSettings:double-click-time: @@ -656,14 +669,143 @@ clutter_settings_init (ClutterSettings *self) ClutterSettings * clutter_settings_get_default (void) { - ClutterMainContext *context = _clutter_context_get_default (); + static ClutterSettings *settings = NULL; - if (G_LIKELY (context->settings != NULL)) - return context->settings; + if (G_UNLIKELY (settings == NULL)) + settings = g_object_new (CLUTTER_TYPE_SETTINGS, NULL); - context->settings = g_object_new (CLUTTER_TYPE_SETTINGS, - "backend", context->backend, - NULL); - - return context->settings; + return settings; +} + +void +_clutter_settings_set_backend (ClutterSettings *settings, + ClutterBackend *backend) +{ + g_assert (CLUTTER_IS_SETTINGS (settings)); + g_assert (CLUTTER_IS_BACKEND (backend)); + + settings->backend = backend; +} + +#define SETTINGS_GROUP "Settings" + +void +_clutter_settings_read_from_file (ClutterSettings *settings, + const gchar *file) +{ + GObjectClass *settings_class; + GObject *settings_obj; + GParamSpec **pspecs; + GKeyFile *keyfile; + GError *error; + guint n_pspecs, i; + + error = NULL; + keyfile = g_key_file_new (); + g_key_file_load_from_file (keyfile, file, G_KEY_FILE_NONE, &error); + if (error != NULL) + { + g_critical ("Unable to read configuration from '%s': %s", + file, + error->message); + g_error_free (error); + goto out; + } + + if (!g_key_file_has_group (keyfile, SETTINGS_GROUP)) + goto out; + + settings_obj = G_OBJECT (settings); + settings_class = G_OBJECT_GET_CLASS (settings); + pspecs = g_object_class_list_properties (settings_class, &n_pspecs); + + CLUTTER_NOTE (BACKEND, "Reading settings from '%s'", file); + + for (i = 0; i < n_pspecs; i++) + { + GParamSpec *pspec = pspecs[i]; + const gchar *p_name = pspec->name; + GType p_type = G_TYPE_FUNDAMENTAL (pspec->value_type); + GValue value = G_VALUE_INIT; + GError *key_error = NULL; + + g_value_init (&value, p_type); + + switch (p_type) + { + case G_TYPE_INT: + case G_TYPE_UINT: + { + gint val; + + val = g_key_file_get_integer (keyfile, + SETTINGS_GROUP, p_name, + &key_error); + if (p_type == G_TYPE_INT) + g_value_set_int (&value, val); + else + g_value_set_uint (&value, val); + } + break; + + case G_TYPE_BOOLEAN: + { + gboolean val; + + val = g_key_file_get_boolean (keyfile, + SETTINGS_GROUP, p_name, + &key_error); + g_value_set_boolean (&value, val); + } + break; + + case G_TYPE_FLOAT: + case G_TYPE_DOUBLE: + { + gdouble val; + + val = g_key_file_get_double (keyfile, + SETTINGS_GROUP, p_name, + &key_error); + if (p_type == G_TYPE_FLOAT) + g_value_set_float (&value, val); + else + g_value_set_double (&value, val); + } + break; + + case G_TYPE_STRING: + { + const gchar *val; + + val = g_key_file_get_string (keyfile, + SETTINGS_GROUP, p_name, + &key_error); + g_value_set_string (&value, val); + } + break; + } + + if (key_error != NULL && + key_error->domain != G_KEY_FILE_ERROR && + key_error->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND) + { + g_critical ("Unable to read the value for setting '%s' in '%s': %s", + p_name, + file, + key_error->message); + } + + if (key_error == NULL) + g_object_set_property (settings_obj, p_name, &value); + else + g_error_free (key_error); + + g_value_unset (&value); + } + + g_free (pspecs); + +out: + g_key_file_free (keyfile); }