All pastes #258790 Raw Edit

Miscellany

public text v1 · immutable
#258790 ·published 2006-11-26 21:04 UTC
rendered paste body
//LIBBERYLSETTINGS
//SO VERSION 0
//see spec in header
#include <beryl-settings.h>
#include <string.h>
#include <malloc.h>
#include <libintl.h>
#include <dlfcn.h>
#include <beryl.h>

CompDisplay *display = NULL;

#define USE_GCONF

#ifdef USE_GCONF
#include <gconf/gconf.h>
#include <gconf/gconf-client.h>
#include <gconf/gconf-value.h>

static GConfClient * client = NULL;

GConfValueType beryl_to_gconf_type(BerylSettingType bst);
#endif

#define NEW(a,b) \
    a * b = malloc(sizeof(a)); memset((b),0,sizeof(a))

// some function prototypes
static void set_array_from_binding(gint * array, BerylSettingValue * value);
static void set_array_from_color(BerylSettingValue * value, gint * array);
static GSList *set_gslist_from_color(GSList *list, BerylSettingValue *value);
static GSList *set_gslist_from_binding(GSList * list, BerylSettingValue * value);
//STATIC FUNCTIONS
static void copy_value(BerylSettingValue * dest, BerylSettingValue * src)
{
    BerylSettingType type;
    if (src->is_list_child)
        type=src->parent->info.for_list.list_of_type;
    else
        type=src->parent->type;
    dest->parent=src->parent;
    dest->is_list_child=src->is_list_child;
    if (type != BERYL_SETTING_TYPE_LIST)
    {
        if (type != BERYL_SETTING_TYPE_STRING)
            memcpy(&dest->value,&src->value,sizeof(BerylSettingValueUnion));
        else
            dest->value.as_string=g_strdup(src->value.as_string);
    }
    else
    {
        GSList * newsrc;
        dest->value.as_list=NULL;
        for (newsrc=src->value.as_list;newsrc;newsrc=newsrc->next)
        {
            NEW(BerylSettingValue,newdest);
            copy_value(newdest,newsrc->data);
            dest->value.as_list=g_slist_append(dest->value.as_list,newdest);
        }
    }
}

static void copy_from_default(BerylSetting * setting)
{
    copy_value(&setting->value,&setting->default_value);
}


static void init_info(BerylSettingInfo * info, CompOption * o, BerylSettingType type,
        gchar * gettext_domain)
{
    switch(type)
    {
        case BERYL_SETTING_TYPE_INT:
            info->for_int.min=o->rest.i.min;
            info->for_int.max=o->rest.i.max;
            break;
        case BERYL_SETTING_TYPE_FLOAT:
            info->for_float.min=o->rest.f.min;
            info->for_float.max=o->rest.f.max;
            info->for_float.precision=o->rest.f.precision;
            break;
        case BERYL_SETTING_TYPE_STRING:
            if (o->rest.s.nString && o->rest.s.string)
            {
                int i;
                for (i=0;i<o->rest.s.nString;i++)
                {
                    info->for_string.raw_values=g_slist_append(
                            info->for_string.raw_values,g_strdup(o->rest.s.string[i]));
                    info->for_string.allowed_values=g_slist_append(
                            info->for_string.allowed_values,
                            g_strdup(dgettext(gettext_domain,o->rest.s.string[i])));
                }
            }
            break;
        case BERYL_SETTING_TYPE_BINDING:
            info->for_bind.key=(o->value.action.state & 
                    CompActionStateInitKey) && TRUE;
            info->for_bind.button=(o->value.action.state & 
                    CompActionStateInitButton) && TRUE;
            info->for_bind.edge=(o->value.action.state & 
                    CompActionStateInitEdge) && TRUE;
            info->for_bind.bell=(o->value.action.state & 
                    CompActionStateInitBell) && TRUE;
            break;
        default:
            break;
    }
}

static void init_value(BerylSettingValue * value, CompOptionValue * from, BerylSettingType type,
        gchar * gettext_domain)
{
    switch(type)
    {
        case BERYL_SETTING_TYPE_INT:
            value->value.as_int=from->i;
            break;
        case BERYL_SETTING_TYPE_FLOAT:
            value->value.as_float=from->f;
            break;
        case BERYL_SETTING_TYPE_BOOL:
            value->value.as_bool=from->b;
            break;
        case BERYL_SETTING_TYPE_COLOR:
            {
                int i;
                for (i=0;i<4;i++)
                    value->value.as_color.array.array[i]=from->c[i];
            }
            break;
        case BERYL_SETTING_TYPE_STRING:
            value->value.as_string=g_strdup(from->s);
            break;
        case BERYL_SETTING_TYPE_BINDING:
            value->value.as_binding.enabled.value.key=(from->action.type & 
                    CompBindingTypeKey)?TRUE:FALSE;
            value->value.as_binding.enabled.value.button=(from->action.type & 
                    CompBindingTypeButton)?TRUE:FALSE;
            value->value.as_binding.button=from->action.button.button;
            value->value.as_binding.button_mod_mask=from->action.button.modifiers;
            value->value.as_binding.keysym=from->action.key.keysym;
            value->value.as_binding.key_mod_mask=from->action.key.modifiers;
            value->value.as_binding.on_bell=from->action.bell;
            value->value.as_binding.edge_mask=from->action.edgeMask;
            break;
        default:
            break;
    }
}

static BerylSettingType translate_type(CompOptionType type)
{
    switch(type)
    {
        case CompOptionTypeInt:
            return BERYL_SETTING_TYPE_INT;
        case CompOptionTypeFloat:
            return BERYL_SETTING_TYPE_FLOAT;
        case CompOptionTypeString:
            return BERYL_SETTING_TYPE_STRING;
        case CompOptionTypeColor:
            return BERYL_SETTING_TYPE_COLOR;
        case CompOptionTypeBool:
            return BERYL_SETTING_TYPE_BOOL;
        case CompOptionTypeAction:
            return BERYL_SETTING_TYPE_BINDING;
        case CompOptionTypeList:
            return BERYL_SETTING_TYPE_LIST;
        default:
            break;
    }
    return BERYL_SETTING_TYPE_COUNT;
}

static void init_option(BerylSettingsPlugin * plugin, CompOption * o, gboolean is_screen)
{
    NEW(BerylSetting,setting);
    setting->parent=plugin;
    setting->is_screen=is_screen;
    setting->is_default=TRUE;
    setting->name=g_strdup(o->name);
    setting->short_desc=g_strdup(
            dgettext(plugin->gettext_domain,o->shortDesc));
    setting->long_desc=g_strdup(dgettext(plugin->gettext_domain,o->longDesc));
    setting->type=translate_type(o->type);
    setting->default_value.parent=setting;
    if (setting->type==BERYL_SETTING_TYPE_LIST)
    {
        NEW(BerylSettingInfo,list_info);
        setting->info.for_list.list_of_type=translate_type(o->value.list.type);
        setting->info.for_list.list_of_info=list_info;
        init_info(setting->info.for_list.list_of_info,o,
                setting->info.for_list.list_of_type,plugin->gettext_domain);
        int i;
        for (i=0;i<o->value.list.nValue;i++)
        {
            NEW(BerylSettingValue,val);
            val->parent=setting;
            val->is_list_child=TRUE;
            init_value(val,&o->value.list.value[i],setting->info.for_list.list_of_type,
                    plugin->gettext_domain);
            setting->default_value.value.as_list=
                g_slist_append(setting->default_value.value.as_list,val);
        }
    }
    else
    {
        init_info(&setting->info,o,setting->type,plugin->gettext_domain);
        init_value(&setting->default_value,&o->value,setting->type,plugin->gettext_domain);
    }
    plugin->settings=g_slist_append(plugin->settings,setting);
    copy_from_default(setting);
}

static void comp_value_to_setting_value(CompOptionValue * comp_value, BerylSettingValue * beryl_value)
{
    BerylSettingType type;
    if (beryl_value->is_list_child)
        type=beryl_value->parent->info.for_list.list_of_type;
    else
        type=beryl_value->parent->type;
    beryl_value->parent->is_default=FALSE;
    switch(type)
    {
        case BERYL_SETTING_TYPE_INT:
            beryl_value->value.as_int=comp_value->i;
            break;
        case BERYL_SETTING_TYPE_FLOAT:
            beryl_value->value.as_float=comp_value->f;
            break;
        case BERYL_SETTING_TYPE_BOOL:
            beryl_value->value.as_bool=comp_value->b;
            break;
        case BERYL_SETTING_TYPE_COLOR:
            {
                int i;
                for (i=0;i<4;i++)
                    beryl_value->value.as_color.array.array[i]=comp_value->c[i];
            }
            break;
        case BERYL_SETTING_TYPE_STRING:
            if (beryl_value->value.as_string)
                free(beryl_value->value.as_string);
            beryl_value->value.as_string=g_strdup(comp_value->s);
            break;
        case BERYL_SETTING_TYPE_BINDING:
            beryl_value->value.as_binding.enabled.value.key =
                (comp_value->action.type & CompBindingTypeKey) != 0;
            beryl_value->value.as_binding.key_mod_mask = comp_value->action.key.modifiers;
            beryl_value->value.as_binding.keysym = comp_value->action.key.keysym;
            beryl_value->value.as_binding.enabled.value.button =
                (comp_value->action.type & CompBindingTypeButton) != 0;
            beryl_value->value.as_binding.button_mod_mask = comp_value->action.button.modifiers;
            beryl_value->value.as_binding.button = comp_value->action.button.button;
            beryl_value->value.as_binding.edge_mask = comp_value->action.edgeMask;
            beryl_value->value.as_binding.on_bell = comp_value->action.bell;
            break;
        case BERYL_SETTING_TYPE_LIST:
            {
                int i;
                beryl_setting_list_clear(beryl_value->parent);
                for (i=0;i<comp_value->list.nValue;i++)
                {
                    BerylSettingValue * val = beryl_setting_list_append(beryl_value->parent);
                    comp_value_to_setting_value(&comp_value->list.value[i],val);
                }
            }
            break;
        default:
            break;
    }

}

