You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

850 lines
18 KiB

/*
* Copyright © 2005 Novell, Inc.
*
* Permission to use, copy, modify, distribute, and sell this software
* and its documentation for any purpose is hereby granted without
* fee, provided that the above copyright notice appear in all copies
* and that both that copyright notice and this permission notice
* appear in supporting documentation, and that the name of
* Novell, Inc. not be used in advertising or publicity pertaining to
* distribution of the software without specific, written prior permission.
* Novell, Inc. makes no representations about the suitability of this
* software for any purpose. It is provided "as is" without express or
* implied warranty.
*
* NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
* NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
* WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Author: David Reveman <davidr@novell.com>
*/
#define _GNU_SOURCE
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <glib.h>
#include <glib/gprintf.h>
#include <gconf/gconf-client.h>
#include <compiz-core.h>
static CompMetadata gconfMetadata;
#define APP_NAME "compiz"
/* From gconf-internal.h. Bleah. */
int gconf_value_compare (const GConfValue *value_a,
const GConfValue *value_b);
static int corePrivateIndex;
typedef struct _GConfCore {
GConfClient *client;
guint cnxn;
CompTimeoutHandle reloadHandle;
InitPluginForObjectProc initPluginForObject;
SetOptionForPluginProc setOptionForPlugin;
} GConfCore;
#define GET_GCONF_CORE(c) \
((GConfCore *) (c)->base.privates[corePrivateIndex].ptr)
#define GCONF_CORE(c) \
GConfCore *gc = GET_GCONF_CORE (c)
static gchar *
gconfGetKey (CompObject *object,
const gchar *plugin,
const gchar *option)
{
const gchar *type;
gchar *key, *name, *objectName;
type = compObjectTypeName (object->type);
if (strcmp (type, "display") == 0)
type = "allscreens";
name = compObjectName (object);
if (name)
{
objectName = g_strdup_printf ("%s%s", type, name);
free (name);
}
else
objectName = g_strdup (type);
if (strcmp (plugin, "core") == 0)
key = g_strjoin ("/", "/apps", APP_NAME, "general", objectName,
"options", option, NULL);
else
key = g_strjoin ("/", "/apps", APP_NAME, "plugins", plugin, objectName,
"options", option, NULL);
g_free (objectName);
return key;
}
static GConfValueType
gconfTypeFromCompType (CompOptionType type)
{
switch (type) {
case CompOptionTypeBool:
case CompOptionTypeBell:
return GCONF_VALUE_BOOL;
case CompOptionTypeInt:
return GCONF_VALUE_INT;
case CompOptionTypeFloat:
return GCONF_VALUE_FLOAT;
case CompOptionTypeString:
case CompOptionTypeColor:
case CompOptionTypeKey:
case CompOptionTypeButton:
case CompOptionTypeEdge:
case CompOptionTypeMatch:
return GCONF_VALUE_STRING;
case CompOptionTypeList:
return GCONF_VALUE_LIST;
default:
break;
}
return GCONF_VALUE_INVALID;
}
static void
gconfSetValue (CompObject *object,
CompOptionValue *value,
CompOptionType type,
GConfValue *gvalue)
{
switch (type) {
case CompOptionTypeBool:
gconf_value_set_bool (gvalue, value->b);
break;
case CompOptionTypeInt:
gconf_value_set_int (gvalue, value->i);
break;
case CompOptionTypeFloat:
gconf_value_set_float (gvalue, value->f);
break;
case CompOptionTypeString:
gconf_value_set_string (gvalue, value->s);
break;
case CompOptionTypeColor: {
gchar *color;
color = colorToString (value->c);
gconf_value_set_string (gvalue, color);
free (color);
} break;
case CompOptionTypeKey: {
gchar *action;
while (object && object->type != COMP_OBJECT_TYPE_DISPLAY)
object = object->parent;
if (!object)
return;
action = keyActionToString (GET_CORE_DISPLAY (object), &value->action);
gconf_value_set_string (gvalue, action);
free (action);
} break;
case CompOptionTypeButton: {
gchar *action;
while (object && object->type != COMP_OBJECT_TYPE_DISPLAY)
object = object->parent;
if (!object)
return;
action = buttonActionToString (GET_CORE_DISPLAY (object),
&value->action);
gconf_value_set_string (gvalue, action);
free (action);
} break;
case CompOptionTypeEdge: {
gchar *edge;
edge = edgeMaskToString (value->action.edgeMask);
gconf_value_set_string (gvalue, edge);
free (edge);
} break;
case CompOptionTypeBell:
gconf_value_set_bool (gvalue, value->action.bell);
break;
case CompOptionTypeMatch: {
gchar *match;
match = matchToString (&value->match);
gconf_value_set_string (gvalue, match);
free (match);
} break;
default:
break;
}
}
static void
gconfSetOption (CompObject *object,
CompOption *o,
const gchar *plugin)
{
GConfValueType type = gconfTypeFromCompType (o->type);
GConfValue *gvalue, *existingValue = NULL;
gchar *key;
GCONF_CORE (&core);
if (type == GCONF_VALUE_INVALID)
return;
key = gconfGetKey (object, plugin, o->name);
existingValue = gconf_client_get (gc->client, key, NULL);
gvalue = gconf_value_new (type);
if (o->type == CompOptionTypeList)
{
GSList *node, *list = NULL;
GConfValue *gv;
int i;
type = gconfTypeFromCompType (o->value.list.type);
for (i = 0; i < o->value.list.nValue; i++)
{
gv = gconf_value_new (type);
gconfSetValue (object, &o->value.list.value[i],
o->value.list.type, gv);
list = g_slist_append (list, gv);
}
gconf_value_set_list_type (gvalue, type);
gconf_value_set_list (gvalue, list);
if (!existingValue || gconf_value_compare (existingValue, gvalue))
gconf_client_set (gc->client, key, gvalue, NULL);
for (node = list; node; node = node->next)
gconf_value_free ((GConfValue *) node->data);
g_slist_free (list);
}
else
{
gconfSetValue (object, &o->value, o->type, gvalue);
if (!existingValue || gconf_value_compare (existingValue, gvalue))
gconf_client_set (gc->client, key, gvalue, NULL);
}
gconf_value_free (gvalue);
if (existingValue)
gconf_value_free (existingValue);
g_free (key);
}
static Bool
gconfGetValue (CompObject *object,
CompOptionValue *value,
CompOptionType type,
GConfValue *gvalue)
{
if (type == CompOptionTypeBool &&
gvalue->type == GCONF_VALUE_BOOL)
{
value->b = gconf_value_get_bool (gvalue);
return TRUE;
}
else if (type == CompOptionTypeInt &&
gvalue->type == GCONF_VALUE_INT)
{
value->i = gconf_value_get_int (gvalue);
return TRUE;
}
else if (type == CompOptionTypeFloat &&
gvalue->type == GCONF_VALUE_FLOAT)
{
value->f = gconf_value_get_float (gvalue);
return TRUE;
}
else if (type == CompOptionTypeString &&
gvalue->type == GCONF_VALUE_STRING)
{
const char *str;
str = gconf_value_get_string (gvalue);
if (str)
{
value->s = strdup (str);
if (value->s)
return TRUE;
}
}
else if (type == CompOptionTypeColor &&
gvalue->type == GCONF_VALUE_STRING)
{
const gchar *color;
color = gconf_value_get_string (gvalue);
if (stringToColor (color, value->c))
return TRUE;
}
else if (type == CompOptionTypeKey &&
gvalue->type == GCONF_VALUE_STRING)
{
const gchar *action;
action = gconf_value_get_string (gvalue);
while (object && object->type != COMP_OBJECT_TYPE_DISPLAY)
object = object->parent;
if (!object)
return FALSE;
stringToKeyAction (GET_CORE_DISPLAY (object), action, &value->action);
return TRUE;
}
else if (type == CompOptionTypeButton &&
gvalue->type == GCONF_VALUE_STRING)
{
const gchar *action;
action = gconf_value_get_string (gvalue);
while (object && object->type != COMP_OBJECT_TYPE_DISPLAY)
object = object->parent;
if (!object)
return FALSE;
stringToButtonAction (GET_CORE_DISPLAY (object), action,
&value->action);
return TRUE;
}
else if (type == CompOptionTypeEdge &&
gvalue->type == GCONF_VALUE_STRING)
{
const gchar *edge;
edge = gconf_value_get_string (gvalue);
value->action.edgeMask = stringToEdgeMask (edge);
return TRUE;
}
else if (type == CompOptionTypeBell &&
gvalue->type == GCONF_VALUE_BOOL)
{
value->action.bell = gconf_value_get_bool (gvalue);
return TRUE;
}
else if (type == CompOptionTypeMatch &&
gvalue->type == GCONF_VALUE_STRING)
{
const gchar *match;
match = gconf_value_get_string (gvalue);
matchInit (&value->match);
matchAddFromString (&value->match, match);
return TRUE;
}
return FALSE;
}
static Bool
gconfReadOptionValue (CompObject *object,
GConfEntry *entry,
CompOption *o,
CompOptionValue *value)
{
GConfValue *gvalue;
gvalue = gconf_entry_get_value (entry);
if (!gvalue)
return FALSE;
compInitOptionValue (value);
if (o->type == CompOptionTypeList &&
gvalue->type == GCONF_VALUE_LIST)
{
GConfValueType type;
GSList *list;
int i, n;
type = gconf_value_get_list_type (gvalue);
if (gconfTypeFromCompType (o->value.list.type) != type)
return FALSE;
list = gconf_value_get_list (gvalue);
n = g_slist_length (list);
value->list.value = NULL;
value->list.nValue = 0;
value->list.type = o->value.list.type;
if (n)
{
value->list.value = malloc (sizeof (CompOptionValue) * n);
if (value->list.value)
{
for (i = 0; i < n; i++)
{
if (!gconfGetValue (object,
&value->list.value[i],
o->value.list.type,
(GConfValue *) list->data))
break;
value->list.nValue++;
list = g_slist_next (list);
}
if (value->list.nValue != n)
{
compFiniOptionValue (value, o->type);
return FALSE;
}
}
}
}
else
{
if (!gconfGetValue (object, value, o->type, gvalue))
return FALSE;
}
return TRUE;
}
static void
gconfGetOption (CompObject *object,
CompOption *o,
const char *plugin)
{
GConfEntry *entry;
gchar *key;
GCONF_CORE (&core);
key = gconfGetKey (object, plugin, o->name);
entry = gconf_client_get_entry (gc->client, key, NULL, TRUE, NULL);
if (entry)
{
CompOptionValue value;
if (gconfReadOptionValue (object, entry, o, &value))
{
(*core.setOptionForPlugin) (object, plugin, o->name, &value);
compFiniOptionValue (&value, o->type);
}
else
{
gconfSetOption (object, o, plugin);
}
gconf_entry_free (entry);
}
g_free (key);
}
static CompBool
gconfReloadObjectTree (CompObject *object,
void *closure);
static CompBool
gconfReloadObjectsWithType (CompObjectType type,
CompObject *parent,
void *closure)
{
compObjectForEach (parent, type, gconfReloadObjectTree, closure);
return TRUE;
}
static CompBool
gconfReloadObjectTree (CompObject *object,
void *closure)
{
CompPlugin *p = (CompPlugin *) closure;
CompOption *option;
int nOption;
option = (*p->vTable->getObjectOptions) (p, object, &nOption);
while (nOption--)
gconfGetOption (object, option++, p->vTable->name);
compObjectForEachType (object, gconfReloadObjectsWithType, closure);
return TRUE;
}
static Bool
gconfReload (void *closure)
{
CompPlugin *p;
GCONF_CORE (&core);
for (p = getPlugins (); p; p = p->next)
{
if (!p->vTable->getObjectOptions)
continue;
gconfReloadObjectTree (&core.base, (void *) p);
}
gc->reloadHandle = 0;
return FALSE;
}
static Bool
gconfSetOptionForPlugin (CompObject *object,
const char *plugin,
const char *name,
CompOptionValue *value)
{
CompBool status;
GCONF_CORE (&core);
UNWRAP (gc, &core, setOptionForPlugin);
status = (*core.setOptionForPlugin) (object, plugin, name, value);
WRAP (gc, &core, setOptionForPlugin, gconfSetOptionForPlugin);
if (status && !gc->reloadHandle)
{
CompPlugin *p;
p = findActivePlugin (plugin);
if (p && p->vTable->getObjectOptions)
{
CompOption *option;
int nOption;
option = (*p->vTable->getObjectOptions) (p, object, &nOption);
option = compFindOption (option, nOption, name, 0);
if (option)
gconfSetOption (object, option, p->vTable->name);
}
}
return status;
}
static CompBool
gconfInitPluginForObject (CompPlugin *p,
CompObject *o)
{
CompBool status;
GCONF_CORE (&core);
UNWRAP (gc, &core, initPluginForObject);
status = (*core.initPluginForObject) (p, o);
WRAP (gc, &core, initPluginForObject, gconfInitPluginForObject);
if (status && p->vTable->getObjectOptions)
{
CompOption *option;
int nOption;
option = (*p->vTable->getObjectOptions) (p, o, &nOption);
while (nOption--)
gconfGetOption (o, option++, p->vTable->name);
}
return status;
}
/* MULTIDPYERROR: only works with one or less displays present */
static void
gconfKeyChanged (GConfClient *client,
guint cnxn_id,
GConfEntry *entry,
gpointer user_data)
{
CompPlugin *plugin;
CompObject *object;
CompOption *option = NULL;
int nOption = 0;
gchar **token;
int objectIndex = 4;
token = g_strsplit (entry->key, "/", 8);
if (g_strv_length (token) < 7)
{
g_strfreev (token);
return;
}
if (strcmp (token[0], "") != 0 ||
strcmp (token[1], "apps") != 0 ||
strcmp (token[2], APP_NAME) != 0)
{
g_strfreev (token);
return;
}
if (strcmp (token[3], "general") == 0)
{
plugin = findActivePlugin ("core");
}
else
{
if (strcmp (token[3], "plugins") != 0 || g_strv_length (token) < 8)
{
g_strfreev (token);
return;
}
objectIndex = 5;
plugin = findActivePlugin (token[4]);
}
if (!plugin)
{
g_strfreev (token);
return;
}
object = compObjectFind (&core.base, COMP_OBJECT_TYPE_DISPLAY, NULL);
if (!object)
{
g_strfreev (token);
return;
}
if (strncmp (token[objectIndex], "screen", 6) == 0)
{
object = compObjectFind (object, COMP_OBJECT_TYPE_SCREEN,
token[objectIndex] + 6);
if (!object)
{
g_strfreev (token);
return;
}
}
else if (strcmp (token[objectIndex], "allscreens") != 0)
{
g_strfreev (token);
return;
}
if (strcmp (token[objectIndex + 1], "options") != 0)
{
g_strfreev (token);
return;
}
if (plugin->vTable->getObjectOptions)
option = (*plugin->vTable->getObjectOptions) (plugin, object,
&nOption);
option = compFindOption (option, nOption, token[objectIndex + 2], 0);
if (option)
{
CompOptionValue value;
if (gconfReadOptionValue (object, entry, option, &value))
{
(*core.setOptionForPlugin) (object,
plugin->vTable->name,
option->name,
&value);
compFiniOptionValue (&value, option->type);
}
}
g_strfreev (token);
}
static void
gconfSendGLibNotify (CompScreen *s)
{
Display *dpy = s->display->display;
XEvent xev;
xev.xclient.type = ClientMessage;
xev.xclient.display = dpy;
xev.xclient.format = 32;
xev.xclient.message_type = XInternAtom (dpy, "_COMPIZ_GLIB_NOTIFY", 0);
xev.xclient.window = s->root;
memset (xev.xclient.data.l, 0, sizeof (xev.xclient.data.l));
XSendEvent (dpy,
s->root,
FALSE,
SubstructureRedirectMask | SubstructureNotifyMask,
&xev);
}
static Bool
gconfInitCore (CompPlugin *p,
CompCore *c)
{
GConfCore *gc;
if (!checkPluginABI ("core", CORE_ABIVERSION))
return FALSE;
gc = malloc (sizeof (GConfCore));
if (!gc)
return FALSE;
g_type_init ();
gc->client = gconf_client_get_default ();
gconf_client_add_dir (gc->client, "/apps/" APP_NAME,
GCONF_CLIENT_PRELOAD_NONE, NULL);
gc->reloadHandle = compAddTimeout (0, 0, gconfReload, 0);
gc->cnxn = gconf_client_notify_add (gc->client, "/apps/" APP_NAME,
gconfKeyChanged, c, NULL, NULL);
WRAP (gc, c, initPluginForObject, gconfInitPluginForObject);
WRAP (gc, c, setOptionForPlugin, gconfSetOptionForPlugin);
c->base.privates[corePrivateIndex].ptr = gc;
return TRUE;
}
static void
gconfFiniCore (CompPlugin *p,
CompCore *c)
{
GCONF_CORE (c);
UNWRAP (gc, c, initPluginForObject);
UNWRAP (gc, c, setOptionForPlugin);
if (gc->reloadHandle)
compRemoveTimeout (gc->reloadHandle);
if (gc->cnxn)
gconf_client_notify_remove (gc->client, gc->cnxn);
gconf_client_remove_dir (gc->client, "/apps/" APP_NAME, NULL);
gconf_client_clear_cache (gc->client);
free (gc);
}
static Bool
gconfInitScreen (CompPlugin *p,
CompScreen *s)
{
gconfSendGLibNotify (s);
return TRUE;
}
static CompBool
gconfInitObject (CompPlugin *p,
CompObject *o)
{
static InitPluginObjectProc dispTab[] = {
(InitPluginObjectProc) gconfInitCore,
(InitPluginObjectProc) 0, /* InitDisplay */
(InitPluginObjectProc) gconfInitScreen
};
RETURN_DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), TRUE, (p, o));
}
static void
gconfFiniObject (CompPlugin *p,
CompObject *o)
{
static FiniPluginObjectProc dispTab[] = {
(FiniPluginObjectProc) gconfFiniCore
};
DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), (p, o));
}
static Bool
gconfInit (CompPlugin *p)
{
if (!compInitPluginMetadataFromInfo (&gconfMetadata, p->vTable->name,
0, 0, 0, 0))
return FALSE;
corePrivateIndex = allocateCorePrivateIndex ();
if (corePrivateIndex < 0)
{
compFiniMetadata (&gconfMetadata);
return FALSE;
}
compAddMetadataFromFile (&gconfMetadata, p->vTable->name);
return TRUE;
}
static void
gconfFini (CompPlugin *p)
{
freeCorePrivateIndex (corePrivateIndex);
compFiniMetadata (&gconfMetadata);
}
static CompMetadata *
gconfGetMetadata (CompPlugin *plugin)
{
return &gconfMetadata;
}
CompPluginVTable gconfVTable = {
"gconf",
gconfGetMetadata,
gconfInit,
gconfFini,
gconfInitObject,
gconfFiniObject,
0, /* GetObjectOptions */
0 /* SetObjectOption */
};
CompPluginVTable *
getCompPluginInfo20070830 (void)
{
return &gconfVTable;
}