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.

966 lines
19 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>
*/
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <ctype.h>
#include <math.h>
#include <compiz-core.h>
struct _Modifier {
char *name;
int modifier;
} modifiers[] = {
{ "<Shift>", ShiftMask },
{ "<Control>", ControlMask },
{ "<Mod1>", Mod1Mask },
{ "<Mod2>", Mod2Mask },
{ "<Mod3>", Mod3Mask },
{ "<Mod4>", Mod4Mask },
{ "<Mod5>", Mod5Mask },
{ "<Alt>", CompAltMask },
{ "<Meta>", CompMetaMask },
{ "<Super>", CompSuperMask },
{ "<Hyper>", CompHyperMask },
{ "<ModeSwitch>", CompModeSwitchMask }
};
#define N_MODIFIERS (sizeof (modifiers) / sizeof (struct _Modifier))
struct _Edge {
char *name;
char *modifierName;
} edges[] = {
{ "Left", "<LeftEdge>" },
{ "Right", "<RightEdge>" },
{ "Top", "<TopEdge>" },
{ "Bottom", "<BottomEdge>" },
{ "TopLeft", "<TopLeftEdge>" },
{ "TopRight", "<TopRightEdge>" },
{ "BottomLeft", "<BottomLeftEdge>" },
{ "BottomRight", "<BottomRightEdge>" }
};
void
compInitOptionValue (CompOptionValue *v)
{
memset (v, 0, sizeof (CompOptionValue));
}
void
compFiniOptionValue (CompOptionValue *v,
CompOptionType type)
{
int i;
switch (type) {
case CompOptionTypeString:
if (v->s)
free (v->s);
break;
case CompOptionTypeMatch:
matchFini (&v->match);
break;
case CompOptionTypeList:
for (i = 0; i < v->list.nValue; i++)
compFiniOptionValue (&v->list.value[i], v->list.type);
if (v->list.value)
free (v->list.value);
break;
default:
break;
}
}
void
compInitOption (CompOption *o)
{
memset (o, 0, sizeof (CompOption));
}
void
compFiniOption (CompOption *o)
{
compFiniOptionValue (&o->value, o->type);
}
CompOption *
compFindOption (CompOption *option,
int nOption,
const char *name,
int *index)
{
int i;
for (i = 0; i < nOption; i++)
{
if (strcmp (option[i].name, name) == 0)
{
if (index)
*index = i;
return &option[i];
}
}
return 0;
}
Bool
compSetBoolOption (CompOption *option,
CompOptionValue *value)
{
int b;
b = (value->b) ? TRUE : FALSE;
if (option->value.b == b)
return FALSE;
option->value.b = b;
return TRUE;
}
Bool
compSetIntOption (CompOption *option,
CompOptionValue *value)
{
if (value->i < option->rest.i.min ||
value->i > option->rest.i.max ||
value->i == option->value.i)
return FALSE;
option->value.i = value->i;
return TRUE;
}
Bool
compSetFloatOption (CompOption *option,
CompOptionValue *value)
{
float v, p;
/* Workaround for float rounding errors */
static float equalRange = 1e-5;
int sign = (value->f < 0 ? -1 : 1);
p = 1.0f / option->rest.f.precision;
v = ((int) (value->f * p + sign * 0.5f)) / p;
if (v < (option->rest.f.min - equalRange) ||
v > (option->rest.f.max + equalRange))
return FALSE;
if (v > (option->value.f - equalRange) &&
v < (option->value.f + equalRange))
return FALSE;
option->value.f = v;
return TRUE;
}
Bool
compSetStringOption (CompOption *option,
CompOptionValue *value)
{
char *s;
s = value->s;
if (!s)
s = "";
if (option->value.s == s)
return FALSE;
if (option->value.s && s)
{
if (strcmp (option->value.s, s) == 0)
return FALSE;
}
if (option->value.s)
free (option->value.s);
option->value.s = strdup (s);
return TRUE;
}
Bool
compSetColorOption (CompOption *option,
CompOptionValue *value)
{
if (memcmp (value->c, option->value.c, sizeof (value->c)) == 0)
return FALSE;
memcpy (option->value.c, value->c, sizeof (value->c));
return TRUE;
}
Bool
compSetActionOption (CompOption *option,
CompOptionValue *value)
{
CompAction *action = &option->value.action;
CompOptionValue v = *value;
/* initiate, terminate, priv and state should never be changed */
v.action.initiate = action->initiate;
v.action.terminate = action->terminate;
v.action.state = action->state;
v.action.priv = action->priv;
if (action->type == v.action.type)
{
switch (option->type) {
case CompOptionTypeKey:
if (!(action->type & CompBindingTypeKey))
return FALSE;
if (action->key.keycode == v.action.key.keycode &&
action->key.modifiers == v.action.key.modifiers)
return FALSE;
break;
case CompOptionTypeButton:
if (!(action->type & (CompBindingTypeButton |
CompBindingTypeEdgeButton)))
return FALSE;
if (action->type & CompBindingTypeEdgeButton)
{
if (action->button.button == v.action.button.button &&
action->button.modifiers == v.action.button.modifiers &&
action->edgeMask == v.action.edgeMask)
return FALSE;
}
else if (action->type & CompBindingTypeButton)
{
if (action->button.button == v.action.button.button &&
action->button.modifiers == v.action.button.modifiers)
return FALSE;
}
break;
case CompOptionTypeEdge:
if (v.action.edgeMask == action->edgeMask)
return FALSE;
break;
case CompOptionTypeBell:
if (v.action.bell == action->bell)
return FALSE;
break;
default:
return FALSE;
}
}
*action = v.action;
return TRUE;
}
Bool
compSetMatchOption (CompOption *option,
CompOptionValue *value)
{
CompDisplay *display = option->value.match.display;
CompMatch match;
if (matchEqual (&option->value.match, &value->match))
return FALSE;
if (!matchCopy (&match, &value->match))
return FALSE;
matchFini (&option->value.match);
option->value.match.op = match.op;
option->value.match.nOp = match.nOp;
if (display)
matchUpdate (display, &option->value.match);
return TRUE;
}
Bool
compSetOptionList (CompOption *option,
CompOptionValue *value)
{
CompOption o;
Bool status = FALSE;
int i, min;
if (value->list.nValue != option->value.list.nValue)
{
CompOptionValue *v;
v = malloc (sizeof (CompOptionValue) * value->list.nValue);
if (!v)
return FALSE;
min = MIN (value->list.nValue, option->value.list.nValue);
for (i = min; i < option->value.list.nValue; i++)
{
switch (option->value.list.type) {
case CompOptionTypeString:
if (option->value.list.value[i].s)
free (option->value.list.value[i].s);
break;
case CompOptionTypeMatch:
matchFini (&option->value.list.value[i].match);
default:
break;
}
}
memset (v, 0, sizeof (CompOptionValue) * value->list.nValue);
if (min)
memcpy (v, option->value.list.value,
sizeof (CompOptionValue) * min);
if (option->value.list.value)
free (option->value.list.value);
option->value.list.value = v;
option->value.list.nValue = value->list.nValue;
status = TRUE;
}
o = *option;
o.type = option->value.list.type;
for (i = 0; i < value->list.nValue; i++)
{
o.value = option->value.list.value[i];
switch (o.type) {
case CompOptionTypeBool:
status |= compSetBoolOption (&o, &value->list.value[i]);
break;
case CompOptionTypeInt:
status |= compSetIntOption (&o, &value->list.value[i]);
break;
case CompOptionTypeFloat:
status |= compSetFloatOption (&o, &value->list.value[i]);
break;
case CompOptionTypeString:
status |= compSetStringOption (&o, &value->list.value[i]);
break;
case CompOptionTypeColor:
status |= compSetColorOption (&o, &value->list.value[i]);
break;
case CompOptionTypeMatch:
status |= compSetMatchOption (&o, &value->list.value[i]);
default:
break;
}
option->value.list.value[i] = o.value;
}
return status;
}
Bool
compSetOption (CompOption *option,
CompOptionValue *value)
{
switch (option->type) {
case CompOptionTypeBool:
return compSetBoolOption (option, value);
case CompOptionTypeInt:
return compSetIntOption (option, value);
case CompOptionTypeFloat:
return compSetFloatOption (option, value);
case CompOptionTypeString:
return compSetStringOption (option, value);
case CompOptionTypeColor:
return compSetColorOption (option, value);
case CompOptionTypeMatch:
return compSetMatchOption (option, value);
case CompOptionTypeAction:
case CompOptionTypeKey:
case CompOptionTypeButton:
case CompOptionTypeEdge:
case CompOptionTypeBell:
return compSetActionOption (option, value);
case CompOptionTypeList:
return compSetOptionList (option, value);
}
return FALSE;
}
Bool
getBoolOptionNamed (CompOption *option,
int nOption,
const char *name,
Bool defaultValue)
{
while (nOption--)
{
if (option->type == CompOptionTypeBool)
if (strcmp (option->name, name) == 0)
return option->value.b;
option++;
}
return defaultValue;
}
int
getIntOptionNamed (CompOption *option,
int nOption,
const char *name,
int defaultValue)
{
while (nOption--)
{
if (option->type == CompOptionTypeInt)
if (strcmp (option->name, name) == 0)
return option->value.i;
option++;
}
return defaultValue;
}
float
getFloatOptionNamed (CompOption *option,
int nOption,
const char *name,
float defaultValue)
{
while (nOption--)
{
if (option->type == CompOptionTypeFloat)
if (strcmp (option->name, name) == 0)
return option->value.f;
option++;
}
return defaultValue;
}
char *
getStringOptionNamed (CompOption *option,
int nOption,
const char *name,
char *defaultValue)
{
while (nOption--)
{
if (option->type == CompOptionTypeString)
if (strcmp (option->name, name) == 0)
return option->value.s;
option++;
}
return defaultValue;
}
unsigned short *
getColorOptionNamed (CompOption *option,
int nOption,
const char *name,
unsigned short *defaultValue)
{
while (nOption--)
{
if (option->type == CompOptionTypeColor)
if (strcmp (option->name, name) == 0)
return option->value.c;
option++;
}
return defaultValue;
}
CompMatch *
getMatchOptionNamed (CompOption *option,
int nOption,
const char *name,
CompMatch *defaultValue)
{
while (nOption--)
{
if (option->type == CompOptionTypeMatch)
if (strcmp (option->name, name) == 0)
return &option->value.match;
option++;
}
return defaultValue;
}
static char *
stringAppend (char *s,
const char *a)
{
char *r;
int len;
len = strlen (a);
if (s)
len += strlen (s);
r = malloc (len + 1);
if (r)
{
if (s)
{
sprintf (r, "%s%s", s, a);
free (s);
}
else
{
sprintf (r, "%s", a);
}
s = r;
}
return s;
}
static char *
modifiersToString (CompDisplay *d,
unsigned int modMask)
{
char *binding = NULL;
int i;
for (i = 0; i < N_MODIFIERS; i++)
{
if (modMask & modifiers[i].modifier)
binding = stringAppend (binding, modifiers[i].name);
}
return binding;
}
static char *
edgeMaskToBindingString (CompDisplay *d,
unsigned int edgeMask)
{
char *binding = NULL;
int i;
for (i = 0; i < SCREEN_EDGE_NUM; i++)
if (edgeMask & (1 << i))
binding = stringAppend (binding, edges[i].modifierName);
return binding;
}
char *
keyBindingToString (CompDisplay *d,
CompKeyBinding *key)
{
char *binding;
binding = modifiersToString (d, key->modifiers);
if (key->keycode != 0)
{
KeySym keysym;
char *keyname;
keysym = XKeycodeToKeysym (d->display, key->keycode, 0);
keyname = XKeysymToString (keysym);
if (keyname)
{
binding = stringAppend (binding, keyname);
}
else
{
char keyCodeStr[256];
snprintf (keyCodeStr, 256, "0x%x", key->keycode);
binding = stringAppend (binding, keyCodeStr);
}
}
return binding;
}
char *
buttonBindingToString (CompDisplay *d,
CompButtonBinding *button)
{
char *binding;
char buttonStr[256];
binding = modifiersToString (d, button->modifiers);
snprintf (buttonStr, 256, "Button%d", button->button);
binding = stringAppend (binding, buttonStr);
return binding;
}
char *
keyActionToString (CompDisplay *d,
CompAction *action)
{
char *binding;
binding = keyBindingToString (d, &action->key);
if (!binding)
return strdup ("Disabled");
return binding;
}
char *
buttonActionToString (CompDisplay *d,
CompAction *action)
{
char *binding, *edge;
char buttonStr[256];
binding = modifiersToString (d, action->button.modifiers);
edge = edgeMaskToBindingString (d, action->edgeMask);
if (edge)
{
binding = stringAppend (binding, edge);
free (edge);
}
snprintf (buttonStr, 256, "Button%d", action->button.button);
binding = stringAppend (binding, buttonStr);
if (!binding)
return strdup ("Disabled");
return binding;
}
static unsigned int
stringToModifiers (CompDisplay *d,
const char *binding)
{
unsigned int mods = 0;
int i;
for (i = 0; i < N_MODIFIERS; i++)
{
if (strstr (binding, modifiers[i].name))
mods |= modifiers[i].modifier;
}
return mods;
}
static unsigned int
bindingStringToEdgeMask (CompDisplay *d,
const char *binding)
{
unsigned int edgeMask = 0;
int i;
for (i = 0; i < SCREEN_EDGE_NUM; i++)
if (strstr (binding, edges[i].modifierName))
edgeMask |= 1 << i;
return edgeMask;
}
Bool
stringToKeyBinding (CompDisplay *d,
const char *binding,
CompKeyBinding *key)
{
char *ptr;
unsigned int mods;
KeySym keysym;
mods = stringToModifiers (d, binding);
ptr = strrchr (binding, '>');
if (ptr)
binding = ptr + 1;
while (*binding && !isalnum (*binding))
binding++;
if (!*binding)
{
if (mods)
{
key->keycode = 0;
key->modifiers = mods;
return TRUE;
}
return FALSE;
}
keysym = XStringToKeysym (binding);
if (keysym != NoSymbol)
{
KeyCode keycode;
keycode = XKeysymToKeycode (d->display, keysym);
if (keycode)
{
key->keycode = keycode;
key->modifiers = mods;
return TRUE;
}
}
if (strncmp (binding, "0x", 2) == 0)
{
key->keycode = strtol (binding, NULL, 0);
key->modifiers = mods;
return TRUE;
}
return FALSE;
}
Bool
stringToButtonBinding (CompDisplay *d,
const char *binding,
CompButtonBinding *button)
{
char *ptr;
unsigned int mods;
mods = stringToModifiers (d, binding);
ptr = strrchr (binding, '>');
if (ptr)
binding = ptr + 1;
while (*binding && !isalnum (*binding))
binding++;
if (strncmp (binding, "Button", strlen ("Button")) == 0)
{
int buttonNum;
if (sscanf (binding + strlen ("Button"), "%d", &buttonNum) == 1)
{
button->button = buttonNum;
button->modifiers = mods;
return TRUE;
}
}
return FALSE;
}
void
stringToKeyAction (CompDisplay *d,
const char *binding,
CompAction *action)
{
if (stringToKeyBinding (d, binding, &action->key))
action->type = CompBindingTypeKey;
else
action->type = CompBindingTypeNone;
}
void
stringToButtonAction (CompDisplay *d,
const char *binding,
CompAction *action)
{
if (stringToButtonBinding (d, binding, &action->button))
{
action->edgeMask = bindingStringToEdgeMask (d, binding);
if (action->edgeMask)
action->type = CompBindingTypeEdgeButton;
else
action->type = CompBindingTypeButton;
}
else
{
action->type = CompBindingTypeNone;
}
}
const char *
edgeToString (unsigned int edge)
{
return edges[edge].name;
}
unsigned int
stringToEdgeMask (const char *edge)
{
unsigned int edgeMask = 0;
char *needle;
int i;
for (i = 0; i < SCREEN_EDGE_NUM; i++)
{
needle = strstr (edge, edgeToString (i));
if (needle)
{
if (needle != edge && isalnum (*(needle - 1)))
continue;
needle += strlen (edgeToString (i));
if (*needle && isalnum (*needle))
continue;
edgeMask |= 1 << i;
}
}
return edgeMask;
}
char *
edgeMaskToString (unsigned int edgeMask)
{
char *edge = NULL;
int i;
for (i = 0; i < SCREEN_EDGE_NUM; i++)
{
if (edgeMask & (1 << i))
{
if (edge)
edge = stringAppend (edge, " | ");
edge = stringAppend (edge, edgeToString (i));
}
}
if (!edge)
return strdup ("");
return edge;
}
Bool
stringToColor (const char *color,
unsigned short *rgba)
{
int c[4];
if (sscanf (color, "#%2x%2x%2x%2x", &c[0], &c[1], &c[2], &c[3]) == 4)
{
rgba[0] = c[0] << 8 | c[0];
rgba[1] = c[1] << 8 | c[1];
rgba[2] = c[2] << 8 | c[2];
rgba[3] = c[3] << 8 | c[3];
return TRUE;
}
return FALSE;
}
char *
colorToString (unsigned short *rgba)
{
char tmp[256];
snprintf (tmp, 256, "#%.2x%.2x%.2x%.2x",
rgba[0] / 256, rgba[1] / 256, rgba[2] / 256, rgba[3] / 256);
return strdup (tmp);
}
const char *
optionTypeToString (CompOptionType type)
{
switch (type) {
case CompOptionTypeBool:
return "bool";
case CompOptionTypeInt:
return "int";
case CompOptionTypeFloat:
return "float";
case CompOptionTypeString:
return "string";
case CompOptionTypeColor:
return "color";
case CompOptionTypeAction:
return "action";
case CompOptionTypeKey:
return "key";
case CompOptionTypeButton:
return "button";
case CompOptionTypeEdge:
return "edge";
case CompOptionTypeBell:
return "bell";
case CompOptionTypeMatch:
return "match";
case CompOptionTypeList:
return "list";
}
return "unknown";
}
Bool
isActionOption (CompOption *option)
{
switch (option->type) {
case CompOptionTypeAction:
case CompOptionTypeKey:
case CompOptionTypeButton:
case CompOptionTypeEdge:
case CompOptionTypeBell:
return TRUE;
default:
break;
}
return FALSE;
}