static void setting_value_to_comp_value(BerylSettingValue * beryl_value, CompOptionValue * comp_value)
{
    BerylSettingType type;
    if (beryl_value->is_list_child)
        type=beryl_value->parent->info.for_list.list_of_type;
    else
        type=beryl_value->parent->type;
    switch(type)
    {
        case BERYL_SETTING_TYPE_INT:
            comp_value->i=beryl_value->value.as_int;
            break;
        case BERYL_SETTING_TYPE_FLOAT:
            comp_value->f=beryl_value->value.as_float;
            break;
        case BERYL_SETTING_TYPE_BOOL:
            comp_value->b=beryl_value->value.as_bool;
            break;
        case BERYL_SETTING_TYPE_COLOR:
            {
                int i;
                for (i=0;i<4;i++)
                    comp_value->c[i]=beryl_value->value.as_color.array.array[i];
            }
            break;
        case BERYL_SETTING_TYPE_STRING:
            comp_value->s=strdup(beryl_value->value.as_string);
            break;
        case BERYL_SETTING_TYPE_BINDING:
            comp_value->action.type = CompBindingTypeNone;
            if (beryl_value->value.as_binding.enabled.value.key)
                comp_value->action.type |= CompBindingTypeKey;
            comp_value->action.key.modifiers = beryl_value->value.as_binding.key_mod_mask;
            comp_value->action.key.keysym = beryl_value->value.as_binding.keysym;
            if (beryl_value->value.as_binding.enabled.value.button)
                comp_value->action.type |= CompBindingTypeButton;
            comp_value->action.button.modifiers = beryl_value->value.as_binding.button_mod_mask;
            comp_value->action.button.button = beryl_value->value.as_binding.button;
            comp_value->action.edgeMask=beryl_value->value.as_binding.edge_mask;
            comp_value->action.bell=beryl_value->value.as_binding.on_bell;
            break;
        case BERYL_SETTING_TYPE_LIST:
            {
                int len=g_slist_length(beryl_value->value.as_list);
                comp_value->list.nValue=len;
                if (len)
                {
                    comp_value->list.value=malloc(sizeof(CompOptionValue)*len);
                    memset(comp_value->list.value,0,
                            sizeof(CompOptionValue)*len);
                    GSList * i;
                    int j=0;
                    for (i=beryl_value->value.as_list;i;i=i->next)
                    {
                        setting_value_to_comp_value(i->data,&comp_value->list.value[j]);
                        j++;
                    }
                }
            }
            break;
        default:
            break;
    }
}

static void load_plugin(BerylSettingsContext * context, gchar * filename)
{
    void * dlhand;
    gchar * err;
    PluginGetInfoProc getInfo;
    CompPluginVTable * vt;
    dlhand=dlopen(filename,RTLD_LAZY);
    if (!dlhand)
    {
        err=dlerror();
        if (err)
            fprintf(stderr, _("libberylsettings: dlopen: %s\n"),err);
        return;
    }
    dlerror();
    getInfo = (PluginGetInfoProc) dlsym (dlhand, "getCompPluginInfo");
    if ((err=dlerror()))
    {
        fprintf (stderr, _("libberylsettings: dlsym: %s\n"), err);
        dlclose(dlhand);
        return;
    }
    vt=getInfo();
    if (vt->version != BERYL_VERSION ||
            vt->struct_plugin_size != sizeof (CompPlugin) ||
            vt->struct_display_size != sizeof (CompDisplay) ||
            vt->struct_screen_size != sizeof (CompScreen) ||
            vt->struct_window_size != sizeof (CompWindow) ||
            vt->struct_texture_size != sizeof (CompTexture) ||
            vt->struct_icon_size != sizeof (CompIcon))
    {
        fprintf (stderr, _("libberylsettings: Couldn't get vtable from '%s' plugin\n"), filename);
        dlclose (dlhand);
        return;
    }
    //make sure a plugin by the same name isn't already loaded
    if (beryl_settings_context_find_plugin(context,vt->name))
    {
        dlclose(dlhand);
        return;
    }
    NEW(BerylSettingsPlugin,plugin);
    plugin->name=g_strdup(vt->name);
    plugin->gettext_domain=g_strdup(vt->gettext_domain);
    plugin->short_desc=g_strdup(dgettext(plugin->gettext_domain,vt->shortDesc));
    plugin->long_desc=g_strdup(dgettext(plugin->gettext_domain,vt->longDesc));
    int n;
    for (n=0;n<vt->nDeps;n++)
    {
        switch(vt->deps[n].rule)
        {
            case CompPluginRuleBefore:
                plugin->load_before=g_slist_append(
                        plugin->load_before,g_strdup(vt->deps[n].name));
                break;
            case CompPluginRuleAfter:
                plugin->load_after=g_slist_append(
                        plugin->load_after,g_strdup(vt->deps[n].name));
                break;
            case CompPluginRuleRequire:
                plugin->requires=g_slist_append(
                        plugin->requires,g_strdup(vt->deps[n].name));
                break;
            default:
                break;
        }
    }
    for (n=0;n<vt->nFeatures;n++)
    {
        plugin->provides=g_slist_append(plugin->provides,
                g_strdup(vt->features[n].name));
    }
    if (vt->getDisplayOptions)
    {
        int n;
        CompOption * o;
        o = vt->getDisplayOptions(NULL,&n);
        for (;n;n--)
        {
            init_option(plugin,o,FALSE);
            o++;
        }
    }
    if (vt->getScreenOptions)
    {
        int n;
        CompOption * o;
        o = vt->getScreenOptions(NULL,&n);
        for (;n;n--)
        {
            init_option(plugin,o,TRUE);
            o++;
        }
    }
    dlclose(dlhand);
    context->plugins=g_slist_append(context->plugins,plugin);
}

static void load_plugins(BerylSettingsContext * context, gchar * path)
/* iterates through $path and adds any plugins found to the context
            $context        the context to add to
            $path           the path to search for plugins (any .so file) */
{
    GDir * d;
    const gchar * n;
    d=g_dir_open(path,0,NULL);
    if (!d)
        return; // no such path
    while((n=g_dir_read_name(d)))
    {
        if (g_str_has_suffix(n,".so") && g_str_has_prefix(n,"lib"))
        {
            gchar * p;
            p=g_strconcat(path,"/",n,NULL);
            //actually load the plugin
            load_plugin(context,p);
            g_free(p);
        }
    }
    g_dir_close(d);
}

static void init_core_settings (BerylSettingsContext * context)
{
    NEW(BerylSettingsPlugin,plugin);
    int n;
    CompOption * o;
    display = malloc(sizeof(CompDisplay));
    compDisplayInitOptions(display,NULL,0);
    plugin->gettext_domain=g_strdup("beryl-core");
    o=compGetDisplayOptions(NULL,&n);
    for (;n;n--)
    {
        init_option(plugin,o,FALSE);
        o++;
    }
    o=compGetOptions(&n);
    for (;n;n--)
    {
        init_option(plugin,o,TRUE);
        o++;
    }
    context->plugins=g_slist_append(context->plugins,plugin);
}

static void free_value(BerylSettingValue * value)
{
    BerylSettingType type;
    if (value->is_list_child)
        type=value->parent->info.for_list.list_of_type;
    else
        type=value->parent->type;
    switch (type)
    {
        case BERYL_SETTING_TYPE_LIST:
            g_slist_foreach(value->value.as_list,
                    (GFunc)free_value,NULL);
            if (value->value.as_list)
                g_slist_free(value->value.as_list);
            break;
        case BERYL_SETTING_TYPE_STRING:
            if (value->value.as_string)
                g_free(value->value.as_string);
        default:
            break;
    }
    if (value->is_list_child)
        free(value);
}

static void free_string_info(BerylSettingInfo * info)
{
    g_slist_foreach(info->for_string.allowed_values,
            (GFunc)g_free,NULL);
    g_slist_foreach(info->for_string.raw_values,
            (GFunc)g_free,NULL);
    if (info->for_string.allowed_values)
        g_slist_free(info->for_string.allowed_values);
    if (info->for_string.raw_values)
        g_slist_free(info->for_string.raw_values);
}

static void free_setting(BerylSetting * setting)
{
    if (setting->name) g_free(setting->name);
    if (setting->short_desc) g_free(setting->short_desc);
    if (setting->long_desc) g_free(setting->long_desc);
    free_value(&setting->default_value);
    free_value(&setting->value);
    if (setting->type == BERYL_SETTING_TYPE_STRING)       
        free_string_info(&setting->info);
    if (setting->type == BERYL_SETTING_TYPE_LIST &&
            setting->info.for_list.list_of_type == BERYL_SETTING_TYPE_STRING)
        free_string_info(setting->info.for_list.list_of_info);
    if (setting->type == BERYL_SETTING_TYPE_LIST)
        free(setting->info.for_list.list_of_info);
    free(setting);
}

