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.
3143 lines
70 KiB
3143 lines
70 KiB
/*
|
|
* Compiz configuration system library
|
|
*
|
|
* Copyright (C) 2007 Dennis Kasprzyk <onestone@opencompositing.org>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#ifdef USE_PROTOBUF
|
|
#include "compizconfig.pb.h"
|
|
#include <google/protobuf/io/zero_copy_stream_impl.h>
|
|
#endif
|
|
|
|
extern "C"
|
|
{
|
|
#ifdef HAVE_CONFIG_H
|
|
# include "../config.h"
|
|
#endif
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <dirent.h>
|
|
#include <sys/stat.h>
|
|
#include <errno.h>
|
|
|
|
#include <libxslt/transform.h>
|
|
#include <libxslt/xsltutils.h>
|
|
|
|
#include <locale.h>
|
|
|
|
#include <compiz-core.h>
|
|
#include <ccs.h>
|
|
#include "ccs-private.h"
|
|
}
|
|
|
|
extern int xmlLoadExtDtdDefaultValue;
|
|
|
|
|
|
#ifdef USE_PROTOBUF
|
|
|
|
Bool usingProtobuf = TRUE;
|
|
|
|
#define PB_ABI_VERSION 20090314
|
|
|
|
typedef metadata::PluginInfo PluginInfoMetadata;
|
|
typedef metadata::PluginBrief PluginBriefMetadata;
|
|
typedef metadata::Plugin PluginMetadata;
|
|
|
|
typedef PluginInfoMetadata::Dependencies DependenciesMetadata;
|
|
typedef PluginMetadata::Screen ScreenMetadata;
|
|
typedef PluginMetadata::Option OptionMetadata;
|
|
typedef PluginMetadata::Extension ExtensionMetadata;
|
|
typedef OptionMetadata::GenericValue GenericValueMetadata;
|
|
|
|
typedef google::protobuf::RepeatedPtrField< std::string > StringList;
|
|
|
|
PluginBriefMetadata persistentPluginBriefPB;
|
|
PluginMetadata persistentPluginPB; // Made global so that it gets reused,
|
|
// for better performance (to avoid
|
|
// mem alloc/free for each plugin)
|
|
|
|
std::string metadataCacheDir = "";
|
|
|
|
static char *
|
|
getLocale ()
|
|
{
|
|
char *lang = getenv ("LANG");
|
|
|
|
if (!lang || !strlen (lang))
|
|
lang = getenv ("LC_ALL");
|
|
|
|
if (!lang || !strlen (lang))
|
|
lang = getenv ("LC_MESSAGES");
|
|
|
|
return lang ? lang : (char *)"";
|
|
}
|
|
|
|
std::string curLocale = std::string (getLocale ());
|
|
std::string shortLocale = curLocale.find ('.') == std::string::npos ?
|
|
curLocale : curLocale.substr (0, curLocale.find ('.'));
|
|
|
|
#endif
|
|
|
|
static void
|
|
ccsAddRestrictionToStringInfo (CCSSettingStringInfo *forString,
|
|
const char *name,
|
|
const char *value)
|
|
{
|
|
CCSStrRestriction *restriction;
|
|
|
|
restriction = (CCSStrRestriction *) calloc (1, sizeof (CCSStrRestriction));
|
|
if (restriction)
|
|
{
|
|
restriction->name = strdup (name);
|
|
restriction->value = strdup (value);
|
|
forString->restriction =
|
|
ccsStrRestrictionListAppend (forString->restriction,
|
|
restriction);
|
|
}
|
|
}
|
|
|
|
static void
|
|
ccsAddRestrictionToStringExtension (CCSStrExtension *extension,
|
|
const char *name,
|
|
const char *value)
|
|
{
|
|
CCSStrRestriction *restriction;
|
|
|
|
restriction = (CCSStrRestriction *) calloc (1, sizeof (CCSStrRestriction));
|
|
if (restriction)
|
|
{
|
|
restriction->name = strdup (name);
|
|
restriction->value = strdup (value);
|
|
extension->restriction =
|
|
ccsStrRestrictionListAppend (extension->restriction, restriction);
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef USE_PROTOBUF
|
|
|
|
static void
|
|
initBoolValuePB (CCSSettingValue * v,
|
|
const GenericValueMetadata & value)
|
|
{
|
|
v->value.asBool = FALSE;
|
|
|
|
if (value.has_bool_value ())
|
|
{
|
|
v->value.asBool = value.bool_value ();
|
|
}
|
|
}
|
|
|
|
static void
|
|
initIntValuePB (CCSSettingValue * v,
|
|
CCSSettingInfo * i,
|
|
const GenericValueMetadata & value)
|
|
{
|
|
v->value.asInt = (i->forInt.min + i->forInt.max) / 2;
|
|
|
|
if (value.has_int_value ())
|
|
{
|
|
int val = value.int_value ();
|
|
if (val >= i->forInt.min && val <= i->forInt.max)
|
|
v->value.asInt = val;
|
|
}
|
|
}
|
|
|
|
static void
|
|
initFloatValuePB (CCSSettingValue * v,
|
|
CCSSettingInfo * i,
|
|
const GenericValueMetadata & value)
|
|
{
|
|
v->value.asFloat = (i->forFloat.min + i->forFloat.max) / 2;
|
|
|
|
if (value.has_float_value ())
|
|
{
|
|
float val = value.float_value ();
|
|
if (val >= i->forFloat.min && val <= i->forFloat.max)
|
|
v->value.asFloat = val;
|
|
}
|
|
}
|
|
|
|
static void
|
|
initStringValuePB (CCSSettingValue * v,
|
|
CCSSettingInfo * i,
|
|
const GenericValueMetadata & value)
|
|
{
|
|
free (v->value.asString);
|
|
|
|
if (value.has_string_value ())
|
|
v->value.asString = strdup (value.string_value ().c_str ());
|
|
else
|
|
v->value.asString = strdup ("");
|
|
}
|
|
|
|
static void
|
|
initColorValuePB (CCSSettingValue * v,
|
|
const GenericValueMetadata & value)
|
|
{
|
|
memset (&v->value.asColor, 0, sizeof (v->value.asColor));
|
|
v->value.asColor.color.alpha = 0xffff;
|
|
|
|
if (!value.has_color_value ())
|
|
return;
|
|
|
|
const OptionMetadata::ColorValue &val = value.color_value ();
|
|
|
|
if (val.has_red ())
|
|
{
|
|
int color = strtol ((char *) val.red ().c_str (), NULL, 0);
|
|
|
|
v->value.asColor.color.red = MAX (0, MIN (0xffff, color));
|
|
}
|
|
|
|
if (val.has_green ())
|
|
{
|
|
int color = strtol ((char *) val.green ().c_str (), NULL, 0);
|
|
|
|
v->value.asColor.color.green = MAX (0, MIN (0xffff, color));
|
|
}
|
|
|
|
if (val.has_blue ())
|
|
{
|
|
int color = strtol ((char *) val.blue ().c_str (), NULL, 0);
|
|
|
|
v->value.asColor.color.blue = MAX (0, MIN (0xffff, color));
|
|
}
|
|
|
|
if (val.has_alpha ())
|
|
{
|
|
int color = strtol ((char *) val.alpha ().c_str (), NULL, 0);
|
|
|
|
v->value.asColor.color.alpha = MAX (0, MIN (0xffff, color));
|
|
}
|
|
}
|
|
|
|
static void
|
|
initMatchValuePB (CCSSettingValue * v,
|
|
const GenericValueMetadata & value)
|
|
{
|
|
free (v->value.asMatch);
|
|
|
|
if (value.has_string_value ())
|
|
v->value.asMatch = strdup (value.string_value ().c_str ());
|
|
else
|
|
v->value.asMatch = strdup ("");
|
|
}
|
|
|
|
static void
|
|
initKeyValuePB (CCSSettingValue * v,
|
|
CCSSettingInfo * i,
|
|
const GenericValueMetadata & value)
|
|
{
|
|
memset (&v->value.asKey, 0, sizeof (v->value.asKey));
|
|
|
|
if (value.has_string_value ())
|
|
{
|
|
const char * val = value.string_value ().c_str ();
|
|
|
|
if (strcasecmp (val, "disabled"))
|
|
{
|
|
ccsStringToKeyBinding (val, &v->value.asKey);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
initButtonValuePB (CCSSettingValue * v,
|
|
CCSSettingInfo * i,
|
|
const GenericValueMetadata & value)
|
|
{
|
|
memset (&v->value.asButton, 0, sizeof (v->value.asButton));
|
|
|
|
if (value.has_string_value ())
|
|
{
|
|
const char * val = value.string_value ().c_str ();
|
|
|
|
if (strcasecmp (val, "disabled"))
|
|
{
|
|
ccsStringToButtonBinding (val, &v->value.asButton);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
initEdgeValuePB (CCSSettingValue * v,
|
|
CCSSettingInfo * i,
|
|
const GenericValueMetadata & value)
|
|
{
|
|
v->value.asEdge = 0;
|
|
|
|
if (value.has_edge_value ())
|
|
v->value.asEdge = value.edge_value ();
|
|
}
|
|
|
|
static void
|
|
initBellValuePB (CCSSettingValue * v,
|
|
CCSSettingInfo * i,
|
|
const GenericValueMetadata & value)
|
|
{
|
|
v->value.asBell = FALSE;
|
|
|
|
if (value.has_bool_value ())
|
|
v->value.asBell = value.bool_value ();
|
|
}
|
|
|
|
static void
|
|
initListValuePB (CCSSettingValue * v,
|
|
CCSSettingInfo * i,
|
|
const OptionMetadata & option)
|
|
{
|
|
int num, j;
|
|
|
|
num = option.default_value_size ();
|
|
|
|
if (num)
|
|
{
|
|
for (j = 0; j < num; j++)
|
|
{
|
|
CCSSettingValue *val;
|
|
val = (CCSSettingValue *) calloc (1, sizeof (CCSSettingValue));
|
|
if (!val)
|
|
continue;
|
|
|
|
val->parent = v->parent;
|
|
val->isListChild = TRUE;
|
|
|
|
switch (i->forList.listType)
|
|
{
|
|
case TypeBool:
|
|
initBoolValuePB (val, option.default_value (j));
|
|
break;
|
|
case TypeInt:
|
|
initIntValuePB (val, i->forList.listInfo,
|
|
option.default_value (j));
|
|
break;
|
|
case TypeFloat:
|
|
initFloatValuePB (val, i->forList.listInfo,
|
|
option.default_value (j));
|
|
break;
|
|
case TypeString:
|
|
initStringValuePB (val, i->forList.listInfo,
|
|
option.default_value (j));
|
|
break;
|
|
case TypeColor:
|
|
initColorValuePB (val, option.default_value (j));
|
|
break;
|
|
case TypeKey:
|
|
initKeyValuePB (val, i->forList.listInfo,
|
|
option.default_value (j));
|
|
break;
|
|
case TypeButton:
|
|
initButtonValuePB (val, i->forList.listInfo,
|
|
option.default_value (j));
|
|
break;
|
|
case TypeEdge:
|
|
initEdgeValuePB (val, i->forList.listInfo,
|
|
option.default_value (j));
|
|
break;
|
|
case TypeBell:
|
|
initBellValuePB (val, i->forList.listInfo,
|
|
option.default_value (j));
|
|
break;
|
|
case TypeMatch:
|
|
initMatchValuePB (val, option.default_value (j));
|
|
default:
|
|
break;
|
|
}
|
|
v->value.asList = ccsSettingValueListAppend (v->value.asList, val);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
initIntInfoPB (CCSSettingInfo * i, const OptionMetadata & option)
|
|
{
|
|
int num, j;
|
|
i->forInt.min = MINSHORT;
|
|
i->forInt.max = MAXSHORT;
|
|
i->forInt.desc = NULL;
|
|
|
|
if (option.has_int_min ())
|
|
i->forInt.min = option.int_min ();
|
|
|
|
if (option.has_int_max ())
|
|
i->forInt.max = option.int_max ();
|
|
|
|
if (!basicMetadata)
|
|
{
|
|
num = option.int_desc_size ();
|
|
for (j = 0; j < num; j++)
|
|
{
|
|
const OptionMetadata::IntDescription & intDescMetadata =
|
|
option.int_desc (j);
|
|
|
|
int val = intDescMetadata.value ();
|
|
|
|
if (val >= i->forInt.min && val <= i->forInt.max)
|
|
{
|
|
CCSIntDesc *intDesc;
|
|
|
|
intDesc = (CCSIntDesc *) calloc (1, sizeof (CCSIntDesc));
|
|
if (intDesc)
|
|
{
|
|
intDesc->name = strdup (intDescMetadata.name ().c_str ());
|
|
intDesc->value = val;
|
|
i->forInt.desc =
|
|
ccsIntDescListAppend (i->forInt.desc, intDesc);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
initFloatInfoPB (CCSSettingInfo * i, const OptionMetadata & option)
|
|
{
|
|
i->forFloat.min = MINSHORT;
|
|
i->forFloat.max = MAXSHORT;
|
|
i->forFloat.precision = 0.1f;
|
|
|
|
if (option.has_float_min ())
|
|
i->forFloat.min = option.float_min ();
|
|
|
|
if (option.has_float_max ())
|
|
i->forFloat.max = option.float_max ();
|
|
|
|
if (option.precision ())
|
|
i->forFloat.precision = option.precision ();
|
|
}
|
|
|
|
static void
|
|
initStringInfoPB (CCSSettingInfo * i, const OptionMetadata & option)
|
|
{
|
|
int num, j;
|
|
i->forString.restriction = NULL;
|
|
i->forString.sortStartsAt = -1;
|
|
i->forString.extensible = FALSE;
|
|
|
|
if (!basicMetadata)
|
|
{
|
|
if (option.has_extensible () && option.extensible ())
|
|
i->forString.extensible = TRUE;
|
|
|
|
if (option.has_sort_start ())
|
|
i->forString.sortStartsAt = option.sort_start ();
|
|
|
|
num = option.str_restriction_size ();
|
|
for (j = 0; j < num; j++)
|
|
{
|
|
const OptionMetadata::StringRestriction &
|
|
restrictionMetadata = option.str_restriction (j);
|
|
|
|
const char *value = restrictionMetadata.value ().c_str ();
|
|
const char *name = restrictionMetadata.name ().c_str ();
|
|
|
|
ccsAddRestrictionToStringInfo (&i->forString, value, name);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
initListInfoPB (CCSSettingInfo * i, const OptionMetadata & option)
|
|
{
|
|
CCSSettingInfo *info;
|
|
|
|
i->forList.listType = TypeBool;
|
|
i->forList.listInfo = NULL;
|
|
|
|
if (option.has_list_type ())
|
|
{
|
|
i->forList.listType = (CCSSettingType) option.list_type ();
|
|
}
|
|
switch (i->forList.listType)
|
|
{
|
|
case TypeInt:
|
|
{
|
|
info = (CCSSettingInfo *) calloc (1, sizeof (CCSSettingInfo));
|
|
if (info)
|
|
initIntInfoPB (info, option);
|
|
i->forList.listInfo = info;
|
|
}
|
|
break;
|
|
case TypeFloat:
|
|
{
|
|
info = (CCSSettingInfo *) calloc (1, sizeof (CCSSettingInfo));
|
|
if (info)
|
|
initFloatInfoPB (info, option);
|
|
i->forList.listInfo = info;
|
|
}
|
|
break;
|
|
case TypeString:
|
|
{
|
|
info = (CCSSettingInfo *) calloc (1, sizeof (CCSSettingInfo));
|
|
if (info)
|
|
initStringInfoPB (info, option);
|
|
i->forList.listInfo = info;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
initActionInfoPB (CCSSettingInfo * i, const OptionMetadata & option)
|
|
{
|
|
i->forAction.internal = FALSE;
|
|
|
|
if (option.has_internal () && option.internal ())
|
|
i->forAction.internal = TRUE;
|
|
}
|
|
|
|
static void
|
|
addOptionForPluginPB (CCSPlugin * plugin,
|
|
const char * name,
|
|
Bool isScreen,
|
|
unsigned int screen,
|
|
const StringList & groups,
|
|
const StringList & subgroups,
|
|
const OptionMetadata & option)
|
|
{
|
|
CCSSetting *setting;
|
|
|
|
if (ccsFindSetting (plugin, name, isScreen, screen))
|
|
{
|
|
fprintf (stderr, "[ERROR]: Option \"%s\" already defined\n", name);
|
|
return;
|
|
}
|
|
|
|
setting = (CCSSetting *) calloc (1, sizeof (CCSSetting));
|
|
if (!setting)
|
|
return;
|
|
|
|
setting->parent = plugin;
|
|
setting->isScreen = isScreen;
|
|
setting->screenNum = screen;
|
|
setting->isDefault = TRUE;
|
|
setting->name = strdup (name);
|
|
|
|
if (!basicMetadata)
|
|
{
|
|
setting->shortDesc =
|
|
strdup (option.has_short_desc () ?
|
|
option.short_desc ().c_str () :
|
|
name);
|
|
setting->longDesc =
|
|
strdup (option.has_long_desc () ?
|
|
option.long_desc ().c_str () :
|
|
name);
|
|
setting->hints = strdup (option.has_hints () ?
|
|
option.hints ().c_str () :
|
|
name);
|
|
setting->group =
|
|
strdup (option.group_id () >= 0 ?
|
|
groups.Get (option.group_id ()).c_str () :
|
|
"");
|
|
setting->subGroup =
|
|
strdup (option.subgroup_id () >= 0 ?
|
|
subgroups.Get (option.subgroup_id ()).c_str () :
|
|
"");
|
|
}
|
|
else
|
|
{
|
|
setting->shortDesc = strdup (name);
|
|
setting->longDesc = strdup ("");
|
|
setting->hints = strdup ("");
|
|
setting->group = strdup ("");
|
|
setting->subGroup = strdup ("");
|
|
}
|
|
|
|
setting->type = (CCSSettingType) option.type ();
|
|
setting->value = &setting->defaultValue;
|
|
setting->defaultValue.parent = setting;
|
|
|
|
switch (setting->type)
|
|
{
|
|
case TypeInt:
|
|
initIntInfoPB (&setting->info, option);
|
|
break;
|
|
case TypeFloat:
|
|
initFloatInfoPB (&setting->info, option);
|
|
break;
|
|
case TypeString:
|
|
initStringInfoPB (&setting->info, option);
|
|
break;
|
|
case TypeList:
|
|
initListInfoPB (&setting->info, option);
|
|
break;
|
|
case TypeKey:
|
|
case TypeButton:
|
|
case TypeEdge:
|
|
case TypeBell:
|
|
initActionInfoPB (&setting->info, option);
|
|
break;
|
|
case TypeAction: // do nothing and fall through
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (option.default_value_size () > 0)
|
|
{
|
|
switch (setting->type)
|
|
{
|
|
case TypeInt:
|
|
initIntValuePB (&setting->defaultValue, &setting->info,
|
|
option.default_value (0));
|
|
break;
|
|
case TypeBool:
|
|
initBoolValuePB (&setting->defaultValue, option.default_value (0));
|
|
break;
|
|
case TypeFloat:
|
|
initFloatValuePB (&setting->defaultValue, &setting->info,
|
|
option.default_value (0));
|
|
break;
|
|
case TypeString:
|
|
initStringValuePB (&setting->defaultValue, &setting->info,
|
|
option.default_value (0));
|
|
break;
|
|
case TypeColor:
|
|
initColorValuePB (&setting->defaultValue, option.default_value (0));
|
|
break;
|
|
case TypeKey:
|
|
initKeyValuePB (&setting->defaultValue, &setting->info,
|
|
option.default_value (0));
|
|
break;
|
|
case TypeButton:
|
|
initButtonValuePB (&setting->defaultValue, &setting->info,
|
|
option.default_value (0));
|
|
break;
|
|
case TypeEdge:
|
|
initEdgeValuePB (&setting->defaultValue, &setting->info,
|
|
option.default_value (0));
|
|
break;
|
|
case TypeBell:
|
|
initBellValuePB (&setting->defaultValue, &setting->info,
|
|
option.default_value (0));
|
|
break;
|
|
case TypeMatch:
|
|
initMatchValuePB (&setting->defaultValue,
|
|
option.default_value (0));
|
|
break;
|
|
case TypeList:
|
|
initListValuePB (&setting->defaultValue, &setting->info,
|
|
option);
|
|
break;
|
|
case TypeAction: // do nothing and fall through
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* if we have no set defaults, we have at least to set
|
|
the string defaults to empty strings */
|
|
switch (setting->type)
|
|
{
|
|
case TypeString:
|
|
setting->defaultValue.value.asString = strdup ("");
|
|
break;
|
|
case TypeMatch:
|
|
setting->defaultValue.value.asMatch = strdup ("");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
PLUGIN_PRIV (plugin);
|
|
|
|
pPrivate->settings = ccsSettingListAppend (pPrivate->settings, setting);
|
|
}
|
|
|
|
static void
|
|
addOptionFromPB (CCSPlugin * plugin,
|
|
Bool isScreen,
|
|
const StringList & groups,
|
|
const StringList & subgroups,
|
|
const OptionMetadata & option)
|
|
{
|
|
const char *name;
|
|
Bool readonly = FALSE;
|
|
|
|
name = option.name ().c_str ();
|
|
|
|
readonly = option.has_read_only () && option.read_only ();
|
|
|
|
if (!strlen (name) || readonly)
|
|
return;
|
|
|
|
if (isScreen)
|
|
{
|
|
for (unsigned i = 0; i < plugin->context->numScreens; i++)
|
|
addOptionForPluginPB (plugin, name, TRUE,
|
|
plugin->context->screens[i],
|
|
groups, subgroups, option);
|
|
}
|
|
else
|
|
addOptionForPluginPB (plugin, name, FALSE, 0, groups, subgroups, option);
|
|
}
|
|
|
|
static void
|
|
initOptionsFromPB (CCSPlugin * plugin,
|
|
const PluginMetadata & pluginPB)
|
|
{
|
|
int numOpt, i;
|
|
|
|
if (pluginPB.has_display ())
|
|
{
|
|
const ScreenMetadata &displayPB = pluginPB.display ();
|
|
|
|
// Display options
|
|
numOpt = displayPB.option_size ();
|
|
for (i = 0; i < numOpt; i++)
|
|
addOptionFromPB (plugin, FALSE,
|
|
displayPB.group_desc (),
|
|
displayPB.subgroup_desc (),
|
|
displayPB.option (i));
|
|
}
|
|
|
|
if (pluginPB.has_screen ())
|
|
{
|
|
const ScreenMetadata &screenPB = pluginPB.screen ();
|
|
|
|
// Screen options
|
|
numOpt = screenPB.option_size ();
|
|
for (i = 0; i < numOpt; i++)
|
|
addOptionFromPB (plugin, TRUE,
|
|
screenPB.group_desc (),
|
|
screenPB.subgroup_desc (),
|
|
screenPB.option (i));
|
|
}
|
|
}
|
|
|
|
static void
|
|
addStringsFromPB (CCSStringList * list,
|
|
const StringList & strings)
|
|
{
|
|
StringList::const_iterator it;
|
|
|
|
for (it = strings.begin (); it != strings.end (); it++)
|
|
{
|
|
const char *value = (*it).c_str ();
|
|
|
|
if (strlen (value))
|
|
*list = ccsStringListAppend (*list, strdup (value));
|
|
}
|
|
}
|
|
|
|
static void
|
|
addStringExtensionFromPB (CCSPlugin * plugin,
|
|
const ExtensionMetadata & extensionPB)
|
|
{
|
|
int j;
|
|
CCSStrExtension *extension;
|
|
|
|
extension = (CCSStrExtension *) calloc (1, sizeof (CCSStrExtension));
|
|
if (!extension)
|
|
return;
|
|
|
|
extension->isScreen = !(extensionPB.has_display () &&
|
|
extensionPB.display ());
|
|
|
|
extension->restriction = NULL;
|
|
|
|
extension->basePlugin = strdup (extensionPB.base_plugin ().c_str ());
|
|
|
|
addStringsFromPB (&extension->baseSettings,
|
|
extensionPB.base_option ());
|
|
|
|
int numRestrictions = extensionPB.str_restriction_size ();
|
|
if (!numRestrictions)
|
|
{
|
|
free (extension);
|
|
return;
|
|
}
|
|
|
|
for (j = 0; j < numRestrictions; j++)
|
|
{
|
|
const OptionMetadata::StringRestriction & restrictionPB =
|
|
extensionPB.str_restriction (j);
|
|
|
|
const char *value = restrictionPB.value ().c_str ();
|
|
const char *name = restrictionPB.name ().c_str ();
|
|
|
|
ccsAddRestrictionToStringExtension (extension, name, value);
|
|
}
|
|
|
|
PLUGIN_PRIV (plugin);
|
|
|
|
pPrivate->stringExtensions =
|
|
ccsStrExtensionListAppend (pPrivate->stringExtensions, extension);
|
|
}
|
|
|
|
static void
|
|
initStringExtensionsFromPB (CCSPlugin * plugin,
|
|
const PluginMetadata & pluginPB)
|
|
{
|
|
int numExtensions, i;
|
|
|
|
numExtensions = pluginPB.extension_size ();
|
|
for (i = 0; i < numExtensions; i++)
|
|
addStringExtensionFromPB (plugin, pluginPB.extension (i));
|
|
}
|
|
|
|
static void
|
|
initRulesFromPB (CCSPlugin * plugin, const PluginInfoMetadata & pluginInfoPB)
|
|
{
|
|
addStringsFromPB (&plugin->providesFeature, pluginInfoPB.feature ());
|
|
|
|
if (!pluginInfoPB.has_deps ())
|
|
return;
|
|
|
|
const DependenciesMetadata & deps = pluginInfoPB.deps ();
|
|
|
|
addStringsFromPB (&plugin->loadAfter, deps.after_plugin ());
|
|
addStringsFromPB (&plugin->loadBefore, deps.before_plugin ());
|
|
addStringsFromPB (&plugin->requiresPlugin, deps.require_plugin ());
|
|
addStringsFromPB (&plugin->requiresFeature, deps.require_feature ());
|
|
addStringsFromPB (&plugin->conflictPlugin, deps.conflict_plugin ());
|
|
addStringsFromPB (&plugin->conflictFeature, deps.conflict_feature ());
|
|
}
|
|
|
|
static void
|
|
addPluginFromPB (CCSContext * context,
|
|
const PluginInfoMetadata & pluginInfoPB,
|
|
char *file,
|
|
char *xmlFile)
|
|
{
|
|
const char *name;
|
|
CCSPlugin *plugin;
|
|
CCSPluginPrivate *pPrivate;
|
|
|
|
name = pluginInfoPB.name ().c_str ();
|
|
|
|
if (!strlen (name))
|
|
return;
|
|
|
|
if (ccsFindPlugin (context, name))
|
|
return;
|
|
|
|
if (!strcmp (name, "ini") || !strcmp (name, "gconf") ||
|
|
!strcmp (name, "ccp") || !strcmp (name, "kconfig"))
|
|
return;
|
|
|
|
plugin = (CCSPlugin *) calloc (1, sizeof (CCSPlugin));
|
|
if (!plugin)
|
|
return;
|
|
|
|
pPrivate = (CCSPluginPrivate *) calloc (1, sizeof (CCSPluginPrivate));
|
|
if (!pPrivate)
|
|
{
|
|
free (plugin);
|
|
return;
|
|
}
|
|
pPrivate->loaded = FALSE;
|
|
|
|
plugin->ccsPrivate = (void *) pPrivate;
|
|
|
|
if (file)
|
|
pPrivate->pbFilePath = strdup (file);
|
|
|
|
if (xmlFile)
|
|
{
|
|
pPrivate->xmlFile = strdup (xmlFile);
|
|
asprintf (&pPrivate->xmlPath, "/compiz/plugin[@name = '%s']", name);
|
|
}
|
|
|
|
plugin->context = context;
|
|
plugin->name = strdup (name);
|
|
|
|
if (!basicMetadata)
|
|
{
|
|
plugin->shortDesc =
|
|
strdup (pluginInfoPB.has_short_desc () ?
|
|
pluginInfoPB.short_desc ().c_str () :
|
|
name);
|
|
plugin->longDesc =
|
|
strdup (pluginInfoPB.has_long_desc () ?
|
|
pluginInfoPB.long_desc ().c_str () :
|
|
name);
|
|
plugin->category = strdup (pluginInfoPB.has_category () ?
|
|
pluginInfoPB.category ().c_str () :
|
|
"");
|
|
}
|
|
else
|
|
{
|
|
plugin->shortDesc = strdup (name);
|
|
plugin->longDesc = strdup (name);
|
|
plugin->category = strdup ("");
|
|
}
|
|
|
|
initRulesFromPB (plugin, pluginInfoPB);
|
|
|
|
context->plugins = ccsPluginListAppend (context->plugins, plugin);
|
|
}
|
|
|
|
static void
|
|
addCoreSettingsFromPB (CCSContext * context,
|
|
const PluginInfoMetadata & pluginInfoPB,
|
|
char *file,
|
|
char *xmlFile)
|
|
{
|
|
CCSPlugin *plugin;
|
|
CCSPluginPrivate *pPrivate;
|
|
|
|
if (ccsFindPlugin (context, "core"))
|
|
return;
|
|
|
|
plugin = (CCSPlugin*) calloc (1, sizeof (CCSPlugin));
|
|
if (!plugin)
|
|
return;
|
|
|
|
pPrivate = (CCSPluginPrivate *) calloc (1, sizeof (CCSPluginPrivate));
|
|
if (!pPrivate)
|
|
{
|
|
free (plugin);
|
|
return;
|
|
}
|
|
|
|
plugin->ccsPrivate = (void *) pPrivate;
|
|
|
|
if (file)
|
|
pPrivate->pbFilePath = strdup (file);
|
|
|
|
if (xmlFile)
|
|
{
|
|
pPrivate->xmlFile = strdup (xmlFile);
|
|
pPrivate->xmlPath = strdup ("/compiz/core");
|
|
}
|
|
|
|
plugin->context = context;
|
|
plugin->name = strdup ("core");
|
|
plugin->category = strdup ("General");
|
|
|
|
if (!basicMetadata)
|
|
{
|
|
plugin->shortDesc =
|
|
strdup (pluginInfoPB.has_short_desc () ?
|
|
pluginInfoPB.short_desc ().c_str () :
|
|
"General Options");
|
|
|
|
plugin->longDesc =
|
|
strdup (pluginInfoPB.has_long_desc () ?
|
|
pluginInfoPB.long_desc ().c_str () :
|
|
"General Compiz Options");
|
|
}
|
|
else
|
|
{
|
|
plugin->shortDesc = strdup ("General Options");
|
|
plugin->longDesc = strdup ("General Compiz Options");
|
|
}
|
|
|
|
initRulesFromPB (plugin, pluginInfoPB);
|
|
context->plugins = ccsPluginListAppend (context->plugins, plugin);
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
static int
|
|
pluginNameFilter (const struct dirent *name)
|
|
{
|
|
int length = strlen (name->d_name);
|
|
|
|
if (length < 7)
|
|
return 0;
|
|
|
|
if (strncmp (name->d_name, "lib", 3) ||
|
|
strncmp (name->d_name + length - 3, ".so", 3))
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
pluginXMLFilter (const struct dirent *name)
|
|
{
|
|
int length = strlen (name->d_name);
|
|
|
|
if (length < 5)
|
|
return 0;
|
|
|
|
if (strncmp (name->d_name + length - 4, ".xml", 4))
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/* XML parsing */
|
|
|
|
static CCSSettingType
|
|
getOptionType (const char *name)
|
|
{
|
|
static struct _TypeMap
|
|
{
|
|
const char *name;
|
|
CCSSettingType type;
|
|
} map[] = {
|
|
{ "bool", TypeBool },
|
|
{ "int", TypeInt },
|
|
{ "float", TypeFloat },
|
|
{ "string", TypeString },
|
|
{ "color", TypeColor },
|
|
{ "action", TypeAction },
|
|
{ "key", TypeKey },
|
|
{ "button", TypeButton },
|
|
{ "edge", TypeEdge },
|
|
{ "bell", TypeBell },
|
|
{ "match", TypeMatch },
|
|
{ "list", TypeList }
|
|
};
|
|
for (unsigned i = 0; i < sizeof (map) / sizeof (map[0]); i++)
|
|
if (strcasecmp (name, map[i].name) == 0)
|
|
return map[i].type;
|
|
|
|
return TypeNum;
|
|
}
|
|
|
|
static char *
|
|
getStringFromXPath (xmlDoc * doc, xmlNode * base, const char *path)
|
|
{
|
|
xmlXPathObjectPtr xpathObj;
|
|
xmlXPathContextPtr xpathCtx;
|
|
char *rv = NULL;
|
|
|
|
xpathCtx = xmlXPathNewContext (doc);
|
|
if (!xpathCtx)
|
|
return NULL;
|
|
|
|
if (base)
|
|
xpathCtx->node = base;
|
|
|
|
xpathObj = xmlXPathEvalExpression (BAD_CAST path, xpathCtx);
|
|
|
|
if (!xpathObj)
|
|
{
|
|
xmlXPathFreeContext (xpathCtx);
|
|
return NULL;
|
|
}
|
|
|
|
xpathObj = xmlXPathConvertString (xpathObj);
|
|
|
|
if (xpathObj->type == XPATH_STRING && xpathObj->stringval
|
|
&& strlen ((char *) xpathObj->stringval))
|
|
{
|
|
rv = strdup ((char *) xpathObj->stringval);
|
|
}
|
|
|
|
xmlXPathFreeObject (xpathObj);
|
|
xmlXPathFreeContext (xpathCtx);
|
|
return rv;
|
|
}
|
|
|
|
static xmlNode **
|
|
getNodesFromXPath (xmlDoc * doc, xmlNode * base, const char *path, int *num)
|
|
{
|
|
xmlXPathObjectPtr xpathObj;
|
|
xmlXPathContextPtr xpathCtx;
|
|
xmlNode **rv = NULL;
|
|
int size;
|
|
int i;
|
|
|
|
*num = 0;
|
|
|
|
xpathCtx = xmlXPathNewContext (doc);
|
|
if (!xpathCtx)
|
|
return NULL;
|
|
|
|
if (base)
|
|
xpathCtx->node = base;
|
|
|
|
xpathObj = xmlXPathEvalExpression (BAD_CAST path, xpathCtx);
|
|
if (!xpathObj)
|
|
{
|
|
xmlXPathFreeContext (xpathCtx);
|
|
return NULL;
|
|
}
|
|
|
|
size = (xpathObj->nodesetval) ? xpathObj->nodesetval->nodeNr : 0;
|
|
if (!size)
|
|
{
|
|
xmlXPathFreeObject (xpathObj);
|
|
xmlXPathFreeContext (xpathCtx);
|
|
return NULL;
|
|
}
|
|
|
|
rv = (xmlNode **) malloc (size * sizeof (xmlNode *));
|
|
if (!rv)
|
|
{
|
|
xmlXPathFreeObject (xpathObj);
|
|
xmlXPathFreeContext (xpathCtx);
|
|
return NULL;
|
|
}
|
|
*num = size;
|
|
|
|
for (i = 0; i < size; i++)
|
|
rv[i] = xpathObj->nodesetval->nodeTab[i];
|
|
|
|
xmlXPathFreeObject (xpathObj);
|
|
xmlXPathFreeContext (xpathCtx);
|
|
|
|
return rv;
|
|
}
|
|
|
|
static Bool
|
|
nodeExists (xmlNode * node, const char *path)
|
|
{
|
|
xmlNode **nodes = NULL;
|
|
int num;
|
|
nodes = getNodesFromXPath (node->doc, node, path, &num);
|
|
|
|
if (num)
|
|
{
|
|
free (nodes);
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static char *
|
|
stringFromNodeDef (xmlNode * node, const char *path, const char *def)
|
|
{
|
|
char *val;
|
|
char *rv = NULL;
|
|
|
|
val = getStringFromXPath (node->doc, node, path);
|
|
|
|
if (val)
|
|
{
|
|
rv = strdup (val);
|
|
free (val);
|
|
}
|
|
else if (def)
|
|
rv = strdup (def);
|
|
|
|
return rv;
|
|
}
|
|
|
|
static char *
|
|
stringFromNodeDefTrans (xmlNode * node, const char *path, const char *def)
|
|
{
|
|
char *lang;
|
|
char newPath[1024];
|
|
char *rv = NULL;
|
|
|
|
lang = getenv ("LANG");
|
|
|
|
if (!lang || !strlen (lang))
|
|
lang = getenv ("LC_ALL");
|
|
|
|
if (!lang || !strlen (lang))
|
|
lang = getenv ("LC_MESSAGES");
|
|
|
|
if (!lang || !strlen (lang))
|
|
return stringFromNodeDef (node, path, def);
|
|
|
|
snprintf (newPath, 1023, "%s[lang('%s')]", path, lang);
|
|
rv = stringFromNodeDef (node, newPath, NULL);
|
|
if (rv)
|
|
return rv;
|
|
|
|
snprintf (newPath, 1023, "%s[lang(substring-before('%s','.'))]", path, lang);
|
|
rv = stringFromNodeDef (node, newPath, NULL);
|
|
if (rv)
|
|
return rv;
|
|
|
|
snprintf (newPath, 1023, "%s[lang(substring-before('%s','_'))]", path, lang);
|
|
rv = stringFromNodeDef (node, newPath, NULL);
|
|
if (rv)
|
|
return rv;
|
|
|
|
snprintf (newPath, 1023, "%s[lang('C')]", path);
|
|
rv = stringFromNodeDef (node, newPath, NULL);
|
|
if (rv)
|
|
return rv;
|
|
|
|
return stringFromNodeDef (node, path, def);
|
|
}
|
|
|
|
static void
|
|
initBoolValue (CCSSettingValue * v,
|
|
xmlNode * node,
|
|
void * valuePBv)
|
|
{
|
|
char *value;
|
|
|
|
v->value.asBool = FALSE;
|
|
|
|
value = getStringFromXPath (node->doc, node, "child::text()");
|
|
|
|
if (value)
|
|
{
|
|
if (strcasecmp ((char *) value, "true") == 0)
|
|
{
|
|
v->value.asBool = TRUE;
|
|
#ifdef USE_PROTOBUF
|
|
if (valuePBv)
|
|
((GenericValueMetadata *) valuePBv)->set_bool_value (TRUE);
|
|
#endif
|
|
}
|
|
free (value);
|
|
}
|
|
}
|
|
|
|
static void
|
|
initIntValue (CCSSettingValue * v,
|
|
CCSSettingInfo * i,
|
|
xmlNode * node,
|
|
void * valuePBv)
|
|
{
|
|
char *value;
|
|
|
|
v->value.asInt = (i->forInt.min + i->forInt.max) / 2;
|
|
|
|
value = getStringFromXPath (node->doc, node, "child::text()");
|
|
|
|
if (value)
|
|
{
|
|
int val = strtol ((char *) value, NULL, 0);
|
|
|
|
if (val >= i->forInt.min && val <= i->forInt.max)
|
|
{
|
|
v->value.asInt = val;
|
|
#ifdef USE_PROTOBUF
|
|
if (valuePBv)
|
|
((GenericValueMetadata *) valuePBv)->set_int_value (val);
|
|
#endif
|
|
}
|
|
|
|
free (value);
|
|
}
|
|
}
|
|
|
|
static void
|
|
initFloatValue (CCSSettingValue * v,
|
|
CCSSettingInfo * i,
|
|
xmlNode * node,
|
|
void * valuePBv)
|
|
{
|
|
char *value;
|
|
char *loc;
|
|
|
|
v->value.asFloat = (i->forFloat.min + i->forFloat.max) / 2;
|
|
|
|
loc = setlocale (LC_NUMERIC, NULL);
|
|
setlocale (LC_NUMERIC, "C");
|
|
value = getStringFromXPath (node->doc, node, "child::text()");
|
|
|
|
if (value)
|
|
{
|
|
float val = strtod ((char *) value, NULL);
|
|
|
|
if (val >= i->forFloat.min && val <= i->forFloat.max)
|
|
{
|
|
v->value.asFloat = val;
|
|
#ifdef USE_PROTOBUF
|
|
if (valuePBv)
|
|
((GenericValueMetadata *) valuePBv)->set_float_value (val);
|
|
#endif
|
|
}
|
|
|
|
free (value);
|
|
}
|
|
|
|
setlocale (LC_NUMERIC, loc);
|
|
}
|
|
|
|
static void
|
|
initStringValue (CCSSettingValue * v,
|
|
CCSSettingInfo * i,
|
|
xmlNode * node,
|
|
void * valuePBv)
|
|
{
|
|
char *value;
|
|
|
|
value = getStringFromXPath (node->doc, node, "child::text()");
|
|
|
|
if (value)
|
|
{
|
|
free (v->value.asString);
|
|
v->value.asString = strdup (value);
|
|
|
|
#ifdef USE_PROTOBUF
|
|
if (valuePBv)
|
|
((GenericValueMetadata *) valuePBv)->set_string_value (value);
|
|
#endif
|
|
free (value);
|
|
}
|
|
else
|
|
v->value.asString = strdup ("");
|
|
}
|
|
|
|
static void
|
|
initColorValue (CCSSettingValue * v, xmlNode * node, void * valuePBv)
|
|
{
|
|
char *value;
|
|
|
|
memset (&v->value.asColor, 0, sizeof (v->value.asColor));
|
|
v->value.asColor.color.alpha = 0xffff;
|
|
|
|
#ifdef USE_PROTOBUF
|
|
OptionMetadata::ColorValue *colorPB = NULL;
|
|
if (valuePBv)
|
|
colorPB = ((GenericValueMetadata *) valuePBv)->mutable_color_value ();
|
|
#endif
|
|
|
|
value = getStringFromXPath (node->doc, node, "red/child::text()");
|
|
if (value)
|
|
{
|
|
int color = strtol ((char *) value, NULL, 0);
|
|
|
|
v->value.asColor.color.red = MAX (0, MIN (0xffff, color));
|
|
#ifdef USE_PROTOBUF
|
|
if (colorPB)
|
|
colorPB->set_red (value);
|
|
#endif
|
|
free (value);
|
|
}
|
|
|
|
value = getStringFromXPath (node->doc, node, "green/child::text()");
|
|
if (value)
|
|
{
|
|
int color = strtol ((char *) value, NULL, 0);
|
|
|
|
v->value.asColor.color.green = MAX (0, MIN (0xffff, color));
|
|
#ifdef USE_PROTOBUF
|
|
if (colorPB)
|
|
colorPB->set_green (value);
|
|
#endif
|
|
free (value);
|
|
}
|
|
|
|
value = getStringFromXPath (node->doc, node, "blue/child::text()");
|
|
if (value)
|
|
{
|
|
int color = strtol ((char *) value, NULL, 0);
|
|
|
|
v->value.asColor.color.blue = MAX (0, MIN (0xffff, color));
|
|
#ifdef USE_PROTOBUF
|
|
if (colorPB)
|
|
colorPB->set_blue (value);
|
|
#endif
|
|
free (value);
|
|
}
|
|
|
|
value = getStringFromXPath (node->doc, node, "alpha/child::text()");
|
|
if (value)
|
|
{
|
|
int color = strtol (value, NULL, 0);
|
|
|
|
v->value.asColor.color.alpha = MAX (0, MIN (0xffff, color));
|
|
#ifdef USE_PROTOBUF
|
|
if (colorPB)
|
|
colorPB->set_alpha (value);
|
|
#endif
|
|
free (value);
|
|
}
|
|
}
|
|
|
|
static void
|
|
initMatchValue (CCSSettingValue * v, xmlNode * node, void * valuePBv)
|
|
{
|
|
char *value;
|
|
|
|
value = getStringFromXPath (node->doc, node, "child::text()");
|
|
if (value)
|
|
{
|
|
free (v->value.asMatch);
|
|
v->value.asMatch = strdup (value);
|
|
|
|
#ifdef USE_PROTOBUF
|
|
if (valuePBv)
|
|
((GenericValueMetadata *) valuePBv)->set_string_value (value);
|
|
#endif
|
|
free (value);
|
|
}
|
|
else
|
|
v->value.asMatch = strdup ("");
|
|
}
|
|
|
|
static void
|
|
initKeyValue (CCSSettingValue * v,
|
|
CCSSettingInfo * i,
|
|
xmlNode * node,
|
|
void * valuePBv)
|
|
{
|
|
char *value;
|
|
|
|
memset (&v->value.asKey, 0, sizeof (v->value.asKey));
|
|
|
|
value = getStringFromXPath (node->doc, node, "child::text()");
|
|
if (value)
|
|
{
|
|
#ifdef USE_PROTOBUF
|
|
if (valuePBv)
|
|
((GenericValueMetadata *) valuePBv)->set_string_value (value);
|
|
#endif
|
|
if (strcasecmp (value, "disabled"))
|
|
{
|
|
ccsStringToKeyBinding (value, &v->value.asKey);
|
|
}
|
|
free (value);
|
|
}
|
|
}
|
|
|
|
static void
|
|
initButtonValue (CCSSettingValue * v,
|
|
CCSSettingInfo * i,
|
|
xmlNode * node,
|
|
void * valuePBv)
|
|
{
|
|
char *value;
|
|
|
|
memset (&v->value.asButton, 0, sizeof (v->value.asButton));
|
|
|
|
value = getStringFromXPath (node->doc, node, "child::text()");
|
|
if (value)
|
|
{
|
|
#ifdef USE_PROTOBUF
|
|
if (valuePBv)
|
|
((GenericValueMetadata *) valuePBv)->set_string_value (value);
|
|
#endif
|
|
if (strcasecmp (value, "disabled"))
|
|
{
|
|
ccsStringToButtonBinding (value, &v->value.asButton);
|
|
}
|
|
free (value);
|
|
}
|
|
}
|
|
|
|
static void
|
|
initEdgeValue (CCSSettingValue * v,
|
|
CCSSettingInfo * i,
|
|
xmlNode * node,
|
|
void * valuePBv)
|
|
{
|
|
xmlNode **nodes;
|
|
char *value;
|
|
int k, num;
|
|
|
|
v->value.asEdge = 0;
|
|
|
|
static const char *edge[] = {
|
|
"Left",
|
|
"Right",
|
|
"Top",
|
|
"Bottom",
|
|
"TopLeft",
|
|
"TopRight",
|
|
"BottomLeft",
|
|
"BottomRight"
|
|
};
|
|
|
|
nodes = getNodesFromXPath (node->doc, node, "edge", &num);
|
|
|
|
for (k = 0; k < num; k++)
|
|
{
|
|
value = getStringFromXPath (node->doc, nodes[k], "@name");
|
|
if (value)
|
|
{
|
|
for (unsigned j = 0; j < sizeof (edge) / sizeof (edge[0]); j++)
|
|
{
|
|
if (strcasecmp ((char *) value, edge[j]) == 0)
|
|
v->value.asEdge |= (1 << j);
|
|
}
|
|
free (value);
|
|
}
|
|
}
|
|
if (num)
|
|
free (nodes);
|
|
|
|
#ifdef USE_PROTOBUF
|
|
if (valuePBv)
|
|
((GenericValueMetadata *) valuePBv)->set_edge_value (v->value.asEdge);
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
initBellValue (CCSSettingValue * v,
|
|
CCSSettingInfo * i,
|
|
xmlNode * node,
|
|
void * valuePBv)
|
|
{
|
|
char *value;
|
|
|
|
v->value.asBell = FALSE;
|
|
|
|
value = getStringFromXPath (node->doc, node, "child::text()");
|
|
if (value)
|
|
{
|
|
if (!strcasecmp (value, "true"))
|
|
{
|
|
v->value.asBell = TRUE;
|
|
#ifdef USE_PROTOBUF
|
|
if (valuePBv)
|
|
((GenericValueMetadata *) valuePBv)->set_bool_value (TRUE);
|
|
#endif
|
|
}
|
|
free (value);
|
|
}
|
|
}
|
|
|
|
static void
|
|
initListValue (CCSSettingValue * v,
|
|
CCSSettingInfo * i,
|
|
xmlNode * node,
|
|
void * optionPBv)
|
|
{
|
|
xmlNode **nodes;
|
|
int num, j;
|
|
|
|
nodes = getNodesFromXPath (node->doc, node, "value", &num);
|
|
if (num)
|
|
{
|
|
for (j = 0; j < num; j++)
|
|
{
|
|
void *valuePBv = NULL;
|
|
#ifdef USE_PROTOBUF
|
|
if (optionPBv)
|
|
valuePBv = ((OptionMetadata *) optionPBv)->add_default_value ();
|
|
#endif
|
|
CCSSettingValue *val;
|
|
val = (CCSSettingValue *) calloc (1, sizeof (CCSSettingValue));
|
|
if (!val)
|
|
continue;
|
|
|
|
val->parent = v->parent;
|
|
val->isListChild = TRUE;
|
|
|
|
switch (i->forList.listType)
|
|
{
|
|
case TypeBool:
|
|
initBoolValue (val, nodes[j], valuePBv);
|
|
break;
|
|
case TypeInt:
|
|
initIntValue (val, i->forList.listInfo, nodes[j], valuePBv);
|
|
break;
|
|
case TypeFloat:
|
|
initFloatValue (val, i->forList.listInfo, nodes[j], valuePBv);
|
|
break;
|
|
case TypeString:
|
|
initStringValue (val, i->forList.listInfo, nodes[j], valuePBv);
|
|
break;
|
|
case TypeColor:
|
|
initColorValue (val, nodes[j], valuePBv);
|
|
break;
|
|
case TypeKey:
|
|
initKeyValue (val, i->forList.listInfo, nodes[j], valuePBv);
|
|
break;
|
|
case TypeButton:
|
|
initButtonValue (val, i->forList.listInfo, nodes[j], valuePBv);
|
|
break;
|
|
case TypeEdge:
|
|
initEdgeValue (val, i->forList.listInfo, nodes[j], valuePBv);
|
|
break;
|
|
case TypeBell:
|
|
initBellValue (val, i->forList.listInfo, nodes[j], valuePBv);
|
|
break;
|
|
case TypeMatch:
|
|
initMatchValue (val, nodes[j], valuePBv);
|
|
default:
|
|
break;
|
|
}
|
|
v->value.asList = ccsSettingValueListAppend (v->value.asList, val);
|
|
}
|
|
free (nodes);
|
|
}
|
|
}
|
|
|
|
static void
|
|
initIntInfo (CCSSettingInfo * i, xmlNode * node, void * optionPBv)
|
|
{
|
|
xmlNode **nodes;
|
|
char *name;
|
|
char *value;
|
|
int num, j;
|
|
i->forInt.min = MINSHORT;
|
|
i->forInt.max = MAXSHORT;
|
|
i->forInt.desc = NULL;
|
|
|
|
value = getStringFromXPath (node->doc, node, "min/child::text()");
|
|
if (value)
|
|
{
|
|
int val = strtol (value, NULL, 0);
|
|
i->forInt.min = val;
|
|
free (value);
|
|
#ifdef USE_PROTOBUF
|
|
if (optionPBv)
|
|
((OptionMetadata *) optionPBv)->set_int_min (val);
|
|
#endif
|
|
}
|
|
|
|
value = getStringFromXPath (node->doc, node, "max/child::text()");
|
|
if (value)
|
|
{
|
|
int val = strtol (value, NULL, 0);
|
|
i->forInt.max = val;
|
|
free (value);
|
|
#ifdef USE_PROTOBUF
|
|
if (optionPBv)
|
|
((OptionMetadata *) optionPBv)->set_int_max (val);
|
|
#endif
|
|
}
|
|
|
|
if (!basicMetadata)
|
|
{
|
|
nodes = getNodesFromXPath (node->doc, node, "desc", &num);
|
|
if (num)
|
|
{
|
|
for (j = 0; j < num; j++)
|
|
{
|
|
value = getStringFromXPath (node->doc, nodes[j],
|
|
"value/child::text()");
|
|
if (value)
|
|
{
|
|
int val = strtol (value, NULL, 0);
|
|
free (value);
|
|
|
|
if (val >= i->forInt.min && val <= i->forInt.max)
|
|
{
|
|
name = stringFromNodeDefTrans (nodes[j],
|
|
"name/child::text()",
|
|
NULL);
|
|
if (name)
|
|
{
|
|
CCSIntDesc *intDesc;
|
|
|
|
intDesc = (CCSIntDesc *) calloc (1, sizeof (CCSIntDesc));
|
|
if (intDesc)
|
|
{
|
|
intDesc->name = strdup (name);
|
|
intDesc->value = val;
|
|
i->forInt.desc =
|
|
ccsIntDescListAppend (i->forInt.desc,
|
|
intDesc);
|
|
#ifdef USE_PROTOBUF
|
|
if (optionPBv)
|
|
{
|
|
OptionMetadata::IntDescription *intDescPB =
|
|
((OptionMetadata *) optionPBv)->
|
|
add_int_desc ();
|
|
intDescPB->set_value (val);
|
|
intDescPB->set_name (name);
|
|
}
|
|
#endif
|
|
}
|
|
free (name);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
free (nodes);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
initFloatInfo (CCSSettingInfo * i, xmlNode * node, void * optionPBv)
|
|
{
|
|
char *value;
|
|
char *loc;
|
|
|
|
i->forFloat.min = MINSHORT;
|
|
i->forFloat.max = MAXSHORT;
|
|
i->forFloat.precision = 0.1f;
|
|
|
|
loc = setlocale (LC_NUMERIC, NULL);
|
|
setlocale (LC_NUMERIC, "C");
|
|
value = getStringFromXPath (node->doc, node, "min/child::text()");
|
|
if (value)
|
|
{
|
|
float val = strtod (value, NULL);
|
|
i->forFloat.min = val;
|
|
free (value);
|
|
#ifdef USE_PROTOBUF
|
|
if (optionPBv)
|
|
((OptionMetadata *) optionPBv)->set_float_min (val);
|
|
#endif
|
|
}
|
|
|
|
value = getStringFromXPath (node->doc, node, "max/child::text()");
|
|
if (value)
|
|
{
|
|
float val = strtod (value, NULL);
|
|
i->forFloat.max = val;
|
|
free (value);
|
|
#ifdef USE_PROTOBUF
|
|
if (optionPBv)
|
|
((OptionMetadata *) optionPBv)->set_float_max (val);
|
|
#endif
|
|
}
|
|
|
|
value = getStringFromXPath (node->doc, node, "precision/child::text()");
|
|
if (value)
|
|
{
|
|
float val = strtod (value, NULL);
|
|
i->forFloat.precision = val;
|
|
free (value);
|
|
#ifdef USE_PROTOBUF
|
|
if (optionPBv)
|
|
((OptionMetadata *) optionPBv)->set_precision (val);
|
|
#endif
|
|
}
|
|
|
|
setlocale (LC_NUMERIC, loc);
|
|
}
|
|
|
|
static void
|
|
initStringInfo (CCSSettingInfo * i, xmlNode * node, void * optionPBv)
|
|
{
|
|
xmlNode **nodes;
|
|
char *name;
|
|
char *value;
|
|
int num, j;
|
|
i->forString.restriction = NULL;
|
|
i->forString.sortStartsAt = -1;
|
|
i->forString.extensible = FALSE;
|
|
|
|
if (!basicMetadata)
|
|
{
|
|
if (nodeExists (node, "extensible"))
|
|
{
|
|
i->forString.extensible = TRUE;
|
|
#ifdef USE_PROTOBUF
|
|
if (optionPBv)
|
|
((OptionMetadata *) optionPBv)->set_extensible (TRUE);
|
|
#endif
|
|
}
|
|
|
|
nodes = getNodesFromXPath (node->doc, node, "sort", &num);
|
|
if (num)
|
|
{
|
|
int val = 0; /* Start sorting at 0 unless otherwise specified. */
|
|
|
|
value = getStringFromXPath (node->doc, nodes[0], "@start");
|
|
if (value)
|
|
{
|
|
/* Custom starting value specified. */
|
|
val = strtol (value, NULL, 0);
|
|
if (val < 0)
|
|
val = 0;
|
|
free (value);
|
|
}
|
|
i->forString.sortStartsAt = val;
|
|
#ifdef USE_PROTOBUF
|
|
if (optionPBv)
|
|
((OptionMetadata *) optionPBv)->set_sort_start (val);
|
|
#endif
|
|
free (nodes);
|
|
}
|
|
|
|
nodes = getNodesFromXPath (node->doc, node, "restriction", &num);
|
|
if (num)
|
|
{
|
|
for (j = 0; j < num; j++)
|
|
{
|
|
#ifdef USE_PROTOBUF
|
|
OptionMetadata::StringRestriction * strRestrictionPB = NULL;
|
|
if (optionPBv)
|
|
strRestrictionPB =
|
|
((OptionMetadata *) optionPBv)->add_str_restriction ();
|
|
#endif
|
|
value = getStringFromXPath (node->doc, nodes[j],
|
|
"value/child::text()");
|
|
if (value)
|
|
{
|
|
name = stringFromNodeDefTrans (nodes[j],
|
|
"name/child::text()",
|
|
NULL);
|
|
if (name)
|
|
{
|
|
ccsAddRestrictionToStringInfo (&i->forString,
|
|
name, value);
|
|
#ifdef USE_PROTOBUF
|
|
if (strRestrictionPB)
|
|
{
|
|
strRestrictionPB->set_value (value);
|
|
strRestrictionPB->set_name (name);
|
|
}
|
|
#endif
|
|
free (name);
|
|
}
|
|
free (value);
|
|
}
|
|
}
|
|
free (nodes);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
initListInfo (CCSSettingInfo * i, xmlNode * node, void * optionPBv)
|
|
{
|
|
char *value;
|
|
CCSSettingInfo *info;
|
|
|
|
i->forList.listType = TypeBool;
|
|
i->forList.listInfo = NULL;
|
|
|
|
value = getStringFromXPath (node->doc, node, "type/child::text()");
|
|
|
|
if (!value)
|
|
return;
|
|
|
|
i->forList.listType = getOptionType (value);
|
|
#ifdef USE_PROTOBUF
|
|
if (optionPBv)
|
|
((OptionMetadata *) optionPBv)->set_list_type
|
|
((OptionMetadata::Type) i->forList.listType);
|
|
#endif
|
|
|
|
free (value);
|
|
|
|
switch (i->forList.listType)
|
|
{
|
|
case TypeInt:
|
|
{
|
|
info = (CCSSettingInfo *) calloc (1, sizeof (CCSSettingInfo));
|
|
if (info)
|
|
initIntInfo (info, node, optionPBv);
|
|
i->forList.listInfo = info;
|
|
}
|
|
break;
|
|
case TypeFloat:
|
|
{
|
|
info = (CCSSettingInfo *) calloc (1, sizeof (CCSSettingInfo));
|
|
if (info)
|
|
initFloatInfo (info, node, optionPBv);
|
|
i->forList.listInfo = info;
|
|
}
|
|
break;
|
|
case TypeString:
|
|
{
|
|
info = (CCSSettingInfo *) calloc (1, sizeof (CCSSettingInfo));
|
|
if (info)
|
|
initStringInfo (info, node, optionPBv);
|
|
i->forList.listInfo = info;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
initActionInfo (CCSSettingInfo * i, xmlNode * node, void * optionPBv)
|
|
{
|
|
char *value;
|
|
|
|
i->forAction.internal = FALSE;
|
|
|
|
value = getStringFromXPath (node->doc, node, "internal/child::text()");
|
|
if (value)
|
|
{
|
|
if (strcasecmp (value, "true") == 0)
|
|
{
|
|
i->forAction.internal = TRUE;
|
|
#ifdef USE_PROTOBUF
|
|
if (optionPBv)
|
|
((OptionMetadata *) optionPBv)->set_internal (TRUE);
|
|
#endif
|
|
}
|
|
free (value);
|
|
return;
|
|
}
|
|
if (nodeExists (node, "internal"))
|
|
{
|
|
i->forAction.internal = TRUE;
|
|
#ifdef USE_PROTOBUF
|
|
if (optionPBv)
|
|
((OptionMetadata *) optionPBv)->set_internal (TRUE);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
#ifdef USE_PROTOBUF
|
|
static void
|
|
checkAddGroupSubgroup (OptionMetadata *optPB,
|
|
StringList *descList,
|
|
char *name,
|
|
Bool isGroup)
|
|
{
|
|
// Check if group has the same name as the last group in the groups list
|
|
int len = descList->size ();
|
|
if (len > 0 &&
|
|
strcmp (name, descList->Get (len - 1).c_str ()) == 0)
|
|
{
|
|
if (isGroup)
|
|
optPB->set_group_id (len - 1);
|
|
else
|
|
optPB->set_subgroup_id (len - 1);
|
|
}
|
|
else
|
|
{
|
|
// Add new group to the list
|
|
descList->Add ()->assign (name);
|
|
|
|
if (isGroup)
|
|
optPB->set_group_id (len);
|
|
else
|
|
optPB->set_subgroup_id (len);
|
|
}
|
|
}
|
|
|
|
static Bool
|
|
createProtoBufCacheDir ()
|
|
{
|
|
if (metadataCacheDir.length () > 0)
|
|
{
|
|
// Cache dir must have been created already, since otherwise it would
|
|
// be "". So we can return here.
|
|
return TRUE;
|
|
}
|
|
char *cacheBaseDir = NULL;
|
|
char *cacheHome = getenv ("XDG_CACHE_HOME");
|
|
|
|
if (cacheHome && strlen (cacheHome))
|
|
{
|
|
asprintf (&cacheBaseDir, "%s", cacheHome);
|
|
}
|
|
else
|
|
{
|
|
char *home = getenv ("HOME");
|
|
if (home && strlen (home))
|
|
{
|
|
asprintf (&cacheBaseDir, "%s/.cache", home);
|
|
}
|
|
}
|
|
|
|
if (cacheBaseDir)
|
|
{
|
|
metadataCacheDir = cacheBaseDir;
|
|
if (metadataCacheDir[metadataCacheDir.length () - 1] != '/')
|
|
metadataCacheDir += "/";
|
|
metadataCacheDir += "compizconfig";
|
|
std::string metadataCacheFileDummy = metadataCacheDir + "/dummy";
|
|
|
|
// Create cache dir
|
|
Bool success = ccsCreateDirFor (metadataCacheFileDummy.c_str ());
|
|
if (!success)
|
|
fprintf (stderr, "[ERROR]: Error creating directory \"%s\"\n",
|
|
metadataCacheDir.c_str ());
|
|
free (cacheBaseDir);
|
|
|
|
if (success)
|
|
return TRUE; // metadataCacheDir will be used later in this case
|
|
|
|
metadataCacheDir = ""; // invalidate metadataCacheDir
|
|
}
|
|
|
|
usingProtobuf = FALSE; // Disable protobuf if cache dir cannot be created
|
|
return FALSE;
|
|
}
|
|
|
|
#endif
|
|
|
|
static void
|
|
addOptionForPlugin (CCSPlugin * plugin,
|
|
char * name,
|
|
char * type,
|
|
Bool isReadonly,
|
|
Bool isScreen,
|
|
unsigned int screen,
|
|
xmlNode * node,
|
|
void * groupListPBv,
|
|
void * subgroupListPBv,
|
|
void * optionPBv)
|
|
{
|
|
xmlNode **nodes;
|
|
int num = 0;
|
|
CCSSetting *setting;
|
|
|
|
if (ccsFindSetting (plugin, name, isScreen, screen))
|
|
{
|
|
fprintf (stderr, "[ERROR]: Option \"%s\" already defined\n", name);
|
|
return;
|
|
}
|
|
|
|
if (getOptionType (type) == TypeNum)
|
|
return;
|
|
|
|
setting = (CCSSetting *) calloc (1, sizeof (CCSSetting));
|
|
if (!setting)
|
|
return;
|
|
|
|
setting->parent = plugin;
|
|
setting->isScreen = isScreen;
|
|
setting->screenNum = screen;
|
|
setting->isDefault = TRUE;
|
|
setting->name = strdup (name);
|
|
|
|
if (!basicMetadata)
|
|
{
|
|
setting->shortDesc =
|
|
stringFromNodeDefTrans (node, "short/child::text()", name);
|
|
setting->longDesc =
|
|
stringFromNodeDefTrans (node, "long/child::text()", "");
|
|
setting->hints = stringFromNodeDef (node, "hints/child::text()", "");
|
|
setting->group =
|
|
stringFromNodeDefTrans (node, "ancestor::group/short/child::text()",
|
|
"");
|
|
setting->subGroup =
|
|
stringFromNodeDefTrans (node,
|
|
"ancestor::subgroup/short/child::text()",
|
|
"");
|
|
}
|
|
else
|
|
{
|
|
setting->shortDesc = strdup (name);
|
|
setting->longDesc = strdup ("");
|
|
setting->hints = strdup ("");
|
|
setting->group = strdup ("");
|
|
setting->subGroup = strdup ("");
|
|
}
|
|
setting->type = getOptionType (type);
|
|
|
|
#ifdef USE_PROTOBUF
|
|
OptionMetadata *optPB = NULL;
|
|
|
|
if (optionPBv)
|
|
{
|
|
optPB = (OptionMetadata *) optionPBv;
|
|
|
|
optPB->set_name (name);
|
|
optPB->set_type ((OptionMetadata::Type) setting->type);
|
|
if (isReadonly)
|
|
optPB->set_read_only (isReadonly);
|
|
|
|
optPB->set_short_desc (setting->shortDesc);
|
|
optPB->set_long_desc (setting->longDesc);
|
|
|
|
if (strlen (setting->hints) > 0)
|
|
optPB->set_hints (setting->hints);
|
|
|
|
if (groupListPBv && strlen (setting->group) > 0)
|
|
checkAddGroupSubgroup (optPB, (StringList *) groupListPBv,
|
|
setting->group, TRUE);
|
|
if (subgroupListPBv && strlen (setting->subGroup) > 0)
|
|
checkAddGroupSubgroup (optPB, (StringList *) subgroupListPBv,
|
|
setting->subGroup, FALSE);
|
|
}
|
|
#endif
|
|
setting->value = &setting->defaultValue;
|
|
setting->defaultValue.parent = setting;
|
|
|
|
switch (setting->type)
|
|
{
|
|
case TypeInt:
|
|
initIntInfo (&setting->info, node, optionPBv);
|
|
break;
|
|
case TypeFloat:
|
|
initFloatInfo (&setting->info, node, optionPBv);
|
|
break;
|
|
case TypeString:
|
|
initStringInfo (&setting->info, node, optionPBv);
|
|
break;
|
|
case TypeList:
|
|
initListInfo (&setting->info, node, optionPBv);
|
|
break;
|
|
case TypeKey:
|
|
case TypeButton:
|
|
case TypeEdge:
|
|
case TypeBell:
|
|
initActionInfo (&setting->info, node, optionPBv);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
nodes = getNodesFromXPath (node->doc, node, "default", &num);
|
|
if (num)
|
|
{
|
|
void * valuePBv = NULL;
|
|
#ifdef USE_PROTOBUF
|
|
if (optPB && setting->type != TypeList)
|
|
valuePBv = optPB->add_default_value ();
|
|
#endif
|
|
switch (setting->type)
|
|
{
|
|
case TypeInt:
|
|
initIntValue (&setting->defaultValue, &setting->info, nodes[0],
|
|
valuePBv);
|
|
break;
|
|
case TypeBool:
|
|
initBoolValue (&setting->defaultValue, nodes[0],
|
|
valuePBv);
|
|
break;
|
|
case TypeFloat:
|
|
initFloatValue (&setting->defaultValue, &setting->info, nodes[0],
|
|
valuePBv);
|
|
break;
|
|
case TypeString:
|
|
initStringValue (&setting->defaultValue, &setting->info, nodes[0],
|
|
valuePBv);
|
|
break;
|
|
case TypeColor:
|
|
initColorValue (&setting->defaultValue, nodes[0], valuePBv);
|
|
break;
|
|
case TypeKey:
|
|
initKeyValue (&setting->defaultValue, &setting->info, nodes[0],
|
|
valuePBv);
|
|
break;
|
|
case TypeButton:
|
|
initButtonValue (&setting->defaultValue, &setting->info, nodes[0],
|
|
valuePBv);
|
|
break;
|
|
case TypeEdge:
|
|
initEdgeValue (&setting->defaultValue, &setting->info, nodes[0],
|
|
valuePBv);
|
|
break;
|
|
case TypeBell:
|
|
initBellValue (&setting->defaultValue, &setting->info, nodes[0],
|
|
valuePBv);
|
|
break;
|
|
case TypeMatch:
|
|
initMatchValue (&setting->defaultValue, nodes[0],
|
|
valuePBv);
|
|
break;
|
|
case TypeList:
|
|
initListValue (&setting->defaultValue, &setting->info, nodes[0],
|
|
optionPBv);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* if we have no set defaults, we have at least to set
|
|
the string defaults to empty strings */
|
|
switch (setting->type)
|
|
{
|
|
case TypeString:
|
|
setting->defaultValue.value.asString = strdup ("");
|
|
break;
|
|
case TypeMatch:
|
|
setting->defaultValue.value.asMatch = strdup ("");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (nodes)
|
|
free (nodes);
|
|
|
|
if (isReadonly)
|
|
{
|
|
// Will come here only when protobuf is enabled
|
|
ccsFreeSetting (setting);
|
|
return;
|
|
}
|
|
// printSetting (setting);
|
|
PLUGIN_PRIV (plugin);
|
|
pPrivate->settings = ccsSettingListAppend (pPrivate->settings, setting);
|
|
}
|
|
|
|
static void
|
|
addOptionFromXMLNode (CCSPlugin * plugin,
|
|
xmlNode * node,
|
|
Bool isScreen,
|
|
void * groupListPBv,
|
|
void * subgroupListPBv,
|
|
void * optionPBv)
|
|
{
|
|
char *name;
|
|
char *type;
|
|
char *readonly;
|
|
Bool isReadonly;
|
|
|
|
if (!node)
|
|
return;
|
|
|
|
name = getStringFromXPath (node->doc, node, "@name");
|
|
|
|
type = getStringFromXPath (node->doc, node, "@type");
|
|
|
|
readonly = getStringFromXPath (node->doc, node, "@read_only");
|
|
isReadonly = readonly && !strcmp (readonly, "true");
|
|
|
|
// If optionPBv is non-NULL, we still want to get the option info to write
|
|
// to .pb file, so we don't return immediately in that case.
|
|
|
|
if (!name || !strlen (name) || !type || !strlen (type) ||
|
|
(!optionPBv && isReadonly))
|
|
{
|
|
if (name)
|
|
free (name);
|
|
if (type)
|
|
free (type);
|
|
if (readonly)
|
|
free (readonly);
|
|
|
|
return;
|
|
}
|
|
|
|
if (isScreen)
|
|
{
|
|
for (unsigned i = 0; i < plugin->context->numScreens; i++)
|
|
addOptionForPlugin (plugin, name, type, isReadonly, TRUE,
|
|
plugin->context->screens[i], node,
|
|
groupListPBv, subgroupListPBv, optionPBv);
|
|
}
|
|
else
|
|
{
|
|
addOptionForPlugin (plugin, name, type, isReadonly, FALSE, 0, node,
|
|
groupListPBv, subgroupListPBv, optionPBv);
|
|
}
|
|
free (name);
|
|
free (type);
|
|
|
|
if (readonly)
|
|
free (readonly);
|
|
}
|
|
|
|
static void
|
|
initDisplayScreenFromRootNode (CCSPlugin * plugin,
|
|
xmlNode * node,
|
|
Bool isScreen,
|
|
void * pluginPBv)
|
|
{
|
|
xmlNode **nodes;
|
|
xmlNode **optNodes;
|
|
int num, i;
|
|
void *groupListPBv = NULL;
|
|
void *subgroupListPBv = NULL;
|
|
|
|
nodes = getNodesFromXPath (node->doc, node,
|
|
(isScreen ? "screen" : "display"), &num);
|
|
if (!num)
|
|
return;
|
|
|
|
#ifdef USE_PROTOBUF
|
|
ScreenMetadata *screenPB = NULL;
|
|
|
|
if (pluginPBv)
|
|
{
|
|
PluginMetadata *pluginPB = (PluginMetadata *) pluginPBv;
|
|
screenPB = (isScreen ?
|
|
pluginPB->mutable_screen () :
|
|
pluginPB->mutable_display ());
|
|
groupListPBv = screenPB->mutable_group_desc ();
|
|
subgroupListPBv = screenPB->mutable_subgroup_desc ();
|
|
}
|
|
#endif
|
|
optNodes = getNodesFromXPath
|
|
(node->doc, nodes[0],
|
|
"option | group/subgroup/option | group/option | subgroup/option",
|
|
&num);
|
|
if (num)
|
|
{
|
|
for (i = 0; i < num; i++)
|
|
{
|
|
void *optionPBv = NULL;
|
|
#ifdef USE_PROTOBUF
|
|
if (screenPB)
|
|
optionPBv = screenPB->add_option ();
|
|
#endif
|
|
addOptionFromXMLNode (plugin, optNodes[i], isScreen,
|
|
groupListPBv, subgroupListPBv, optionPBv);
|
|
}
|
|
|
|
free (optNodes);
|
|
}
|
|
free (nodes);
|
|
}
|
|
|
|
static inline void
|
|
initOptionsFromRootNode (CCSPlugin * plugin,
|
|
xmlNode * node,
|
|
void * pluginPBv)
|
|
{
|
|
// For display
|
|
initDisplayScreenFromRootNode (plugin, node, FALSE, pluginPBv);
|
|
|
|
// For screen
|
|
initDisplayScreenFromRootNode (plugin, node, TRUE, pluginPBv);
|
|
}
|
|
|
|
static void
|
|
addStringsFromPath (CCSStringList * list,
|
|
const char * path,
|
|
xmlNode * node,
|
|
void * stringListPBv)
|
|
{
|
|
xmlNode **nodes;
|
|
int num, i;
|
|
nodes = getNodesFromXPath (node->doc, node, path, &num);
|
|
|
|
if (num)
|
|
{
|
|
for (i = 0; i < num; i++)
|
|
{
|
|
char *value = stringFromNodeDef (nodes[i], "child::text()", NULL);
|
|
|
|
if (value && strlen (value))
|
|
{
|
|
*list = ccsStringListAppend (*list, value);
|
|
#ifdef USE_PROTOBUF
|
|
if (stringListPBv)
|
|
((StringList *) stringListPBv)->Add ()->assign (value);
|
|
#endif
|
|
}
|
|
if (value && !strlen (value))
|
|
free (value);
|
|
}
|
|
|
|
free (nodes);
|
|
}
|
|
}
|
|
|
|
static void
|
|
addStringExtensionFromXMLNode (CCSPlugin * plugin,
|
|
xmlNode * node,
|
|
void * extensionPBv)
|
|
{
|
|
xmlNode **nodes;
|
|
int num, j;
|
|
CCSStrExtension *extension;
|
|
char *name;
|
|
char *value;
|
|
char *isDisplay;
|
|
void * stringListPBv = NULL;
|
|
|
|
extension = (CCSStrExtension *) calloc (1, sizeof (CCSStrExtension));
|
|
if (!extension)
|
|
return;
|
|
|
|
isDisplay = getStringFromXPath (node->doc, node, "@display");
|
|
|
|
extension->isScreen = !(isDisplay && !strcmp (isDisplay, "true"));
|
|
|
|
if (isDisplay)
|
|
free (isDisplay);
|
|
|
|
extension->restriction = NULL;
|
|
|
|
extension->basePlugin = getStringFromXPath (node->doc, node, "@base_plugin");
|
|
if (!extension->basePlugin)
|
|
extension->basePlugin = strdup ("");
|
|
|
|
#ifdef USE_PROTOBUF
|
|
ExtensionMetadata * extensionPB = NULL;
|
|
if (extensionPBv)
|
|
{
|
|
extensionPB = (ExtensionMetadata *) extensionPBv;
|
|
extensionPB->set_display (!extension->isScreen);
|
|
extensionPB->set_base_plugin (extension->basePlugin);
|
|
stringListPBv = extensionPB->mutable_base_option ();
|
|
}
|
|
#endif
|
|
|
|
addStringsFromPath (&extension->baseSettings, "base_option", node,
|
|
stringListPBv);
|
|
|
|
nodes = getNodesFromXPath (node->doc, node, "restriction", &num);
|
|
if (!num)
|
|
{
|
|
free (extension);
|
|
return;
|
|
}
|
|
|
|
for (j = 0; j < num; j++)
|
|
{
|
|
value = getStringFromXPath (node->doc, nodes[j], "value/child::text()");
|
|
if (value)
|
|
{
|
|
name = stringFromNodeDefTrans (nodes[j], "name/child::text()",
|
|
NULL);
|
|
if (name)
|
|
{
|
|
ccsAddRestrictionToStringExtension (extension, name, value);
|
|
#ifdef USE_PROTOBUF
|
|
if (extensionPB)
|
|
{
|
|
OptionMetadata::StringRestriction *strRestrictionPB =
|
|
extensionPB->add_str_restriction ();
|
|
strRestrictionPB->set_value (value);
|
|
strRestrictionPB->set_name (name);
|
|
}
|
|
#endif
|
|
free (name);
|
|
}
|
|
free (value);
|
|
}
|
|
}
|
|
free (nodes);
|
|
|
|
PLUGIN_PRIV (plugin);
|
|
|
|
pPrivate->stringExtensions =
|
|
ccsStrExtensionListAppend (pPrivate->stringExtensions, extension);
|
|
}
|
|
|
|
static void
|
|
initStringExtensionsFromRootNode (CCSPlugin * plugin,
|
|
xmlNode * node,
|
|
void * pluginPBv)
|
|
{
|
|
xmlNode **nodes;
|
|
int num, i;
|
|
nodes = getNodesFromXPath (node->doc, node, "/compiz/*/extension", &num);
|
|
|
|
for (i = 0; i < num; i++)
|
|
{
|
|
void *extensionPBv = NULL;
|
|
#ifdef USE_PROTOBUF
|
|
if (pluginPBv)
|
|
{
|
|
PluginMetadata *pluginPB = (PluginMetadata *) pluginPBv;
|
|
extensionPBv = pluginPB->add_extension ();
|
|
}
|
|
#endif
|
|
addStringExtensionFromXMLNode (plugin, nodes[i], extensionPBv);
|
|
}
|
|
free (nodes);
|
|
}
|
|
|
|
static void
|
|
initRulesFromRootNode (CCSPlugin * plugin, xmlNode * node, void * pluginInfoPBv)
|
|
{
|
|
void *featureListPBv = NULL;
|
|
void *pluginAfterListPBv = NULL;
|
|
void *pluginBeforeListPBv = NULL;
|
|
void *requirePluginListPBv = NULL;
|
|
void *requireFeatureListPBv = NULL;
|
|
void *conflictPluginListPBv = NULL;
|
|
void *conflictFeatureListPBv = NULL;
|
|
#ifdef USE_PROTOBUF
|
|
if (pluginInfoPBv)
|
|
{
|
|
PluginInfoMetadata *pluginInfoPB = (PluginInfoMetadata *) pluginInfoPBv;
|
|
featureListPBv = pluginInfoPB->mutable_feature ();
|
|
|
|
DependenciesMetadata *deps = pluginInfoPB->mutable_deps ();
|
|
pluginAfterListPBv = deps->mutable_after_plugin ();
|
|
pluginBeforeListPBv = deps->mutable_before_plugin ();
|
|
requirePluginListPBv = deps->mutable_require_plugin ();
|
|
requireFeatureListPBv = deps->mutable_require_feature ();
|
|
conflictPluginListPBv = deps->mutable_conflict_plugin ();
|
|
conflictFeatureListPBv = deps->mutable_conflict_feature ();
|
|
}
|
|
#endif
|
|
|
|
addStringsFromPath (&plugin->providesFeature, "feature", node,
|
|
featureListPBv);
|
|
|
|
addStringsFromPath (&plugin->loadAfter,
|
|
"deps/relation[@type = 'after']/plugin", node,
|
|
pluginAfterListPBv);
|
|
addStringsFromPath (&plugin->loadBefore,
|
|
"deps/relation[@type = 'before']/plugin", node,
|
|
pluginBeforeListPBv);
|
|
addStringsFromPath (&plugin->requiresPlugin,
|
|
"deps/requirement/plugin", node, requirePluginListPBv);
|
|
addStringsFromPath (&plugin->requiresFeature,
|
|
"deps/requirement/feature", node, requireFeatureListPBv);
|
|
addStringsFromPath (&plugin->conflictPlugin,
|
|
"deps/conflict/plugin", node, conflictPluginListPBv);
|
|
addStringsFromPath (&plugin->conflictFeature,
|
|
"deps/conflict/feature", node, conflictFeatureListPBv);
|
|
}
|
|
|
|
#ifdef USE_PROTOBUF
|
|
static void
|
|
fillBasicInfoIntoPB (CCSPlugin *plugin, PluginInfoMetadata *pluginInfoPB)
|
|
{
|
|
if (!pluginInfoPB)
|
|
return;
|
|
|
|
pluginInfoPB->set_name (plugin->name);
|
|
pluginInfoPB->set_short_desc (plugin->shortDesc);
|
|
pluginInfoPB->set_long_desc (plugin->longDesc);
|
|
pluginInfoPB->set_category (plugin->category);
|
|
}
|
|
#endif
|
|
|
|
/* Returns TRUE on success. */
|
|
static Bool
|
|
addPluginFromXMLNode (CCSContext * context,
|
|
xmlNode * node,
|
|
char * file,
|
|
void * pluginInfoPBv)
|
|
{
|
|
char *name;
|
|
CCSPlugin *plugin;
|
|
CCSPluginPrivate *pPrivate;
|
|
|
|
if (!node)
|
|
return FALSE;
|
|
|
|
name = getStringFromXPath (node->doc, node, "@name");
|
|
|
|
if (!name || !strlen (name))
|
|
{
|
|
if (name)
|
|
free (name);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!strcmp (name, "ini") || !strcmp (name, "gconf") ||
|
|
!strcmp (name, "ccp") || !strcmp (name, "kconfig"))
|
|
{
|
|
free (name);
|
|
return FALSE;
|
|
}
|
|
|
|
if (ccsFindPlugin (context, name))
|
|
{
|
|
free (name);
|
|
return FALSE;
|
|
}
|
|
|
|
plugin = (CCSPlugin *) calloc (1, sizeof (CCSPlugin));
|
|
if (!plugin)
|
|
return FALSE;
|
|
|
|
pPrivate = (CCSPluginPrivate *) calloc (1, sizeof (CCSPluginPrivate));
|
|
if (!pPrivate)
|
|
{
|
|
free (plugin);
|
|
return FALSE;
|
|
}
|
|
|
|
plugin->ccsPrivate = (void *) pPrivate;
|
|
|
|
if (file)
|
|
pPrivate->xmlFile = strdup (file);
|
|
|
|
asprintf (&pPrivate->xmlPath, "/compiz/plugin[@name = '%s']", name);
|
|
plugin->context = context;
|
|
plugin->name = strdup (name);
|
|
|
|
if (!basicMetadata)
|
|
{
|
|
plugin->shortDesc =
|
|
stringFromNodeDefTrans (node, "short/child::text()", name);
|
|
plugin->longDesc =
|
|
stringFromNodeDefTrans (node, "long/child::text()", name);
|
|
plugin->category =
|
|
stringFromNodeDef (node, "category/child::text()", "");
|
|
}
|
|
else
|
|
{
|
|
plugin->shortDesc = strdup (name);
|
|
plugin->longDesc = strdup (name);
|
|
plugin->category = strdup ("");
|
|
}
|
|
#ifdef USE_PROTOBUF
|
|
fillBasicInfoIntoPB (plugin, (PluginInfoMetadata *) pluginInfoPBv);
|
|
#endif
|
|
|
|
initRulesFromRootNode (plugin, node, pluginInfoPBv);
|
|
|
|
context->plugins = ccsPluginListAppend (context->plugins, plugin);
|
|
free (name);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* Returns TRUE on success. */
|
|
static Bool
|
|
addCoreSettingsFromXMLNode (CCSContext * context,
|
|
xmlNode * node,
|
|
char *file,
|
|
void * pluginInfoPBv)
|
|
{
|
|
CCSPlugin *plugin;
|
|
CCSPluginPrivate *pPrivate;
|
|
|
|
if (!node)
|
|
return FALSE;
|
|
|
|
if (ccsFindPlugin (context, "core"))
|
|
return FALSE;
|
|
|
|
plugin = (CCSPlugin *) calloc (1, sizeof (CCSPlugin));
|
|
if (!plugin)
|
|
return FALSE;
|
|
|
|
pPrivate = (CCSPluginPrivate *) calloc (1, sizeof (CCSPluginPrivate));
|
|
if (!pPrivate)
|
|
{
|
|
free (plugin);
|
|
return FALSE;
|
|
}
|
|
|
|
plugin->ccsPrivate = (void *) pPrivate;
|
|
|
|
if (file)
|
|
pPrivate->xmlFile = strdup (file);
|
|
|
|
pPrivate->xmlPath = strdup ("/compiz/core");
|
|
plugin->context = context;
|
|
plugin->name = strdup ("core");
|
|
plugin->category = strdup ("General");
|
|
|
|
if (!basicMetadata)
|
|
{
|
|
plugin->shortDesc =
|
|
stringFromNodeDefTrans (node, "short/child::text()",
|
|
"General Options");
|
|
plugin->longDesc =
|
|
stringFromNodeDefTrans (node, "long/child::text()",
|
|
"General Compiz Options");
|
|
}
|
|
else
|
|
{
|
|
plugin->shortDesc = strdup ("General Options");
|
|
plugin->longDesc = strdup ("General Compiz Options");
|
|
}
|
|
#ifdef USE_PROTOBUF
|
|
fillBasicInfoIntoPB (plugin, (PluginInfoMetadata *) pluginInfoPBv);
|
|
#endif
|
|
|
|
initRulesFromRootNode (plugin, node, pluginInfoPBv);
|
|
context->plugins = ccsPluginListAppend (context->plugins, plugin);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* End of XML parsing */
|
|
|
|
#ifdef USE_PROTOBUF
|
|
|
|
// Either pluginMinMetadata or pluginMetadata should be non-NULL
|
|
static Bool
|
|
loadPluginMetadataFromProtoBuf (char *pbPath,
|
|
PluginBriefMetadata *pluginMinMetadata,
|
|
PluginMetadata *pluginMetadata)
|
|
{
|
|
Bool success = FALSE;
|
|
|
|
FILE *pbFile = fopen (pbPath, "rb");
|
|
if (pbFile)
|
|
{
|
|
google::protobuf::io::FileInputStream inputStream (fileno (pbFile));
|
|
if ((pluginMinMetadata &&
|
|
pluginMinMetadata->ParseFromZeroCopyStream (&inputStream)) ||
|
|
(pluginMetadata &&
|
|
pluginMetadata->ParseFromZeroCopyStream (&inputStream)))
|
|
success = TRUE;
|
|
inputStream.Close ();
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
// Returns TRUE if successfully loads .pb file and .pb is up to date.
|
|
static Bool
|
|
checkAndLoadProtoBuf (char *pbPath,
|
|
struct stat *pbStat,
|
|
struct stat *xmlStat,
|
|
PluginBriefMetadata *pluginBriefPB)
|
|
{
|
|
const PluginInfoMetadata &pluginInfoPB = pluginBriefPB->info ();
|
|
|
|
if (pbStat->st_mtime < xmlStat->st_mtime || // is .pb older than .xml?
|
|
!loadPluginMetadataFromProtoBuf (pbPath, pluginBriefPB, NULL) ||
|
|
(!basicMetadata && pluginBriefPB->info ().basic_metadata ()) ||
|
|
pluginInfoPB.pb_abi_version () != PB_ABI_VERSION ||
|
|
pluginInfoPB.time () != (unsigned long)xmlStat->st_mtime ||
|
|
// xml modification time mismatch?
|
|
(pluginInfoPB.locale () != "NONE" &&
|
|
pluginInfoPB.locale () != shortLocale))
|
|
{
|
|
// .pb needs update
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
// Write .pb data to .pb file
|
|
static void
|
|
writePBFile (char *pbFilePath,
|
|
PluginMetadata *pluginPB,
|
|
PluginBriefMetadata *pluginBriefPB,
|
|
struct stat *xmlStat)
|
|
{
|
|
if (!createProtoBufCacheDir ())
|
|
return;
|
|
|
|
PluginInfoMetadata *pluginInfoPB;
|
|
|
|
if (pluginPB)
|
|
{
|
|
pluginInfoPB = pluginPB->mutable_info ();
|
|
pluginInfoPB->set_brief_metadata (FALSE);
|
|
}
|
|
else
|
|
{
|
|
pluginInfoPB = pluginBriefPB->mutable_info ();
|
|
pluginInfoPB->set_pb_abi_version (PB_ABI_VERSION);
|
|
pluginInfoPB->set_locale (shortLocale);
|
|
pluginInfoPB->set_time ((unsigned long)xmlStat->st_mtime);
|
|
pluginInfoPB->set_brief_metadata (TRUE);
|
|
}
|
|
|
|
pluginInfoPB->set_basic_metadata (basicMetadata);
|
|
|
|
FILE *pbFile = fopen (pbFilePath, "wb");
|
|
if (pbFile)
|
|
{
|
|
google::protobuf::io::FileOutputStream
|
|
outputStream (fileno (pbFile));
|
|
if (pluginPB)
|
|
pluginPB->SerializeToZeroCopyStream (&outputStream);
|
|
else
|
|
pluginBriefPB->SerializeToZeroCopyStream (&outputStream);
|
|
outputStream.Close ();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* Returns TRUE on success. */
|
|
static Bool
|
|
loadPluginFromXML (CCSContext * context,
|
|
xmlDoc * doc,
|
|
char *filename,
|
|
void * pluginInfoPBv)
|
|
{
|
|
xmlNode **nodes;
|
|
int num;
|
|
Bool success = FALSE;
|
|
|
|
nodes = getNodesFromXPath (doc, NULL, "/compiz/core", &num);
|
|
if (num)
|
|
{
|
|
success = addCoreSettingsFromXMLNode (context, nodes[0], filename,
|
|
pluginInfoPBv);
|
|
free (nodes);
|
|
return success;
|
|
}
|
|
|
|
nodes = getNodesFromXPath (doc, NULL, "/compiz/plugin", &num);
|
|
if (num)
|
|
{
|
|
success = addPluginFromXMLNode (context, nodes[0], filename,
|
|
pluginInfoPBv);
|
|
free (nodes);
|
|
}
|
|
return success;
|
|
}
|
|
|
|
#ifdef USE_PROTOBUF
|
|
static void
|
|
updatePBFilePath (CCSContext * context, char *name, char *pbFilePath)
|
|
{
|
|
CCSPlugin *plugin = ccsFindPlugin (context, name);
|
|
if (plugin)
|
|
{
|
|
PLUGIN_PRIV (plugin);
|
|
|
|
if (pPrivate->pbFilePath)
|
|
free (pPrivate->pbFilePath);
|
|
pPrivate->pbFilePath = strdup (pbFilePath);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static void
|
|
loadPluginFromXMLFile (CCSContext * context, char *xmlName, char *xmlDirPath)
|
|
{
|
|
char *xmlFilePath = NULL;
|
|
char *pbFilePath = NULL;
|
|
void *pluginInfoPBv = NULL;
|
|
|
|
asprintf (&xmlFilePath, "%s/%s", xmlDirPath, xmlName);
|
|
if (!xmlFilePath)
|
|
{
|
|
fprintf (stderr, "[ERROR]: Can't allocate memory\n");
|
|
return;
|
|
}
|
|
|
|
#ifdef USE_PROTOBUF
|
|
char *name = NULL;
|
|
struct stat xmlStat;
|
|
Bool removePB = FALSE;
|
|
|
|
if (usingProtobuf)
|
|
{
|
|
if (stat (xmlFilePath, &xmlStat))
|
|
{
|
|
free (xmlFilePath);
|
|
return;
|
|
}
|
|
|
|
// Check if the corresponding .pb exists in cache
|
|
Bool error = TRUE;
|
|
struct stat pbStat;
|
|
|
|
name = strndup (xmlName, strlen (xmlName) - 4);
|
|
if (!name)
|
|
{
|
|
fprintf (stderr, "[ERROR]: Can't allocate memory\n");
|
|
free (xmlFilePath);
|
|
return;
|
|
}
|
|
|
|
if (createProtoBufCacheDir () &&
|
|
metadataCacheDir.length () > 0)
|
|
{
|
|
asprintf (&pbFilePath, "%s/%s.pb", metadataCacheDir.c_str (), name);
|
|
if (!pbFilePath)
|
|
{
|
|
fprintf (stderr, "[ERROR]: Can't allocate memory\n");
|
|
free (xmlFilePath);
|
|
free (name);
|
|
return;
|
|
}
|
|
error = stat (pbFilePath, &pbStat);
|
|
}
|
|
|
|
if (!error)
|
|
{
|
|
if (checkAndLoadProtoBuf (pbFilePath, &pbStat, &xmlStat,
|
|
&persistentPluginBriefPB))
|
|
{
|
|
// Found and loaded .pb
|
|
if (!strcmp (name, "core"))
|
|
addCoreSettingsFromPB (context,
|
|
persistentPluginBriefPB.info (),
|
|
pbFilePath, xmlFilePath);
|
|
else
|
|
addPluginFromPB (context, persistentPluginBriefPB.info (),
|
|
pbFilePath, xmlFilePath);
|
|
|
|
updatePBFilePath (context, name, pbFilePath);
|
|
|
|
free (xmlFilePath);
|
|
free (pbFilePath);
|
|
free (name);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
removePB = TRUE;
|
|
}
|
|
}
|
|
persistentPluginBriefPB.Clear ();
|
|
pluginInfoPBv = persistentPluginBriefPB.mutable_info ();
|
|
}
|
|
#endif
|
|
|
|
// Load from .xml
|
|
FILE *fp = fopen (xmlFilePath, "r");
|
|
Bool xmlLoaded = FALSE;
|
|
|
|
if (fp)
|
|
{
|
|
fclose (fp);
|
|
xmlDoc *doc = xmlReadFile (xmlFilePath, NULL, 0);
|
|
if (doc)
|
|
{
|
|
xmlLoaded = loadPluginFromXML (context, doc, xmlFilePath,
|
|
pluginInfoPBv);
|
|
xmlFreeDoc (doc);
|
|
}
|
|
}
|
|
free (xmlFilePath);
|
|
|
|
#ifdef USE_PROTOBUF
|
|
if (usingProtobuf && xmlLoaded)
|
|
{
|
|
if (removePB)
|
|
remove (pbFilePath); // Attempt to remove .pb
|
|
writePBFile (pbFilePath, NULL, &persistentPluginBriefPB, &xmlStat);
|
|
updatePBFilePath (context, name, pbFilePath);
|
|
}
|
|
|
|
if (pbFilePath)
|
|
free (pbFilePath);
|
|
if (name)
|
|
free (name);
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
loadPluginsFromXMLFiles (CCSContext * context, char *path)
|
|
{
|
|
struct dirent **nameList;
|
|
int nFile, i;
|
|
|
|
if (!path)
|
|
return;
|
|
|
|
nFile = scandir (path, &nameList, pluginXMLFilter, NULL);
|
|
|
|
if (nFile <= 0)
|
|
return;
|
|
|
|
for (i = 0; i < nFile; i++)
|
|
{
|
|
loadPluginFromXMLFile (context, nameList[i]->d_name, path);
|
|
free (nameList[i]);
|
|
}
|
|
free (nameList);
|
|
}
|
|
|
|
static void
|
|
addPluginNamed (CCSContext * context, char *name)
|
|
{
|
|
CCSPlugin *plugin;
|
|
CCSPluginPrivate *pPrivate;
|
|
|
|
if (ccsFindPlugin (context, name))
|
|
return;
|
|
|
|
if (!strcmp (name, "ini") || !strcmp (name, "gconf") ||
|
|
!strcmp (name, "ccp") || !strcmp (name, "kconfig"))
|
|
return;
|
|
|
|
plugin = (CCSPlugin *) calloc (1, sizeof (CCSPlugin));
|
|
if (!plugin)
|
|
return;
|
|
|
|
pPrivate = (CCSPluginPrivate *) calloc (1, sizeof (CCSPluginPrivate));
|
|
if (!pPrivate)
|
|
{
|
|
free (plugin);
|
|
return;
|
|
}
|
|
|
|
plugin->ccsPrivate = (void *) pPrivate;
|
|
|
|
plugin->context = context;
|
|
plugin->name = strdup (name);
|
|
|
|
if (!plugin->shortDesc)
|
|
plugin->shortDesc = strdup (name);
|
|
if (!plugin->longDesc)
|
|
plugin->longDesc = strdup (name);
|
|
if (!plugin->category)
|
|
plugin->category = strdup ("");
|
|
|
|
pPrivate->loaded = TRUE;
|
|
collateGroups (pPrivate);
|
|
context->plugins = ccsPluginListAppend (context->plugins, plugin);
|
|
}
|
|
|
|
static void
|
|
loadPluginsFromName (CCSContext * context, char *path)
|
|
{
|
|
struct dirent **nameList;
|
|
int nFile, i;
|
|
|
|
if (!path)
|
|
return;
|
|
|
|
nFile = scandir (path, &nameList, pluginNameFilter, NULL);
|
|
if (nFile <= 0)
|
|
return;
|
|
|
|
for (i = 0; i < nFile; i++)
|
|
{
|
|
char name[1024];
|
|
sscanf (nameList[i]->d_name, "lib%s", name);
|
|
if (strlen (name) > 3)
|
|
name[strlen (name) - 3] = 0;
|
|
free (nameList[i]);
|
|
addPluginNamed (context, name);
|
|
}
|
|
free (nameList);
|
|
}
|
|
|
|
#ifdef USE_PROTOBUF
|
|
static inline void
|
|
initPBLoading ()
|
|
{
|
|
// Update usingProtobuf with the COMPIZ_NO_PROTOBUF environment variable
|
|
char *compizNoProtobuf = getenv ("COMPIZ_NO_PROTOBUF");
|
|
usingProtobuf = !(compizNoProtobuf &&
|
|
(strcasecmp (compizNoProtobuf, "1") == 0 ||
|
|
strcasecmp (compizNoProtobuf, "yes") == 0 ||
|
|
strcasecmp (compizNoProtobuf, "true") == 0));
|
|
if (usingProtobuf)
|
|
{
|
|
// Verify that the version of the library that we linked against is
|
|
// compatible with the version of the headers we compiled against.
|
|
GOOGLE_PROTOBUF_VERIFY_VERSION;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
Bool
|
|
ccsLoadPlugin (CCSContext * context, char *name)
|
|
{
|
|
#ifdef USE_PROTOBUF
|
|
initPBLoading ();
|
|
#endif
|
|
|
|
char *xmlDirPath = NULL;
|
|
char *xmlName = NULL;
|
|
asprintf (&xmlName, "%s.xml", name);
|
|
|
|
if (xmlName)
|
|
{
|
|
char *home = getenv ("HOME");
|
|
if (home && strlen (home))
|
|
{
|
|
char *home = getenv ("HOME");
|
|
asprintf (&xmlDirPath, "%s/.compiz/metadata", home);
|
|
if (xmlDirPath)
|
|
{
|
|
loadPluginFromXMLFile (context, xmlName, xmlDirPath);
|
|
free (xmlDirPath);
|
|
}
|
|
}
|
|
|
|
loadPluginFromXMLFile (context, xmlName, (char *) METADATADIR);
|
|
free (xmlName);
|
|
}
|
|
|
|
return (ccsFindPlugin (context, name) != NULL);
|
|
}
|
|
|
|
void
|
|
ccsLoadPlugins (CCSContext * context)
|
|
{
|
|
D (D_FULL, "Adding plugins\n");
|
|
|
|
#ifdef USE_PROTOBUF
|
|
initPBLoading ();
|
|
#endif
|
|
|
|
char *home = getenv ("HOME");
|
|
if (home && strlen (home))
|
|
{
|
|
char *homeplugins = NULL;
|
|
asprintf (&homeplugins, "%s/.compiz/metadata", home);
|
|
if (homeplugins)
|
|
{
|
|
loadPluginsFromXMLFiles (context, homeplugins);
|
|
free (homeplugins);
|
|
}
|
|
}
|
|
loadPluginsFromXMLFiles (context, (char *)METADATADIR);
|
|
|
|
if (home && strlen (home))
|
|
{
|
|
char *homeplugins = NULL;
|
|
asprintf (&homeplugins, "%s/.compiz/plugins", home);
|
|
if (homeplugins)
|
|
{
|
|
loadPluginsFromName (context, homeplugins);
|
|
free (homeplugins);
|
|
}
|
|
}
|
|
loadPluginsFromName (context, (char *)PLUGINDIR);
|
|
}
|
|
|
|
static void
|
|
loadOptionsStringExtensionsFromXML (CCSPlugin * plugin,
|
|
void * pluginPBv,
|
|
struct stat *xmlStat)
|
|
{
|
|
PLUGIN_PRIV (plugin);
|
|
|
|
xmlDoc *doc = NULL;
|
|
xmlNode **nodes;
|
|
int num;
|
|
|
|
if (stat (pPrivate->xmlFile, xmlStat))
|
|
return;
|
|
|
|
FILE *fp = fopen (pPrivate->xmlFile, "r");
|
|
if (!fp)
|
|
return;
|
|
|
|
fclose (fp);
|
|
doc = xmlReadFile (pPrivate->xmlFile, NULL, 0);
|
|
|
|
nodes = getNodesFromXPath (doc, NULL, pPrivate->xmlPath, &num);
|
|
if (num)
|
|
{
|
|
initOptionsFromRootNode (plugin, nodes[0], pluginPBv);
|
|
if (!basicMetadata)
|
|
initStringExtensionsFromRootNode (plugin, nodes[0], pluginPBv);
|
|
free (nodes);
|
|
}
|
|
if (doc)
|
|
xmlFreeDoc (doc);
|
|
}
|
|
|
|
void
|
|
ccsLoadPluginSettings (CCSPlugin * plugin)
|
|
{
|
|
Bool ignoreXML = FALSE;
|
|
Bool loadedAtLeastBriefPB = FALSE;
|
|
void *pluginPBToWrite = NULL;
|
|
|
|
#ifdef USE_PROTOBUF
|
|
initPBLoading ();
|
|
#endif
|
|
|
|
PLUGIN_PRIV (plugin);
|
|
|
|
if (pPrivate->loaded)
|
|
return;
|
|
|
|
pPrivate->loaded = TRUE;
|
|
D (D_FULL, "Initializing %s options...", plugin->name);
|
|
|
|
#ifdef USE_PROTOBUF
|
|
if (usingProtobuf && pPrivate->pbFilePath)
|
|
{
|
|
loadedAtLeastBriefPB =
|
|
loadPluginMetadataFromProtoBuf (pPrivate->pbFilePath,
|
|
NULL, &persistentPluginPB);
|
|
if (loadedAtLeastBriefPB)
|
|
{
|
|
if (!persistentPluginPB.info ().brief_metadata () &&
|
|
(basicMetadata ||
|
|
!persistentPluginPB.info ().basic_metadata ()))
|
|
{
|
|
initOptionsFromPB (plugin, persistentPluginPB);
|
|
if (!basicMetadata)
|
|
initStringExtensionsFromPB (plugin, persistentPluginPB);
|
|
ignoreXML = TRUE;
|
|
}
|
|
else
|
|
pluginPBToWrite = &persistentPluginPB;
|
|
}
|
|
else
|
|
pluginPBToWrite = &persistentPluginPB;
|
|
}
|
|
#endif
|
|
|
|
struct stat xmlStat;
|
|
|
|
// Load from .xml
|
|
if (!ignoreXML && pPrivate->xmlFile)
|
|
loadOptionsStringExtensionsFromXML (plugin, pluginPBToWrite, &xmlStat);
|
|
|
|
#ifdef USE_PROTOBUF
|
|
if (pluginPBToWrite && pPrivate->pbFilePath && loadedAtLeastBriefPB)
|
|
writePBFile (pPrivate->pbFilePath, (PluginMetadata *) pluginPBToWrite,
|
|
NULL, &xmlStat);
|
|
#endif
|
|
D (D_FULL, "done\n");
|
|
|
|
collateGroups (pPrivate);
|
|
ccsReadPluginSettings (plugin);
|
|
}
|
|
|