static void free_plugin(BerylSettingsPlugin * plugin)
{
    if (plugin->name) g_free(plugin->name);
    if (plugin->short_desc) g_free(plugin->short_desc);
    if (plugin->long_desc) g_free(plugin->long_desc);
    if (plugin->gettext_domain) g_free(plugin->gettext_domain);
    g_slist_foreach(plugin->load_after,(GFunc)g_free,NULL);
    g_slist_foreach(plugin->load_before,(GFunc)g_free,NULL);
    g_slist_foreach(plugin->provides,(GFunc)g_free,NULL);
    g_slist_foreach(plugin->requires,(GFunc)g_free,NULL);
    g_slist_foreach(plugin->settings,(GFunc)free_setting,NULL);
    if (plugin->load_after) g_slist_free(plugin->load_after);
    if (plugin->load_before) g_slist_free(plugin->load_before);
    if (plugin->provides) g_slist_free(plugin->provides);
    if (plugin->requires) g_slist_free(plugin->requires);
    if (plugin->settings) g_slist_free(plugin->settings);
    free(plugin);
}

typedef struct _BerylSettingsFindPlugin
{
    gchar * search_for;
    BerylSettingsPlugin * found;
} BerylSettingsFindPlugin;

static void find_plugin(BerylSettingsPlugin * plugin, BerylSettingsFindPlugin * find)
{
    if (find->found)
        return;
    if (find->search_for==NULL && plugin->name==NULL)
    {
        find->found=plugin;
        return;
    }
    if (find->search_for && plugin->name && strcmp(find->search_for,plugin->name)==0)
        find->found=plugin;
}

typedef struct _BerylSettingsFindSetting
{
    gchar * search_for;
    gboolean is_screen;
    BerylSetting * found;
} BerylSettingsFindSetting;

static void find_setting(BerylSetting * setting, BerylSettingsFindSetting * find)
{
    if (find->found)
        return;
    if (find->search_for && 
            setting->name && 
            find->is_screen==setting->is_screen &&
            strcmp(find->search_for,setting->name)==0)
        find->found=setting;
}

static void set_binding_value_from_array(BerylSettingValue * value, gint * array, gsize array_count)
{
    gboolean button_enabled;
    gboolean key_enabled;
    if (array_count==8)
    {
        //it's 8 if new-style semantics are used, allowing the enabled to be set separately
        //this should reintroduce some features gconf allowed but csm didn't
        button_enabled=array[6]?TRUE:FALSE;
        key_enabled=array[7]?TRUE:FALSE;
    }
    else if (array_count==6)
    {
        button_enabled=array[1]?TRUE:FALSE;
        key_enabled=array[3]?TRUE:FALSE;
    }
    if (array_count==6 || array_count==8)
    {
        beryl_setting_value_set_key_enabled(value,&key_enabled);
        beryl_setting_value_set_button_enabled(value,&button_enabled);
        beryl_setting_value_set_buttonmods(value,&array[0]);
        beryl_setting_value_set_button(value,&array[1]);
        beryl_setting_value_set_keymods(value,&array[2]);
        beryl_setting_value_set_keysym(value,&array[3]);
        beryl_setting_value_set_edgemask(value,&array[4]);
        gboolean bell_set=array[5]?TRUE:FALSE;
        beryl_setting_value_set_bell(value,&bell_set);
    }
}

static void set_color_value_from_array(BerylSettingValue * value, gint * array)
{
    BerylSettingColorValue color_val;
    memcpy(color_val.array.array,array,sizeof(gint)*4);
    beryl_setting_value_set_color(value,&color_val);
}

// g_slist_nth_data(data,n) returns a gpointer, so * to dereference?
static void set_binding_value_from_gslist(BerylSettingValue * value, GSList *data, gint num_per, gint start)
{
    gboolean button_enabled;
    gboolean key_enabled;
    if (num_per==8)
    {
        //it's 8 if new-style semantics are used, allowing the enabled to be set separately
        //this should reintroduce some features gconf allowed but csm didn't
        button_enabled = g_slist_nth_data(data,start+6)?TRUE:FALSE;
        key_enabled = g_slist_nth_data(data,start+7)?TRUE:FALSE;
    }
    else if (num_per==6)
    {
        button_enabled = g_slist_nth_data(data, start + 1)?TRUE:FALSE;
        key_enabled = g_slist_nth_data(data, start + 3)?TRUE:FALSE;
    };

    if (num_per==6 || num_per==8)
    {
        beryl_setting_value_set_key_enabled(value,&key_enabled);
        beryl_setting_value_set_button_enabled(value,&button_enabled);
        beryl_setting_value_set_buttonmods(value,g_slist_nth_data(data, start + 0));
        beryl_setting_value_set_button(value,g_slist_nth_data(data, start + 1));
        beryl_setting_value_set_keymods(value,g_slist_nth_data(data, start + 2));
        beryl_setting_value_set_keysym(value,g_slist_nth_data(data, start + 3));
        beryl_setting_value_set_edgemask(value, g_slist_nth_data(data, start + 4));
        gboolean bell_set = g_slist_nth_data(data, start + 5)?TRUE:FALSE;
        beryl_setting_value_set_bell(value,&bell_set);
    };
}

static void set_color_value_from_gslist(BerylSettingValue *value, GSList *data, gint start)
{
    BerylSettingColorValue color_val;

    color_val.array.array[0] = (gint) g_slist_nth_data(data, start + 0);
    color_val.array.array[1] = (gint) g_slist_nth_data(data, start + 1);
    color_val.array.array[2] = (gint) g_slist_nth_data(data, start + 2);
    color_val.array.array[3] = (gint) g_slist_nth_data(data, start + 3);

    beryl_setting_value_set_color(value,&color_val);
}

#ifdef USE_GCONF
GConfValueType beryl_to_gconf_type(BerylSettingType bst)
{
    switch(bst)
    {
        case BERYL_SETTING_TYPE_BOOL:
            return GCONF_VALUE_BOOL;
        break;
        case BERYL_SETTING_TYPE_INT:
        case BERYL_SETTING_TYPE_COLOR:
        case BERYL_SETTING_TYPE_BINDING:
	    return GCONF_VALUE_INT;
        break;
        case BERYL_SETTING_TYPE_STRING:
            return GCONF_VALUE_STRING;
        break;
        case BERYL_SETTING_TYPE_FLOAT:
	    return GCONF_VALUE_FLOAT;
        break;
        case     BERYL_SETTING_TYPE_COUNT:
                 printf("A count...\n");
                 return GCONF_VALUE_INT;
        default:
            return GCONF_VALUE_INVALID;
        break;
    }
}

static void read_setting(BerylSetting * setting, GConfClient *client)
{
    gboolean bool_val;
    gint int_val;
    gdouble float_val;
    gchar * str_val;
    gsize ret_count;
    GSList *list = NULL;
    int i = 0;

    beryl_setting_reset_to_default(setting);

    // TODO: If it's a key that metacity also uses... load from that instead
    gchar *keyname=g_strconcat(setting->is_screen?"s_":"a_",setting->name,NULL);
    gchar *groupname = setting->parent->name?setting->parent->name:"_";
    gchar *temp = g_strconcat("/apps/beryl/",groupname,NULL);
        groupname=g_strconcat(temp, "/", NULL);
    g_free(temp);

    gchar *gconf_path = g_strconcat(groupname,keyname, NULL);

    g_free(groupname);
    g_free(keyname);

    GConfValue *key = gconf_client_get_without_default(client, gconf_path, NULL);
    g_free(gconf_path);

    if (key == NULL)
        return;

    setting->is_default=FALSE;
    switch(setting->type)
    {
        case BERYL_SETTING_TYPE_INT:
            int_val = gconf_value_get_int(key);
            beryl_setting_value_set_int(&setting->value, &int_val);
            break;
        case BERYL_SETTING_TYPE_FLOAT:
                float_val = gconf_value_get_float(key);
                beryl_setting_value_set_float(&setting->value, &float_val);
            break;
        case BERYL_SETTING_TYPE_BOOL:
                bool_val = gconf_value_get_bool(key);
                beryl_setting_value_set_bool(&setting->value, &bool_val);
            break;
        case BERYL_SETTING_TYPE_COLOR:
            list = gconf_value_get_list(key);
            if (list && g_slist_length(list) == 4)
                    set_color_value_from_gslist(&setting->value,list, 0);
            g_slist_free(list);
            break;
        case BERYL_SETTING_TYPE_STRING:
            //FIXME probably need _set_raw_string here
            //but since beryl_setting_value_set_string(&setting->value,(const gchar **) &str_val);
            str_val = gconf_value_get_string(key);
            setting->value.value.as_string=g_strdup(str_val);
            setting->is_default=FALSE;
            g_free(str_val);
            break;
        case BERYL_SETTING_TYPE_BINDING:
            list=gconf_value_get_list(key);
            if (list)
                set_binding_value_from_gslist(&setting->value,list, g_slist_length(list), 0);
            g_slist_free(list);
            break;
        case BERYL_SETTING_TYPE_LIST:
            list = gconf_value_get_list(key);
            ret_count = g_slist_length(list);
            if (!list || ret_count == 0)
            {
                g_slist_free(list);
		break;
            };

            switch(setting->info.for_list.list_of_type)
            {
                case BERYL_SETTING_TYPE_STRING:
                    beryl_setting_list_clear(setting);
                    for (i = 0;i < ret_count; i++)
                    {
                        BerylSettingValue * value=beryl_setting_list_append(setting);
                        value->value.as_string = g_strdup(g_slist_nth_data(list, i));
                        value->parent->is_default=FALSE;
                        //beryl_setting_value_set_string(value,(const gchar **) &str_ret[i]);
                    }

                    g_slist_free(list);
                    break;
                case BERYL_SETTING_TYPE_FLOAT:
                    beryl_setting_list_clear(setting);
                    for (i=0;i<ret_count;i++)
                    {
                        BerylSettingValue * value=beryl_setting_list_append(setting);
                        beryl_setting_value_set_float(value, g_slist_nth_data(list, i));
                    }

                    g_slist_free(list);
                    break;
                case BERYL_SETTING_TYPE_INT:
                    beryl_setting_list_clear(setting);
                    for (i=0;i<ret_count;i++)
                    {
                        BerylSettingValue * value=beryl_setting_list_append(setting);
                        beryl_setting_value_set_int(value,g_slist_nth_data(list, i));
                    }

                    g_slist_free(list);
                    break;
                case BERYL_SETTING_TYPE_BOOL:
                    beryl_setting_list_clear(setting);
                    for (i=0;i<ret_count;i++)
                    {
                        BerylSettingValue * value=beryl_setting_list_append(setting);
                        beryl_setting_value_set_bool(value,g_slist_nth_data(list, i));
                    }

                    g_slist_free(list);
                    break;
                case BERYL_SETTING_TYPE_COLOR:
                    if (ret_count%4==0)
                    {
                        beryl_setting_list_clear(setting);
                        for (i = 0; i < ret_count / 4; i++)
                        {
                            BerylSettingValue * value=beryl_setting_list_append(setting);
                            set_color_value_from_gslist(value,list, i*4);
                        }
                    }

                    g_slist_free(list);
                    break;
                case BERYL_SETTING_TYPE_BINDING:
                    if (ret_count%6==0 || ret_count%8==0)
                    {
                        beryl_setting_list_clear(setting);
                        int num_per=(ret_count%6==0)?6:8;
                        for (i=0;i<ret_count/num_per;i++)
                        {
                            BerylSettingValue * value=beryl_setting_list_append(setting);
                            set_binding_value_from_gslist(value, list, num_per, i*num_per);
                        }
                    }

                    g_slist_free(list);
                    break;
                default:
                    break;
            }
            break;
        default:
            break;
    }
//    Some memory loss here? :(
//    if (key) gconf_value_free(key);
}

static void write_setting(BerylSetting * setting, GConfClient *client)
{
    GSList *list = NULL;
    int i;
 
    // TODO: If it's a key that metacity also uses... write to that as well
    gchar *keyname=g_strconcat(setting->is_screen?"s_":"a_",setting->name,NULL);
    gchar *groupname = setting->parent->name?setting->parent->name:"_";
    gchar *temp = g_strconcat("/apps/beryl/",groupname,NULL);
        groupname=g_strconcat(temp, "/", NULL);
    g_free(temp);

    gchar *gconf_path = g_strconcat(groupname,keyname, NULL);

    g_free(groupname);
    g_free(keyname);

//    if (setting->is_default && setting->is_screen)
//    {
//        gconf_client_unset(client, gconf_path, NULL);
//        g_free(gconf_path);
//        return;
//    }
    if (!gconf_client_key_is_writable(client, gconf_path, NULL))
    {
        printf("libberylsettings: main.c: write_setting: key \"%s\" not writable", gconf_path);
        g_free(gconf_path);
        return;
    };

    switch(setting->type)
    {
        case BERYL_SETTING_TYPE_STRING:
            gconf_client_set_string(client, gconf_path, setting->value.value.as_string, NULL);
            break;
        case BERYL_SETTING_TYPE_FLOAT:
                gconf_client_set_float(client, gconf_path, setting->value.value.as_float, NULL);
            break;
        case BERYL_SETTING_TYPE_INT:
            gconf_client_set_int(client,gconf_path, setting->value.value.as_int, NULL);
            break;
        case BERYL_SETTING_TYPE_BOOL:
            gconf_client_set_bool(client,gconf_path, setting->value.value.as_bool, NULL);
            break;
        case BERYL_SETTING_TYPE_BINDING:
            {
                list = set_gslist_from_binding(list,&setting->value);
                gconf_client_set_list(client,gconf_path, GCONF_VALUE_INT, list, NULL);
                g_slist_free(list);
            }
            break;
        case BERYL_SETTING_TYPE_COLOR:
            {
                list = set_gslist_from_color(list, &setting->value);
                gconf_client_set_list(client, gconf_path,GCONF_VALUE_INT,list, NULL);
                g_slist_free(list);
            }
            break;
        case BERYL_SETTING_TYPE_LIST:
        {
            GSList *data = g_slist_alloc();
            BerylSettingValue *v;
            for (i = 0; i < g_slist_length(setting->value.value.as_list); i++)
            {
                v = g_slist_nth_data(setting->value.value.as_list, i);
                switch (setting->info.for_list.list_of_type)
                {
                    case BERYL_SETTING_TYPE_STRING:
                      data = g_slist_append(data, v->value.as_string);
                    break;
                    default: break;
                };

            };

            gconf_client_set_list(client, gconf_path, beryl_to_gconf_type(setting->info.for_list.list_of_type), data, NULL);

            g_slist_free(data);
          }
            break;
         default:
            break;
    }

    g_free(gconf_path);
}

static void read_settings_for_plugin(BerylSettingsPlugin * plugin, GConfClient *client)
{
    g_slist_foreach(plugin->settings,(GFunc)read_setting, client);
}

static void write_settings_for_plugin(BerylSettingsPlugin * plugin, GConfClient *client)
{
    g_slist_foreach(plugin->settings,(GFunc)write_setting, client);
    gconf_client_suggest_sync(client, NULL);
}


void beryl_settings_context_read(BerylSettingsContext * context)
{
    g_type_init();

    if (!client)
        client = gconf_client_get_default();

    if (!GCONF_IS_CLIENT(client))
    {
        printf("Error getting client :(\n");
        return;
    };
    if (gconf_client_dir_exists(client, "/apps/beryl", NULL))
        gconf_client_add_dir(client, "/apps/beryl", GCONF_CLIENT_PRELOAD_RECURSIVE, NULL);

    g_slist_foreach(context->plugins,(GFunc)read_settings_for_plugin, client);
}

void beryl_settings_context_write(BerylSettingsContext * context)
{
    g_type_init();

    if (!client)
        client = gconf_client_get_default();

    if (gconf_client_dir_exists(client, "/apps/beryl", NULL))
        gconf_client_add_dir(client, "/apps/beryl", GCONF_CLIENT_PRELOAD_NONE, NULL);

    g_slist_foreach(context->plugins,(GFunc)write_settings_for_plugin,client);
}

#else

static void read_setting(BerylSetting * setting, GKeyFile * f)
{
    const gchar * groupname;
    gchar * keyname;
    gboolean bool_val;
    gint int_val;
    gdouble float_val;
    gchar * str_val;
    gchar ** str_ret;
    gsize ret_count;
    gint * int_ret;
    gboolean * bool_ret;
    GError * e;
    beryl_setting_reset_to_default(setting);
    if (setting->parent->name)
        groupname=setting->parent->name;
    else
        groupname="_"; // pseudo-plugin for core
    if (setting->is_screen)
        keyname=g_strconcat("s_",setting->name,NULL);
    else
        keyname=g_strconcat("a_",setting->name,NULL);
    if (!g_key_file_has_key(f,groupname,keyname,NULL))
    {
        g_free(keyname);
        return;
    }
    e=NULL;
    setting->is_default=FALSE;
    switch(setting->type)
    {
        case BERYL_SETTING_TYPE_INT:
            int_val=g_key_file_get_integer(f,groupname,keyname,&e);
            if (!e)
                beryl_setting_value_set_int(&setting->value,&int_val);
            break;
        case BERYL_SETTING_TYPE_FLOAT:
            str_val=g_key_file_get_string(f,groupname,keyname,&e);
            if (!e && str_val)
            {
                float_val=g_ascii_strtod(str_val,NULL);
                beryl_setting_value_set_float(&setting->value,&float_val);
                g_free(str_val);
            }
            break;
        case BERYL_SETTING_TYPE_BOOL:
            bool_val=g_key_file_get_boolean(f,groupname,keyname,&e);
            if (!e)
                beryl_setting_value_set_bool(&setting->value,&bool_val);
            break;
        case BERYL_SETTING_TYPE_COLOR:
            int_ret=g_key_file_get_integer_list(f,groupname,keyname,&ret_count,&e);
            if (!e && int_ret)
            {
                if (ret_count==4)
                    set_color_value_from_array(&setting->value,int_ret);
                g_free(int_ret);
            }
            break;
        case BERYL_SETTING_TYPE_STRING:
            str_val=g_key_file_get_string(f,groupname,keyname,&e);
            if (!e && str_val)
            {
                //FIXME probably need _set_raw_string here
                //but since
                //beryl_setting_value_set_string(&setting->value,(const gchar **) &str_val);
                setting->value.value.as_string=g_strdup(str_val);
                setting->is_default=FALSE;
                g_free(str_val);
            }
            break;
        case BERYL_SETTING_TYPE_BINDING:
            int_ret=g_key_file_get_integer_list(f,groupname,keyname,&ret_count,&e);
            if (!e && int_ret)
            {
                set_binding_value_from_array(&setting->value,int_ret,ret_count);
                g_free(int_ret);
            }
            break;
        case BERYL_SETTING_TYPE_LIST:
            switch(setting->info.for_list.list_of_type)
            {
                case BERYL_SETTING_TYPE_STRING:
                    str_ret=g_key_file_get_string_list(f,groupname,keyname,&ret_count,&e);
                    if (!e && str_ret)
                    {
                        beryl_setting_list_clear(setting);
                        int i;
                        for (i=0;i<ret_count;i++)
                        {
                            BerylSettingValue * value=beryl_setting_list_append(setting);
                            value->value.as_string=g_strdup(str_ret[i]);
                            value->parent->is_default=FALSE;
                            //beryl_setting_value_set_string(value,(const gchar **) &str_ret[i]);
                        }
                        g_strfreev(str_ret);
                    }
                    break;
                case BERYL_SETTING_TYPE_FLOAT:
                    str_ret=g_key_file_get_string_list(f,groupname,keyname,&ret_count,&e);
                    if (!e && str_ret)
                    {
                        beryl_setting_list_clear(setting);
                        int i;
                        for (i=0;i<ret_count;i++)
                        {
                            BerylSettingValue * value=beryl_setting_list_append(setting);
                            float_val=g_ascii_strtod(str_ret[i],NULL);
                            beryl_setting_value_set_float(value,&float_val);
                        }
                        g_strfreev(str_ret);
                    }
                    break;
                case BERYL_SETTING_TYPE_INT:
                    int_ret=g_key_file_get_integer_list(f,groupname,keyname,&ret_count,&e);
                    if (!e && int_ret)
                    {
                        beryl_setting_list_clear(setting);
                        int i;
                        for (i=0;i<ret_count;i++)
                        {
                            BerylSettingValue * value=beryl_setting_list_append(setting);
                            beryl_setting_value_set_int(value,&int_ret[i]);
                        }
                        g_free(int_ret);
                    }
                    break;
                case BERYL_SETTING_TYPE_BOOL:
                    bool_ret=g_key_file_get_boolean_list(f,groupname,keyname,&ret_count,&e);
                    if (!e && bool_ret)
                    {
                        beryl_setting_list_clear(setting);
                        int i;
                        for (i=0;i<ret_count;i++)
                        {
                            BerylSettingValue * value=beryl_setting_list_append(setting);
                            beryl_setting_value_set_bool(value,&bool_ret[i]);
                        }
                        g_free(bool_ret);
                    }
                    break;
                case BERYL_SETTING_TYPE_COLOR:
                    int_ret=g_key_file_get_integer_list(f,groupname,keyname,&ret_count,&e);
                    if (!e && int_ret)
                    {
                        if (ret_count%4==0)
                        {
                            beryl_setting_list_clear(setting);
                            int i;
                            for (i=0;i<ret_count/4;i++)
                            {
                                BerylSettingValue * value=beryl_setting_list_append(setting);
                                set_color_value_from_array(value,&int_ret[i*4]);
                            }
                        }
                        g_free(int_ret);
                    }
                    break;
                case BERYL_SETTING_TYPE_BINDING:
                    int_ret=g_key_file_get_integer_list(f,groupname,keyname,&ret_count,&e);
                    if (!e && int_ret)
                    {
                        if (ret_count%6==0 || ret_count%8==0)
                        {
                            beryl_setting_list_clear(setting);
                            int num_per=(ret_count%6==0)?6:8;
                            int i;
                            for (i=0;i<ret_count/num_per;i++)
                            {
                                BerylSettingValue * value=beryl_setting_list_append(setting);
                                set_binding_value_from_array(value,&int_ret[i*num_per],num_per);
                            }
                        }
                        g_free(int_ret);
                    }
                    break;
                default:
                    break;
            }
            break;
        default:
            break;
    }
    g_free(keyname);
}

static void write_setting(BerylSetting * setting, GKeyFile * f)
{
    gchar * groupname=setting->parent->name?setting->parent->name:"_";
    gchar * keyname=g_strconcat(setting->is_screen?"s_":"a_",setting->name,NULL);
    if (setting->is_default)
    {
        g_key_file_remove_key(f,groupname,keyname,NULL);
        g_free(keyname);
        return;
    }
    switch(setting->type)
    {
        case BERYL_SETTING_TYPE_STRING:
            g_key_file_set_string(f,groupname,keyname,setting->value.value.as_string);
            break;
        case BERYL_SETTING_TYPE_FLOAT:
            {
                gchar buffer[G_ASCII_DTOSTR_BUF_SIZE];
                g_ascii_dtostr(buffer,G_ASCII_DTOSTR_BUF_SIZE,setting->value.value.as_float);
                g_key_file_set_string(f,groupname,keyname,buffer);
            }
            break;
        case BERYL_SETTING_TYPE_INT:
            g_key_file_set_integer(f,groupname,keyname,setting->value.value.as_int);
            break;
        case BERYL_SETTING_TYPE_BOOL:
            g_key_file_set_boolean(f,groupname,keyname,setting->value.value.as_bool);
            break;
        case BERYL_SETTING_TYPE_BINDING:
            {
                gint bindval[8];
                set_array_from_binding(bindval,&setting->value);
                g_key_file_set_integer_list(f,groupname,keyname,bindval,8);
            }
            break;
        case BERYL_SETTING_TYPE_COLOR:
            {
                gint colorval[4];
                set_array_from_color(&setting->value,colorval);
                g_key_file_set_integer_list(f,groupname,keyname,colorval,4);
            }
            break;
        case BERYL_SETTING_TYPE_LIST:
            {
                gsize len=g_slist_length(setting->value.value.as_list);
                switch(setting->info.for_list.list_of_type)
                {
                    case BERYL_SETTING_TYPE_BOOL:
                        {
                            gboolean * list = malloc(len*sizeof(gboolean));
                            gboolean * ptr = list;
                            g_slist_foreach(setting->value.value.as_list,(GFunc)copy_bool,&ptr);
                            g_key_file_set_boolean_list(f,groupname,keyname,list,len);
                            free(list);
                        }
                        break;
                    case BERYL_SETTING_TYPE_INT:
                        {
                            gint * list = malloc(len*sizeof(gint));
                            gint * ptr = list;
                            g_slist_foreach(setting->value.value.as_list,(GFunc)copy_int,&ptr);
                            g_key_file_set_integer_list(f,groupname,keyname,list,len);
                            free(list);
                        }
                        break;
                    case BERYL_SETTING_TYPE_STRING:
                        {
                            gchar ** list = malloc((len+1)*sizeof(gchar *));
                            list[len]=NULL;
                            gchar ** ptr=list;
                            g_slist_foreach(setting->value.value.as_list,(GFunc)copy_string,&ptr);
                            g_key_file_set_string_list(f,groupname,keyname,(const gchar * const *)list,
                                    len);
                            g_strfreev(list); // TODO - I think this is safe with malloc()'d arrays
                        }
                        break;
                    case BERYL_SETTING_TYPE_FLOAT:
                        {
                            gchar ** list = malloc((len+1)*sizeof(gchar *));
                            list[len]=NULL;
                            gchar ** ptr=list;
                            g_slist_foreach(setting->value.value.as_list,(GFunc)copy_float,&ptr);
                            g_key_file_set_string_list(f,groupname,keyname,(const gchar * const *)list,
                                    len);
                            g_strfreev(list); // TODO - I think this is safe with malloc()'d arrays
                        }
                        break;
                    case BERYL_SETTING_TYPE_COLOR:
                        {
                            gint * list = malloc(len*sizeof(gint)*4);
                            gint * ptr = list;
                            g_slist_foreach(setting->value.value.as_list,(GFunc)copy_color,&ptr);
                            g_key_file_set_integer_list(f,groupname,keyname,list,len);
                            free(list);
                        }
                        break;
                    case BERYL_SETTING_TYPE_BINDING:
                        {
                            gint * list = malloc(len*sizeof(gint)*8);
                            gint * ptr = list;
                            g_slist_foreach(setting->value.value.as_list,(GFunc)copy_binding,&ptr);
                            g_key_file_set_integer_list(f,groupname,keyname,list,len);
                            free(list);
                        }
                        break;
                    default:
                        break;
                }
            }
            break;
        default:
            break;
    }
    g_free(keyname);
}

static void read_settings_for_plugin(BerylSettingsPlugin * plugin, GKeyFile * f)
{
    g_slist_foreach(plugin->settings,(GFunc)read_setting,f);
}

static void write_settings_for_plugin(BerylSettingsPlugin * plugin, GKeyFile * f)
{
    g_slist_foreach(plugin->settings,(GFunc)write_setting,f);
}

void beryl_settings_context_read(BerylSettingsContext * context)
{
    GKeyFile * f;
    gchar * path = g_strconcat(g_get_home_dir(),"/.beryl/settings",NULL);
    f=g_key_file_new();
    if (!g_key_file_load_from_file(f,path,0,NULL))
    {
        g_key_file_free(f);
        g_free(path);
        return;
    }
    g_free(path);
    g_slist_foreach(context->plugins,(GFunc)read_settings_for_plugin,f);
    g_key_file_free(f);
}

void beryl_settings_context_write(BerylSettingsContext * context)
{
    GKeyFile * f;
    gchar * path = g_strconcat(g_get_home_dir(),"/.beryl/",NULL);
    gchar * data;
    g_mkdir_with_parents(path,00755); // ensure path exists
    g_free(path);
    path=g_strconcat(g_get_home_dir(),"/.beryl/settings",NULL);
    f=g_key_file_new();
    g_key_file_load_from_file(f,path,0,NULL);
    g_slist_foreach(context->plugins,(GFunc)write_settings_for_plugin,f);
    if (g_file_test(path,G_FILE_TEST_IS_SYMLINK))
    {
        gchar * tmp = path;
        path=g_file_read_link(path,NULL);
        g_free(tmp);
    }
    data = g_key_file_to_data(f,NULL,NULL);
    g_file_set_contents(path,data,-1,NULL);
    g_free(path);
    g_free(data);
    g_key_file_free(f);
}

#endif

static void set_array_from_binding(gint * array, BerylSettingValue * value)
{
    array[0]=value->value.as_binding.button_mod_mask;
    array[1]=value->value.as_binding.button;
    array[2]=value->value.as_binding.key_mod_mask;
    array[3]=value->value.as_binding.keysym;
    array[4]=value->value.as_binding.edge_mask;
    array[5]=value->value.as_binding.on_bell?1:0;
    array[6]=value->value.as_binding.enabled.value.button?1:0;
    array[7]=value->value.as_binding.enabled.value.key?1:0;
}

static void set_array_from_color(BerylSettingValue * value, gint * array)
{
    memcpy(array,value->value.as_color.array.array,sizeof(gint)*4);
}

static GSList *set_gslist_from_binding(GSList * list, BerylSettingValue * value)
{
    gboolean bindval = value->value.as_binding.on_bell?1:0;
    gboolean bindbtn = value->value.as_binding.enabled.value.button?1:0;
    gboolean bindkey = value->value.as_binding.enabled.value.key?1:0;

    // Get us some happy empty RAM
    if (list) g_slist_free(list);
    list = g_slist_alloc();

    list = g_slist_append(list, &value->value.as_binding.button_mod_mask);
    list = g_slist_append(list, &value->value.as_binding.button);
    list = g_slist_append(list, &value->value.as_binding.key_mod_mask);
    list = g_slist_append(list, &value->value.as_binding.keysym);
    list = g_slist_append(list, &value->value.as_binding.edge_mask);
    list = g_slist_append(list, &bindval);
    list = g_slist_append(list, &bindbtn);
    list = g_slist_append(list, &bindkey);
    return list;
}

static GSList *set_gslist_from_color(GSList *list, BerylSettingValue *value)
{
    if (list) g_slist_free(list);
    list = g_slist_alloc();

    list = g_slist_append(list, &value->value.as_color.array.array[0]);
    list = g_slist_append(list, &value->value.as_color.array.array[1]);
    list = g_slist_append(list, &value->value.as_color.array.array[2]);
    list = g_slist_append(list, &value->value.as_color.array.array[3]);
    return list;
}


static void copy_bool(BerylSettingValue * value, gboolean ** ptr)
{
    *(*ptr++)=value->value.as_bool;
}

static void copy_int(BerylSettingValue * value, gint ** ptr)
{
    *(*ptr++)=value->value.as_int;
}

static void copy_float(BerylSettingValue * value, gchar *** ptr)
{
    gchar buffer[G_ASCII_DTOSTR_BUF_SIZE];
    *(*ptr++)=g_strdup(g_ascii_dtostr(buffer,G_ASCII_DTOSTR_BUF_SIZE,value->value.as_float));
}

static void copy_binding(BerylSettingValue * value, gint ** ptr)
{
    set_array_from_binding(*ptr,value);
    *ptr+=8;
}

static void copy_string(BerylSettingValue * value, gchar *** ptr)
{
    *(*ptr)=g_strdup(value->value.as_string);
    *ptr=*ptr+1;
}

static void copy_color(BerylSettingValue * value, gint ** ptr)
{
    set_array_from_color(value,*ptr);
    *ptr+=4;
}

typedef struct _BerylFindListSettingIndex
{
    int index;
    gboolean found;
    BerylSettingValue * value;
} BerylFindListSettingIndex;

static void find_index_of(BerylSettingValue * value, BerylFindListSettingIndex * find)
{
    if (find->found)
        return;
    if (find->value==value)
        find->found=TRUE;
    else
        find->index++;
}

static gboolean check_s_type(BerylSetting * setting, BerylSettingType type)
{
    if (setting->type==BERYL_SETTING_TYPE_LIST)
        return setting->info.for_list.list_of_type==type;
    else
        return setting->type==type;
}

static BerylSettingInfo * get_s_info(BerylSetting * setting)
{
    if (setting->type==BERYL_SETTING_TYPE_LIST)
        return setting->info.for_list.list_of_info;
    else
        return &setting->info;
}

static gboolean check_type(BerylSettingValue * value, BerylSettingType type)
{
    if (value->is_list_child)
        return value->parent->info.for_list.list_of_type==type;
    else
        return value->parent->type==type;
}

static BerylSettingInfo * get_info(BerylSettingValue * value)
{
    if (value->is_list_child)
        return value->parent->info.for_list.list_of_info;
    else
        return &value->parent->info;
}

//EXPORTED FUNCTIONS
BerylSettingsContext * beryl_settings_context_new(void)
{
    gchar * homeplugins=g_strconcat(g_get_home_dir(),"/.beryl/plugins",NULL);
    NEW(BerylSettingsContext,context);
    init_core_settings(context);
    load_plugins(context,homeplugins);
    load_plugins(context,PLUGINDIR);    // load after, because that way name conflicts
                                        // will lead to the home version being loaded
    g_free(homeplugins);
    return context;
}

void beryl_settings_context_destroy(BerylSettingsContext * context)
{
    g_slist_foreach(context->plugins,(GFunc)free_plugin,NULL);
    if (context->plugins) g_slist_free(context->plugins);
    free(context);
    free(display);
}

BerylSettingsPlugin * beryl_settings_context_find_plugin(BerylSettingsContext * context, gchar * name)
{
    BerylSettingsFindPlugin find;
    find.found=NULL;
    find.search_for=name;
    g_slist_foreach(context->plugins,(GFunc)find_plugin,&find);
    return find.found;
}

BerylSetting * beryl_settings_plugin_find_setting(BerylSettingsPlugin * plugin, gchar * name,
        gboolean is_screen)
{
    BerylSettingsFindSetting find;
    find.found=NULL;
    find.search_for=name;
    find.is_screen=is_screen;
    g_slist_foreach(plugin->settings,(GFunc)find_setting,&find);
    return find.found;
}



BerylSettingsContext * beryl_settings_context_comp_new(void)
{
    NEW(BerylSettingsContext,context);
    context->is_comp=TRUE;
    return context;
}

gboolean beryl_settings_context_comp_add_plugin(BerylSettingsContext * context, gchar * name)
{
    if (!context->is_comp) return FALSE;
    if (beryl_settings_context_find_plugin(context,name))
        return FALSE;
    NEW(BerylSettingsPlugin,plugin);
    if (name)
        plugin->name=g_strdup(name);
    plugin->gettext_domain=g_strdup("");
    context->plugins=g_slist_append(context->plugins,plugin);
    return TRUE;
}

#if 0 /* Incomplete */
void beryl_settings_context_comp_free_option(s_plugin, option)
{
	BerylSetting * setting;

	g_free(setting->name);
	g_free(setting->short_desc);
	g_free(setting->long_desc);
	free_value(setting->value);
}
#endif

gboolean beryl_settings_context_comp_add_option(BerylSettingsContext * context,
        gchar * plugin, CompOption * option, gboolean is_screen)
{
    if (!context->is_comp) return FALSE;
    BerylSettingsPlugin * s_plugin;
    if (!(s_plugin=beryl_settings_context_find_plugin(context,plugin)))
        return FALSE;
    if (beryl_settings_plugin_find_setting(s_plugin,option->name,is_screen))
        return FALSE;
    init_option(s_plugin,option,is_screen);
    return TRUE;
}

gboolean beryl_settings_context_comp_get_option_value(BerylSettingsContext * context,
        gchar * plugin, gchar * name, gboolean is_screen, CompOptionValue * value)
{
    if (!context->is_comp) return FALSE;
    BerylSettingsPlugin * s_plugin;
    BerylSetting * setting;
    if (!(s_plugin=beryl_settings_context_find_plugin(context,plugin)))
    {
        return FALSE;
    }
    if (!(setting=beryl_settings_plugin_find_setting(s_plugin,name,is_screen)))
    {
        return FALSE;
    }
    setting_value_to_comp_value(&setting->value,value);
    return TRUE;
}

gboolean beryl_settings_context_comp_set_option_value(BerylSettingsContext * context,
        gchar * plugin, gchar * name, gboolean is_screen, CompOptionValue * value)
{
    if (!context->is_comp) return FALSE;
    BerylSettingsPlugin * s_plugin;
    BerylSetting * setting;
    if (!(s_plugin=beryl_settings_context_find_plugin(context,plugin)))
        return FALSE;
    if (!(setting=beryl_settings_plugin_find_setting(s_plugin,name,is_screen)))
        return FALSE;
    setting->is_default=FALSE;
    comp_value_to_setting_value(value,&setting->value);
    return TRUE;
}

gboolean beryl_settings_send_reload_signal(void)
{
    gchar * args[] =
    {"killall","-u",(gchar *)g_get_user_name(),"-SIGUSR1","-r","^beryl$|^beryl-xgl$",NULL};
    gchar * ret=NULL;
    if (!g_spawn_sync(NULL,args,NULL,G_SPAWN_STDERR_TO_DEV_NULL | G_SPAWN_SEARCH_PATH,
                NULL,NULL,&ret,NULL,NULL,NULL) || !ret)
        return FALSE;
    return TRUE;
}

void beryl_setting_reset_to_default(BerylSetting * setting)
{
    //if (!setting->is_default)
    free_value(&setting->value);
    copy_from_default(setting);
    setting->is_default=TRUE;
}

void beryl_setting_list_clear(BerylSetting * setting)
{
    if (setting->type != BERYL_SETTING_TYPE_LIST)
        return;
    setting->is_default=FALSE;
    g_slist_foreach(setting->value.value.as_list,
            (GFunc)free_value,NULL);
    if (setting->value.value.as_list)
        g_slist_free(setting->value.value.as_list);
    setting->value.value.as_list=NULL;
}

BerylSettingValue * beryl_setting_list_append(BerylSetting * setting)
{
    if (setting->type != BERYL_SETTING_TYPE_LIST)
        return NULL;
    setting->is_default=FALSE;
    NEW(BerylSettingValue,value);
    value->parent=setting;
    value->is_list_child=TRUE;
    setting->value.value.as_list=g_slist_append(setting->value.value.as_list,value);
    return value;
}

gint beryl_setting_list_length(BerylSetting * setting)
{
    if (setting->type != BERYL_SETTING_TYPE_LIST)
        return -1;
    return g_slist_length(setting->value.value.as_list);
}

gint beryl_setting_list_value_index(BerylSettingValue * value)
{
    if (!value->is_list_child)
        return -1;
    BerylFindListSettingIndex find;
    find.found=FALSE;
    find.index=0;
    find.value=value;
    g_slist_foreach(value->parent->value.value.as_list,(GFunc)find_index_of,&find);
    if (find.found)
        return find.index;
    else
        return -1;
}

void beryl_setting_list_value_swap_with(BerylSettingValue * value, guint n)
{
    if (!value->is_list_child)
        return;
    GSList * a = g_slist_nth(value->parent->value.value.as_list,n);
    GSList * b = g_slist_find(value->parent->value.value.as_list,value);
    if (!a || !b)
        return;
    value->parent->is_default=FALSE;
    b->data=a->data;
    a->data=value;
}

void beryl_setting_list_value_move_before(BerylSettingValue * value, guint n)
{
    if (!value->is_list_child)
        return;
    GSList * a = g_slist_nth(value->parent->value.value.as_list,n);
    if (a && a->data==value)
        return;
    value->parent->is_default=FALSE;
    if (!a)
    {
        value->parent->value.value.as_list=g_slist_append(g_slist_remove(
                value->parent->value.value.as_list,value),value);
        return;
    }
    value->parent->value.value.as_list=g_slist_insert_before(
            g_slist_remove(value->parent->value.value.as_list,value),a,value);
}

void beryl_setting_list_value_remove(BerylSettingValue * value)
{
    if (!value->is_list_child)
        return;
    value->parent->is_default=FALSE;
    value->parent->value.value.as_list=g_slist_remove(
            value->parent->value.value.as_list,value);
    free_value(value);
}

BerylSettingValue * beryl_setting_get_primary_value(BerylSetting * setting)
{
    return &setting->value;
}

gboolean beryl_setting_value_get_int(BerylSettingValue * value, gint * data)
{
    if (!check_type(value,BERYL_SETTING_TYPE_INT))
        return FALSE;
    *data=value->value.as_int;
    return TRUE;
}

gboolean beryl_setting_value_get_float(BerylSettingValue * value, gdouble * data)
{
    if (!check_type(value,BERYL_SETTING_TYPE_FLOAT))
        return FALSE;
    *data=value->value.as_float;
    return TRUE;
}

gboolean beryl_setting_value_get_bool(BerylSettingValue * value, gboolean * data)
{
    if (!check_type(value,BERYL_SETTING_TYPE_BOOL))
        return FALSE;
    *data=value->value.as_bool;
    return TRUE;
}

gboolean beryl_setting_value_get_string(BerylSettingValue * value, const gchar ** data)
{
    if (!check_type(value,BERYL_SETTING_TYPE_STRING))
        return FALSE;
    BerylSettingInfo * info=get_info(value);
    if (info->for_string.raw_values)
    {

        GSList * reststring=info->for_string.raw_values;
        GSList * xlatstring=info->for_string.allowed_values;
        while(reststring)
        {
            if (strcmp(value->value.as_string,reststring->data)==0)
            {
                *data=xlatstring->data;
                return TRUE;
            }
            reststring=reststring->next;
            xlatstring=xlatstring->next;
        }
    }
    else
    {
        *data=value->value.as_string;
        return TRUE;
    }
    return FALSE;
}

gboolean beryl_setting_value_get_keysym(BerylSettingValue * value, gint * data)
{
    if (!check_type(value,BERYL_SETTING_TYPE_BINDING))
        return FALSE;
    *data=value->value.as_binding.keysym;
    return TRUE;
}

gboolean beryl_setting_value_get_keymods(BerylSettingValue * value, gint * data)
{
    if (!check_type(value,BERYL_SETTING_TYPE_BINDING))
        return FALSE;
    *data=value->value.as_binding.key_mod_mask;
    return TRUE;
}

gboolean beryl_setting_value_get_button(BerylSettingValue * value, gint * data)
{
    if (!check_type(value,BERYL_SETTING_TYPE_BINDING))
        return FALSE;
    *data=value->value.as_binding.button;
    return TRUE;
}

gboolean beryl_setting_value_get_buttonmods(BerylSettingValue * value, gint * data)
{
    if (!check_type(value,BERYL_SETTING_TYPE_BINDING))
        return FALSE;
    *data=value->value.as_binding.button_mod_mask;
    return TRUE;
}

gboolean beryl_setting_value_get_edgemask(BerylSettingValue * value, gint * data)
{
    if (!check_type(value,BERYL_SETTING_TYPE_BINDING))
        return FALSE;
    *data=value->value.as_binding.edge_mask;
    return TRUE;
}

gboolean beryl_setting_value_get_bell(BerylSettingValue * value, gboolean * data)
{
    if (!check_type(value,BERYL_SETTING_TYPE_BINDING))
        return FALSE;
    *data=value->value.as_binding.on_bell;
    return TRUE;
}

gboolean beryl_setting_value_get_key_enabled(BerylSettingValue * value, gboolean * data)
{
    if (!check_type(value,BERYL_SETTING_TYPE_BINDING))
        return FALSE;
    *data=value->value.as_binding.enabled.value.key;
    return TRUE;
}

gboolean beryl_setting_value_get_button_enabled(BerylSettingValue * value, gboolean * data)
{
    if (!check_type(value,BERYL_SETTING_TYPE_BINDING))
        return FALSE;
    *data=value->value.as_binding.enabled.value.button;
    return TRUE;
}

gboolean beryl_setting_value_get_color(BerylSettingValue * value, BerylSettingColorValue * data)
{
    if (!check_type(value,BERYL_SETTING_TYPE_COLOR))
        return FALSE;
    memcpy(data,&value->value.as_color,sizeof(BerylSettingColorValue));
    return TRUE;
}

gboolean beryl_setting_value_get_value_list(BerylSettingValue * value, GSList ** data)
{
    if (!check_type(value,BERYL_SETTING_TYPE_LIST))
        return FALSE;
    *data=value->value.as_list;
    return TRUE;
}

gboolean beryl_setting_value_set_int(BerylSettingValue * value, gint * data)
{
    if (!check_type(value,BERYL_SETTING_TYPE_INT))
        return FALSE;
    BerylSettingInfo * info = get_info(value);
    if (*data<info->for_int.min || *data>info->for_int.max)
        return FALSE;
    value->parent->is_default=FALSE;
    value->value.as_int=*data;
    return TRUE;
}

gboolean beryl_setting_value_set_float(BerylSettingValue * value, gdouble * data)
{
    if (!check_type(value,BERYL_SETTING_TYPE_FLOAT))
        return FALSE;
    BerylSettingInfo * info = get_info(value);
    if (*data<info->for_float.min || *data>info->for_float.max)
        return FALSE;
    value->parent->is_default=FALSE;
    value->value.as_float=*data;
    return TRUE;
}

gboolean beryl_setting_value_set_bool(BerylSettingValue * value, gboolean * data)
{
    if (!check_type(value,BERYL_SETTING_TYPE_BOOL))
        return FALSE;
    value->parent->is_default=FALSE;
    value->value.as_bool=*data;
    return TRUE;
}

gboolean beryl_setting_value_set_string(BerylSettingValue * value, const gchar ** data)
{
    if (!check_type(value,BERYL_SETTING_TYPE_STRING))
        return FALSE;
    BerylSettingInfo * info = get_info(value);
    if (info->for_string.raw_values)
    {
        GSList * reststring=info->for_string.raw_values;
        GSList * xlatstring=info->for_string.allowed_values;
        while(reststring)
        {
            if (strcmp(*data,xlatstring->data)==0)
            {
                if (value->value.as_string)
                    g_free(value->value.as_string);
                value->value.as_string=g_strdup(reststring->data);
                value->parent->is_default=FALSE;
                return TRUE;
            }
            reststring=reststring->next;
            xlatstring=xlatstring->next;
        }
    }
    else
    {
        if (value->value.as_string)
            g_free(value->value.as_string);
        value->value.as_string=g_strdup(*data);
        value->parent->is_default=FALSE;
        return TRUE;
    }
    return FALSE;
}

gboolean beryl_setting_value_set_keysym(BerylSettingValue * value, gint * data)
{
    if (!check_type(value,BERYL_SETTING_TYPE_BINDING))
        return FALSE;
    if (!get_info(value)->for_bind.key)
        return FALSE;
    value->value.as_binding.keysym=*data;
    value->parent->is_default=FALSE;
    return TRUE;
}

gboolean beryl_setting_value_set_keymods(BerylSettingValue * value, gint * data)
{
    if (!check_type(value,BERYL_SETTING_TYPE_BINDING))
        return FALSE;
    if (!get_info(value)->for_bind.key)
        return FALSE;
    value->value.as_binding.key_mod_mask=*data;
    value->parent->is_default=FALSE;
    return TRUE;
}

gboolean beryl_setting_value_set_button(BerylSettingValue * value, gint * data)
{
    if (!check_type(value,BERYL_SETTING_TYPE_BINDING))
        return FALSE;
    if (!get_info(value)->for_bind.button)
        return FALSE;
    value->value.as_binding.button=*data;
    value->parent->is_default=FALSE;
    return TRUE;
}

gboolean beryl_setting_value_set_buttonmods(BerylSettingValue * value, gint * data)
{
    if (!check_type(value,BERYL_SETTING_TYPE_BINDING))
        return FALSE;
    if (!get_info(value)->for_bind.button)
        return FALSE;
    value->value.as_binding.button_mod_mask=*data;
    value->parent->is_default=FALSE;
    return TRUE;
}

gboolean beryl_setting_value_set_edgemask(BerylSettingValue * value, gint * data)
{
    if (!check_type(value,BERYL_SETTING_TYPE_BINDING))
        return FALSE;
    if (!get_info(value)->for_bind.edge)
        return FALSE;
    value->value.as_binding.edge_mask=*data;
    value->parent->is_default=FALSE;
    return TRUE;
}

gboolean beryl_setting_value_set_bell(BerylSettingValue * value, gboolean * data)
{
    if (!check_type(value,BERYL_SETTING_TYPE_BINDING))
        return FALSE;
    if (!get_info(value)->for_bind.bell)
        return FALSE;
    value->value.as_binding.on_bell=*data;
    value->parent->is_default=FALSE;
    return TRUE;
}

gboolean beryl_setting_value_set_key_enabled(BerylSettingValue * value, gboolean * data)
{
    if (!check_type(value,BERYL_SETTING_TYPE_BINDING))
        return FALSE;
    if (!get_info(value)->for_bind.key)
        return FALSE;
    value->value.as_binding.enabled.value.key=*data;
    value->parent->is_default=FALSE;
    return TRUE;
}

gboolean beryl_setting_value_set_button_enabled(BerylSettingValue * value, gboolean * data)
{
    if (!check_type(value,BERYL_SETTING_TYPE_BINDING))
        return FALSE;
    if (!get_info(value)->for_bind.button)
        return FALSE;
    value->value.as_binding.enabled.value.button=*data;
    value->parent->is_default=FALSE;
    return TRUE;
}

gboolean beryl_setting_value_set_color(BerylSettingValue * value, BerylSettingColorValue * data)
{
    if (!check_type(value,BERYL_SETTING_TYPE_COLOR))
        return FALSE;
    memcpy(&value->value.as_color,data,sizeof(BerylSettingColorValue));
    value->parent->is_default=FALSE;
    return TRUE;
}

gboolean beryl_setting_get_can_set_key(BerylSetting * setting, gboolean * data)
{
    if (!check_s_type(setting,BERYL_SETTING_TYPE_BINDING))
        return FALSE;
    *data=get_s_info(setting)->for_bind.key;
    return TRUE;
}

gboolean beryl_setting_get_can_set_button(BerylSetting * setting, gboolean * data)
{
    if (!check_s_type(setting,BERYL_SETTING_TYPE_BINDING))
        return FALSE;
    *data=get_s_info(setting)->for_bind.button;
    return TRUE;
}

gboolean beryl_setting_get_can_set_edgemask(BerylSetting * setting, gboolean * data)
{
    if (!check_s_type(setting,BERYL_SETTING_TYPE_BINDING))
        return FALSE;
    *data=get_s_info(setting)->for_bind.edge;
    return TRUE;
}

gboolean beryl_setting_get_can_set_bell(BerylSetting * setting, gboolean * data)
{
    if (!check_s_type(setting,BERYL_SETTING_TYPE_BINDING))
        return FALSE;
    *data=get_s_info(setting)->for_bind.bell;
    return TRUE;
}

gboolean beryl_setting_get_allowed_strings(BerylSetting * setting, GSList ** data)
{
    if (!check_s_type(setting,BERYL_SETTING_TYPE_STRING))
        return FALSE;
    *data=get_s_info(setting)->for_string.allowed_values;
    return TRUE;
}

gboolean beryl_setting_get_raw_allowed_strings(BerylSetting * setting, GSList ** data)
{
    if (!check_s_type(setting,BERYL_SETTING_TYPE_STRING))
        return FALSE;
    *data=get_s_info(setting)->for_string.raw_values;
    return TRUE;
}

gboolean beryl_setting_get_int_max(BerylSetting * setting, gint * data)
{
    if (!check_s_type(setting,BERYL_SETTING_TYPE_INT))
        return FALSE;
    *data=get_s_info(setting)->for_int.max;
    return TRUE;
}

gboolean beryl_setting_get_int_min(BerylSetting * setting, gint * data)
{
    if (!check_s_type(setting,BERYL_SETTING_TYPE_INT))
        return FALSE;
    *data=get_s_info(setting)->for_int.min;
    return TRUE;
}

gboolean beryl_setting_get_float_min(BerylSetting * setting, gdouble * data)
{
    if (!check_s_type(setting,BERYL_SETTING_TYPE_FLOAT))
        return FALSE;
    *data=get_s_info(setting)->for_float.min;
    return TRUE;
}

gboolean beryl_setting_get_float_max(BerylSetting * setting, gdouble * data)
{
    if (!check_s_type(setting,BERYL_SETTING_TYPE_FLOAT))
        return FALSE;
    *data=get_s_info(setting)->for_float.max;
    return TRUE;
}

gboolean beryl_setting_get_float_precision(BerylSetting * setting, gdouble * data)
{
    if (!check_s_type(setting,BERYL_SETTING_TYPE_FLOAT))
        return FALSE;
    *data=get_s_info(setting)->for_float.precision;
    return TRUE;
}

gboolean beryl_setting_get_list_type(BerylSetting * setting, BerylSettingType * data)
{
    if (setting->type != BERYL_SETTING_TYPE_LIST)
        return FALSE;
    *data=setting->info.for_list.list_of_type;
    return TRUE;
}

BerylSettingType beryl_setting_get_type(BerylSetting * setting)
{
    return setting->type;
}

const gchar * beryl_setting_get_short_desc(BerylSetting * setting)
{
    return setting->short_desc;
}

const gchar * beryl_setting_get_long_desc(BerylSetting * setting)
{
    return setting->long_desc;
}

const gchar * beryl_setting_get_name(BerylSetting * setting)
{
    return setting->name;
}

const gchar * beryl_settings_plugin_get_short_desc(BerylSettingsPlugin * plugin)
{
    return plugin->short_desc;
}

const gchar * beryl_settings_plugin_get_long_desc(BerylSettingsPlugin * plugin)
{
    return plugin->long_desc;
}

const gchar * beryl_settings_plugin_get_name(BerylSettingsPlugin * plugin)
{
    return plugin->name;
}

GSList * beryl_settings_context_get_plugins(BerylSettingsContext * context)
{
    return context->plugins;
}

GSList * beryl_settings_plugin_get_settings(BerylSettingsPlugin * plugin)
{
    return plugin->settings;
}

gpointer beryl_settings_context_get_private(BerylSettingsContext * context)
{
    return context->private_ptr;
}

void beryl_settings_context_set_private(BerylSettingsContext * context, gpointer private_ptr)
{
    context->private_ptr = private_ptr;
}

gpointer beryl_settings_plugin_get_private(BerylSettingsPlugin * plugin)
{
    return plugin->private_ptr;
}

void beryl_settings_plugin_set_private(BerylSettingsPlugin * plugin, gpointer private_ptr)
{
    plugin->private_ptr = private_ptr;
}

gpointer beryl_setting_get_private(BerylSetting * setting)
{
    return setting->private_ptr;
}

void beryl_setting_set_private(BerylSetting * setting, gpointer private_ptr)
{
    setting->private_ptr = private_ptr;
}