mirror of
https://gitlab.freedesktop.org/mesa/mesa.git
synced 2025-12-23 11:10:10 +01:00
driconf: Restore the ability to override driconf with the environment.
I distinctly remember testing this during development, yet clearly deleted
the code.
I pulled the parseValue code back up to the top where it used to be since
we have to use it, rather than just adding a prototype.
Fixes: 8a05d6ffc6 ("driconf: Make the driver's declarations be structs instead of XML.")
Reviewed-by: Michel Dänzer <mdaenzer@redhat.com>
Reviewed-by: Marek Olšák <marek.olsak@amd.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/7012>
This commit is contained in:
parent
455bfecdff
commit
7fb4ab9ec1
1 changed files with 370 additions and 352 deletions
|
|
@ -61,262 +61,6 @@
|
||||||
#define PATH_MAX 4096
|
#define PATH_MAX 4096
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/** \brief Find an option in an option cache with the name as key */
|
|
||||||
static uint32_t
|
|
||||||
findOption(const driOptionCache *cache, const char *name)
|
|
||||||
{
|
|
||||||
uint32_t len = strlen(name);
|
|
||||||
uint32_t size = 1 << cache->tableSize, mask = size - 1;
|
|
||||||
uint32_t hash = 0;
|
|
||||||
uint32_t i, shift;
|
|
||||||
|
|
||||||
/* compute a hash from the variable length name */
|
|
||||||
for (i = 0, shift = 0; i < len; ++i, shift = (shift+8) & 31)
|
|
||||||
hash += (uint32_t)name[i] << shift;
|
|
||||||
hash *= hash;
|
|
||||||
hash = (hash >> (16-cache->tableSize/2)) & mask;
|
|
||||||
|
|
||||||
/* this is just the starting point of the linear search for the option */
|
|
||||||
for (i = 0; i < size; ++i, hash = (hash+1) & mask) {
|
|
||||||
/* if we hit an empty entry then the option is not defined (yet) */
|
|
||||||
if (cache->info[hash].name == 0)
|
|
||||||
break;
|
|
||||||
else if (!strcmp(name, cache->info[hash].name))
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
/* this assertion fails if the hash table is full */
|
|
||||||
assert (i < size);
|
|
||||||
|
|
||||||
return hash;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** \brief Like strdup with error checking. */
|
|
||||||
#define XSTRDUP(dest,source) do { \
|
|
||||||
if (!(dest = strdup(source))) { \
|
|
||||||
fprintf(stderr, "%s: %d: out of memory.\n", __FILE__, __LINE__); \
|
|
||||||
abort(); \
|
|
||||||
} \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
/** \brief Check if a value is in info->range. */
|
|
||||||
UNUSED static bool
|
|
||||||
checkValue(const driOptionValue *v, const driOptionInfo *info)
|
|
||||||
{
|
|
||||||
switch (info->type) {
|
|
||||||
case DRI_ENUM: /* enum is just a special integer */
|
|
||||||
case DRI_INT:
|
|
||||||
return (info->range.start._int == info->range.end._int ||
|
|
||||||
(v->_int >= info->range.start._int &&
|
|
||||||
v->_int <= info->range.end._int));
|
|
||||||
|
|
||||||
case DRI_FLOAT:
|
|
||||||
return (info->range.start._float == info->range.end._float ||
|
|
||||||
(v->_float >= info->range.start._float &&
|
|
||||||
v->_float <= info->range.end._float));
|
|
||||||
|
|
||||||
default:
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
driParseOptionInfo(driOptionCache *info,
|
|
||||||
const driOptionDescription *configOptions,
|
|
||||||
unsigned numOptions)
|
|
||||||
{
|
|
||||||
/* Make the hash table big enough to fit more than the maximum number of
|
|
||||||
* config options we've ever seen in a driver.
|
|
||||||
*/
|
|
||||||
info->tableSize = 6;
|
|
||||||
info->info = calloc(1 << info->tableSize, sizeof(driOptionInfo));
|
|
||||||
info->values = calloc(1 << info->tableSize, sizeof(driOptionValue));
|
|
||||||
if (info->info == NULL || info->values == NULL) {
|
|
||||||
fprintf(stderr, "%s: %d: out of memory.\n", __FILE__, __LINE__);
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
UNUSED bool in_section = false;
|
|
||||||
for (int o = 0; o < numOptions; o++) {
|
|
||||||
const driOptionDescription *opt = &configOptions[o];
|
|
||||||
|
|
||||||
if (opt->info.type == DRI_SECTION) {
|
|
||||||
in_section = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* for driconf xml generation, options must always be preceded by a
|
|
||||||
* DRI_CONF_SECTION
|
|
||||||
*/
|
|
||||||
assert(in_section);
|
|
||||||
|
|
||||||
const char *name = opt->info.name;
|
|
||||||
int i = findOption(info, name);
|
|
||||||
driOptionInfo *optinfo = &info->info[i];
|
|
||||||
driOptionValue *optval = &info->values[i];
|
|
||||||
|
|
||||||
assert(!optinfo->name); /* No duplicate options in your list. */
|
|
||||||
|
|
||||||
optinfo->type = opt->info.type;
|
|
||||||
optinfo->range = opt->info.range;
|
|
||||||
XSTRDUP(optinfo->name, name);
|
|
||||||
|
|
||||||
switch (opt->info.type) {
|
|
||||||
case DRI_BOOL:
|
|
||||||
optval->_bool = opt->value._bool;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case DRI_INT:
|
|
||||||
case DRI_ENUM:
|
|
||||||
optval->_int = opt->value._int;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case DRI_FLOAT:
|
|
||||||
optval->_float = opt->value._float;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case DRI_STRING:
|
|
||||||
XSTRDUP(optval->_string, opt->value._string);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case DRI_SECTION:
|
|
||||||
unreachable("handled above");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Built-in default values should always be valid. */
|
|
||||||
assert(checkValue(optval, optinfo));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
char *
|
|
||||||
driGetOptionsXml(const driOptionDescription *configOptions, unsigned numOptions)
|
|
||||||
{
|
|
||||||
char *str = ralloc_strdup(NULL,
|
|
||||||
"<?xml version=\"1.0\" standalone=\"yes\"?>\n" \
|
|
||||||
"<!DOCTYPE driinfo [\n" \
|
|
||||||
" <!ELEMENT driinfo (section*)>\n" \
|
|
||||||
" <!ELEMENT section (description+, option+)>\n" \
|
|
||||||
" <!ELEMENT description (enum*)>\n" \
|
|
||||||
" <!ATTLIST description lang CDATA #FIXED \"en\"\n" \
|
|
||||||
" text CDATA #REQUIRED>\n" \
|
|
||||||
" <!ELEMENT option (description+)>\n" \
|
|
||||||
" <!ATTLIST option name CDATA #REQUIRED\n" \
|
|
||||||
" type (bool|enum|int|float) #REQUIRED\n" \
|
|
||||||
" default CDATA #REQUIRED\n" \
|
|
||||||
" valid CDATA #IMPLIED>\n" \
|
|
||||||
" <!ELEMENT enum EMPTY>\n" \
|
|
||||||
" <!ATTLIST enum value CDATA #REQUIRED\n" \
|
|
||||||
" text CDATA #REQUIRED>\n" \
|
|
||||||
"]>" \
|
|
||||||
"<driinfo>\n");
|
|
||||||
|
|
||||||
bool in_section = false;
|
|
||||||
for (int o = 0; o < numOptions; o++) {
|
|
||||||
const driOptionDescription *opt = &configOptions[o];
|
|
||||||
|
|
||||||
const char *name = opt->info.name;
|
|
||||||
const char *types[] = {
|
|
||||||
[DRI_BOOL] = "bool",
|
|
||||||
[DRI_INT] = "int",
|
|
||||||
[DRI_FLOAT] = "float",
|
|
||||||
[DRI_ENUM] = "enum",
|
|
||||||
[DRI_STRING] = "string",
|
|
||||||
};
|
|
||||||
|
|
||||||
if (opt->info.type == DRI_SECTION) {
|
|
||||||
if (in_section)
|
|
||||||
ralloc_asprintf_append(&str, " </section>\n");
|
|
||||||
|
|
||||||
ralloc_asprintf_append(&str,
|
|
||||||
" <section>\n"
|
|
||||||
" <description lang=\"en\" text=\"%s\"/>\n",
|
|
||||||
opt->desc);
|
|
||||||
|
|
||||||
in_section = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
ralloc_asprintf_append(&str,
|
|
||||||
" <option name=\"%s\" type=\"%s\" default=\"",
|
|
||||||
name,
|
|
||||||
types[opt->info.type]);
|
|
||||||
|
|
||||||
switch (opt->info.type) {
|
|
||||||
case DRI_BOOL:
|
|
||||||
ralloc_asprintf_append(&str, opt->value._bool ? "true" : "false");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case DRI_INT:
|
|
||||||
case DRI_ENUM:
|
|
||||||
ralloc_asprintf_append(&str, "%d", opt->value._int);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case DRI_FLOAT:
|
|
||||||
ralloc_asprintf_append(&str, "%f", opt->value._float);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case DRI_STRING:
|
|
||||||
ralloc_asprintf_append(&str, "%s", opt->value._string);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case DRI_SECTION:
|
|
||||||
unreachable("handled above");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
ralloc_asprintf_append(&str, "\"");
|
|
||||||
|
|
||||||
|
|
||||||
switch (opt->info.type) {
|
|
||||||
case DRI_INT:
|
|
||||||
case DRI_ENUM:
|
|
||||||
if (opt->info.range.start._int < opt->info.range.end._int) {
|
|
||||||
ralloc_asprintf_append(&str, " valid=\"%d:%d\"",
|
|
||||||
opt->info.range.start._int,
|
|
||||||
opt->info.range.end._int);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case DRI_FLOAT:
|
|
||||||
if (opt->info.range.start._float < opt->info.range.end._float) {
|
|
||||||
ralloc_asprintf_append(&str, " valid=\"%f:%f\"",
|
|
||||||
opt->info.range.start._float,
|
|
||||||
opt->info.range.end._float);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
ralloc_asprintf_append(&str, ">\n"); /* end of <option> */
|
|
||||||
|
|
||||||
|
|
||||||
ralloc_asprintf_append(&str, " <description lang=\"en\" text=\"%s\"%s>\n",
|
|
||||||
opt->desc, opt->info.type != DRI_ENUM ? "/" : "");
|
|
||||||
|
|
||||||
if (opt->info.type == DRI_ENUM) {
|
|
||||||
for (int i = 0; i < ARRAY_SIZE(opt->enums) && opt->enums[i].desc; i++) {
|
|
||||||
ralloc_asprintf_append(&str, " <enum value=\"%d\" text=\"%s\"/>\n",
|
|
||||||
opt->enums[i].value, opt->enums[i].desc);
|
|
||||||
}
|
|
||||||
ralloc_asprintf_append(&str, " </description>\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
ralloc_asprintf_append(&str, " </option>\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(in_section);
|
|
||||||
ralloc_asprintf_append(&str, " </section>\n");
|
|
||||||
|
|
||||||
ralloc_asprintf_append(&str, "</driinfo>\n");
|
|
||||||
|
|
||||||
char *output = strdup(str);
|
|
||||||
ralloc_free(str);
|
|
||||||
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if WITH_XMLCONFIG
|
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
be_verbose(void)
|
be_verbose(void)
|
||||||
{
|
{
|
||||||
|
|
@ -327,102 +71,6 @@ be_verbose(void)
|
||||||
return strstr(s, "silent") == NULL;
|
return strstr(s, "silent") == NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Print message to \c stderr if the \c LIBGL_DEBUG environment variable
|
|
||||||
* is set.
|
|
||||||
*
|
|
||||||
* Is called from the drivers.
|
|
||||||
*
|
|
||||||
* \param f \c printf like format string.
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
__driUtilMessage(const char *f, ...)
|
|
||||||
{
|
|
||||||
va_list args;
|
|
||||||
const char *libgl_debug;
|
|
||||||
|
|
||||||
libgl_debug=getenv("LIBGL_DEBUG");
|
|
||||||
if (libgl_debug && !strstr(libgl_debug, "quiet")) {
|
|
||||||
fprintf(stderr, "libGL: ");
|
|
||||||
va_start(args, f);
|
|
||||||
vfprintf(stderr, f, args);
|
|
||||||
va_end(args);
|
|
||||||
fprintf(stderr, "\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** \brief Output a warning message. */
|
|
||||||
#define XML_WARNING1(msg) do { \
|
|
||||||
__driUtilMessage("Warning in %s line %d, column %d: "msg, data->name, \
|
|
||||||
(int) XML_GetCurrentLineNumber(data->parser), \
|
|
||||||
(int) XML_GetCurrentColumnNumber(data->parser)); \
|
|
||||||
} while (0)
|
|
||||||
#define XML_WARNING(msg, ...) do { \
|
|
||||||
__driUtilMessage("Warning in %s line %d, column %d: "msg, data->name, \
|
|
||||||
(int) XML_GetCurrentLineNumber(data->parser), \
|
|
||||||
(int) XML_GetCurrentColumnNumber(data->parser), \
|
|
||||||
##__VA_ARGS__); \
|
|
||||||
} while (0)
|
|
||||||
/** \brief Output an error message. */
|
|
||||||
#define XML_ERROR1(msg) do { \
|
|
||||||
__driUtilMessage("Error in %s line %d, column %d: "msg, data->name, \
|
|
||||||
(int) XML_GetCurrentLineNumber(data->parser), \
|
|
||||||
(int) XML_GetCurrentColumnNumber(data->parser)); \
|
|
||||||
} while (0)
|
|
||||||
#define XML_ERROR(msg, ...) do { \
|
|
||||||
__driUtilMessage("Error in %s line %d, column %d: "msg, data->name, \
|
|
||||||
(int) XML_GetCurrentLineNumber(data->parser), \
|
|
||||||
(int) XML_GetCurrentColumnNumber(data->parser), \
|
|
||||||
##__VA_ARGS__); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
/** \brief Parser context for configuration files. */
|
|
||||||
struct OptConfData {
|
|
||||||
const char *name;
|
|
||||||
XML_Parser parser;
|
|
||||||
driOptionCache *cache;
|
|
||||||
int screenNum;
|
|
||||||
const char *driverName, *execName;
|
|
||||||
const char *kernelDriverName;
|
|
||||||
const char *engineName;
|
|
||||||
const char *applicationName;
|
|
||||||
uint32_t engineVersion;
|
|
||||||
uint32_t applicationVersion;
|
|
||||||
uint32_t ignoringDevice;
|
|
||||||
uint32_t ignoringApp;
|
|
||||||
uint32_t inDriConf;
|
|
||||||
uint32_t inDevice;
|
|
||||||
uint32_t inApp;
|
|
||||||
uint32_t inOption;
|
|
||||||
};
|
|
||||||
|
|
||||||
/** \brief Elements in configuration files. */
|
|
||||||
enum OptConfElem {
|
|
||||||
OC_APPLICATION = 0, OC_DEVICE, OC_DRICONF, OC_ENGINE, OC_OPTION, OC_COUNT
|
|
||||||
};
|
|
||||||
static const char *OptConfElems[] = {
|
|
||||||
[OC_APPLICATION] = "application",
|
|
||||||
[OC_DEVICE] = "device",
|
|
||||||
[OC_DRICONF] = "driconf",
|
|
||||||
[OC_ENGINE] = "engine",
|
|
||||||
[OC_OPTION] = "option",
|
|
||||||
};
|
|
||||||
|
|
||||||
static int compare(const void *a, const void *b) {
|
|
||||||
return strcmp(*(char *const*)a, *(char *const*)b);
|
|
||||||
}
|
|
||||||
/** \brief Binary search in a string array. */
|
|
||||||
static uint32_t
|
|
||||||
bsearchStr(const char *name, const char *elems[], uint32_t count)
|
|
||||||
{
|
|
||||||
const char **found;
|
|
||||||
found = bsearch(&name, elems, count, sizeof(char *), compare);
|
|
||||||
if (found)
|
|
||||||
return found - elems;
|
|
||||||
else
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** \brief Locale-independent integer parser.
|
/** \brief Locale-independent integer parser.
|
||||||
*
|
*
|
||||||
* Works similar to strtol. Leading space is NOT skipped. The input
|
* Works similar to strtol. Leading space is NOT skipped. The input
|
||||||
|
|
@ -602,6 +250,376 @@ parseValue(driOptionValue *v, driOptionType type, const char *string)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** \brief Find an option in an option cache with the name as key */
|
||||||
|
static uint32_t
|
||||||
|
findOption(const driOptionCache *cache, const char *name)
|
||||||
|
{
|
||||||
|
uint32_t len = strlen(name);
|
||||||
|
uint32_t size = 1 << cache->tableSize, mask = size - 1;
|
||||||
|
uint32_t hash = 0;
|
||||||
|
uint32_t i, shift;
|
||||||
|
|
||||||
|
/* compute a hash from the variable length name */
|
||||||
|
for (i = 0, shift = 0; i < len; ++i, shift = (shift+8) & 31)
|
||||||
|
hash += (uint32_t)name[i] << shift;
|
||||||
|
hash *= hash;
|
||||||
|
hash = (hash >> (16-cache->tableSize/2)) & mask;
|
||||||
|
|
||||||
|
/* this is just the starting point of the linear search for the option */
|
||||||
|
for (i = 0; i < size; ++i, hash = (hash+1) & mask) {
|
||||||
|
/* if we hit an empty entry then the option is not defined (yet) */
|
||||||
|
if (cache->info[hash].name == 0)
|
||||||
|
break;
|
||||||
|
else if (!strcmp(name, cache->info[hash].name))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* this assertion fails if the hash table is full */
|
||||||
|
assert (i < size);
|
||||||
|
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** \brief Like strdup with error checking. */
|
||||||
|
#define XSTRDUP(dest,source) do { \
|
||||||
|
if (!(dest = strdup(source))) { \
|
||||||
|
fprintf(stderr, "%s: %d: out of memory.\n", __FILE__, __LINE__); \
|
||||||
|
abort(); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/** \brief Check if a value is in info->range. */
|
||||||
|
UNUSED static bool
|
||||||
|
checkValue(const driOptionValue *v, const driOptionInfo *info)
|
||||||
|
{
|
||||||
|
switch (info->type) {
|
||||||
|
case DRI_ENUM: /* enum is just a special integer */
|
||||||
|
case DRI_INT:
|
||||||
|
return (info->range.start._int == info->range.end._int ||
|
||||||
|
(v->_int >= info->range.start._int &&
|
||||||
|
v->_int <= info->range.end._int));
|
||||||
|
|
||||||
|
case DRI_FLOAT:
|
||||||
|
return (info->range.start._float == info->range.end._float ||
|
||||||
|
(v->_float >= info->range.start._float &&
|
||||||
|
v->_float <= info->range.end._float));
|
||||||
|
|
||||||
|
default:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
driParseOptionInfo(driOptionCache *info,
|
||||||
|
const driOptionDescription *configOptions,
|
||||||
|
unsigned numOptions)
|
||||||
|
{
|
||||||
|
/* Make the hash table big enough to fit more than the maximum number of
|
||||||
|
* config options we've ever seen in a driver.
|
||||||
|
*/
|
||||||
|
info->tableSize = 6;
|
||||||
|
info->info = calloc(1 << info->tableSize, sizeof(driOptionInfo));
|
||||||
|
info->values = calloc(1 << info->tableSize, sizeof(driOptionValue));
|
||||||
|
if (info->info == NULL || info->values == NULL) {
|
||||||
|
fprintf(stderr, "%s: %d: out of memory.\n", __FILE__, __LINE__);
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
UNUSED bool in_section = false;
|
||||||
|
for (int o = 0; o < numOptions; o++) {
|
||||||
|
const driOptionDescription *opt = &configOptions[o];
|
||||||
|
|
||||||
|
if (opt->info.type == DRI_SECTION) {
|
||||||
|
in_section = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* for driconf xml generation, options must always be preceded by a
|
||||||
|
* DRI_CONF_SECTION
|
||||||
|
*/
|
||||||
|
assert(in_section);
|
||||||
|
|
||||||
|
const char *name = opt->info.name;
|
||||||
|
int i = findOption(info, name);
|
||||||
|
driOptionInfo *optinfo = &info->info[i];
|
||||||
|
driOptionValue *optval = &info->values[i];
|
||||||
|
|
||||||
|
assert(!optinfo->name); /* No duplicate options in your list. */
|
||||||
|
|
||||||
|
optinfo->type = opt->info.type;
|
||||||
|
optinfo->range = opt->info.range;
|
||||||
|
XSTRDUP(optinfo->name, name);
|
||||||
|
|
||||||
|
switch (opt->info.type) {
|
||||||
|
case DRI_BOOL:
|
||||||
|
optval->_bool = opt->value._bool;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DRI_INT:
|
||||||
|
case DRI_ENUM:
|
||||||
|
optval->_int = opt->value._int;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DRI_FLOAT:
|
||||||
|
optval->_float = opt->value._float;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DRI_STRING:
|
||||||
|
XSTRDUP(optval->_string, opt->value._string);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DRI_SECTION:
|
||||||
|
unreachable("handled above");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Built-in default values should always be valid. */
|
||||||
|
assert(checkValue(optval, optinfo));
|
||||||
|
|
||||||
|
char *envVal = getenv(name);
|
||||||
|
if (envVal != NULL) {
|
||||||
|
driOptionValue v;
|
||||||
|
if (parseValue(&v, opt->info.type, envVal) &&
|
||||||
|
checkValue(&v, optinfo)) {
|
||||||
|
/* don't use XML_WARNING, we want the user to see this! */
|
||||||
|
if (be_verbose()) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"ATTENTION: default value of option %s overridden by environment.\n",
|
||||||
|
name);
|
||||||
|
}
|
||||||
|
*optval = v;
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "illegal environment value for %s: \"%s\". Ignoring.\n",
|
||||||
|
name, envVal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char *
|
||||||
|
driGetOptionsXml(const driOptionDescription *configOptions, unsigned numOptions)
|
||||||
|
{
|
||||||
|
char *str = ralloc_strdup(NULL,
|
||||||
|
"<?xml version=\"1.0\" standalone=\"yes\"?>\n" \
|
||||||
|
"<!DOCTYPE driinfo [\n" \
|
||||||
|
" <!ELEMENT driinfo (section*)>\n" \
|
||||||
|
" <!ELEMENT section (description+, option+)>\n" \
|
||||||
|
" <!ELEMENT description (enum*)>\n" \
|
||||||
|
" <!ATTLIST description lang CDATA #FIXED \"en\"\n" \
|
||||||
|
" text CDATA #REQUIRED>\n" \
|
||||||
|
" <!ELEMENT option (description+)>\n" \
|
||||||
|
" <!ATTLIST option name CDATA #REQUIRED\n" \
|
||||||
|
" type (bool|enum|int|float) #REQUIRED\n" \
|
||||||
|
" default CDATA #REQUIRED\n" \
|
||||||
|
" valid CDATA #IMPLIED>\n" \
|
||||||
|
" <!ELEMENT enum EMPTY>\n" \
|
||||||
|
" <!ATTLIST enum value CDATA #REQUIRED\n" \
|
||||||
|
" text CDATA #REQUIRED>\n" \
|
||||||
|
"]>" \
|
||||||
|
"<driinfo>\n");
|
||||||
|
|
||||||
|
bool in_section = false;
|
||||||
|
for (int o = 0; o < numOptions; o++) {
|
||||||
|
const driOptionDescription *opt = &configOptions[o];
|
||||||
|
|
||||||
|
const char *name = opt->info.name;
|
||||||
|
const char *types[] = {
|
||||||
|
[DRI_BOOL] = "bool",
|
||||||
|
[DRI_INT] = "int",
|
||||||
|
[DRI_FLOAT] = "float",
|
||||||
|
[DRI_ENUM] = "enum",
|
||||||
|
[DRI_STRING] = "string",
|
||||||
|
};
|
||||||
|
|
||||||
|
if (opt->info.type == DRI_SECTION) {
|
||||||
|
if (in_section)
|
||||||
|
ralloc_asprintf_append(&str, " </section>\n");
|
||||||
|
|
||||||
|
ralloc_asprintf_append(&str,
|
||||||
|
" <section>\n"
|
||||||
|
" <description lang=\"en\" text=\"%s\"/>\n",
|
||||||
|
opt->desc);
|
||||||
|
|
||||||
|
in_section = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ralloc_asprintf_append(&str,
|
||||||
|
" <option name=\"%s\" type=\"%s\" default=\"",
|
||||||
|
name,
|
||||||
|
types[opt->info.type]);
|
||||||
|
|
||||||
|
switch (opt->info.type) {
|
||||||
|
case DRI_BOOL:
|
||||||
|
ralloc_asprintf_append(&str, opt->value._bool ? "true" : "false");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DRI_INT:
|
||||||
|
case DRI_ENUM:
|
||||||
|
ralloc_asprintf_append(&str, "%d", opt->value._int);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DRI_FLOAT:
|
||||||
|
ralloc_asprintf_append(&str, "%f", opt->value._float);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DRI_STRING:
|
||||||
|
ralloc_asprintf_append(&str, "%s", opt->value._string);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DRI_SECTION:
|
||||||
|
unreachable("handled above");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ralloc_asprintf_append(&str, "\"");
|
||||||
|
|
||||||
|
|
||||||
|
switch (opt->info.type) {
|
||||||
|
case DRI_INT:
|
||||||
|
case DRI_ENUM:
|
||||||
|
if (opt->info.range.start._int < opt->info.range.end._int) {
|
||||||
|
ralloc_asprintf_append(&str, " valid=\"%d:%d\"",
|
||||||
|
opt->info.range.start._int,
|
||||||
|
opt->info.range.end._int);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DRI_FLOAT:
|
||||||
|
if (opt->info.range.start._float < opt->info.range.end._float) {
|
||||||
|
ralloc_asprintf_append(&str, " valid=\"%f:%f\"",
|
||||||
|
opt->info.range.start._float,
|
||||||
|
opt->info.range.end._float);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ralloc_asprintf_append(&str, ">\n"); /* end of <option> */
|
||||||
|
|
||||||
|
|
||||||
|
ralloc_asprintf_append(&str, " <description lang=\"en\" text=\"%s\"%s>\n",
|
||||||
|
opt->desc, opt->info.type != DRI_ENUM ? "/" : "");
|
||||||
|
|
||||||
|
if (opt->info.type == DRI_ENUM) {
|
||||||
|
for (int i = 0; i < ARRAY_SIZE(opt->enums) && opt->enums[i].desc; i++) {
|
||||||
|
ralloc_asprintf_append(&str, " <enum value=\"%d\" text=\"%s\"/>\n",
|
||||||
|
opt->enums[i].value, opt->enums[i].desc);
|
||||||
|
}
|
||||||
|
ralloc_asprintf_append(&str, " </description>\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
ralloc_asprintf_append(&str, " </option>\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(in_section);
|
||||||
|
ralloc_asprintf_append(&str, " </section>\n");
|
||||||
|
|
||||||
|
ralloc_asprintf_append(&str, "</driinfo>\n");
|
||||||
|
|
||||||
|
char *output = strdup(str);
|
||||||
|
ralloc_free(str);
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if WITH_XMLCONFIG
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print message to \c stderr if the \c LIBGL_DEBUG environment variable
|
||||||
|
* is set.
|
||||||
|
*
|
||||||
|
* Is called from the drivers.
|
||||||
|
*
|
||||||
|
* \param f \c printf like format string.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
__driUtilMessage(const char *f, ...)
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
const char *libgl_debug;
|
||||||
|
|
||||||
|
libgl_debug=getenv("LIBGL_DEBUG");
|
||||||
|
if (libgl_debug && !strstr(libgl_debug, "quiet")) {
|
||||||
|
fprintf(stderr, "libGL: ");
|
||||||
|
va_start(args, f);
|
||||||
|
vfprintf(stderr, f, args);
|
||||||
|
va_end(args);
|
||||||
|
fprintf(stderr, "\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** \brief Output a warning message. */
|
||||||
|
#define XML_WARNING1(msg) do { \
|
||||||
|
__driUtilMessage("Warning in %s line %d, column %d: "msg, data->name, \
|
||||||
|
(int) XML_GetCurrentLineNumber(data->parser), \
|
||||||
|
(int) XML_GetCurrentColumnNumber(data->parser)); \
|
||||||
|
} while (0)
|
||||||
|
#define XML_WARNING(msg, ...) do { \
|
||||||
|
__driUtilMessage("Warning in %s line %d, column %d: "msg, data->name, \
|
||||||
|
(int) XML_GetCurrentLineNumber(data->parser), \
|
||||||
|
(int) XML_GetCurrentColumnNumber(data->parser), \
|
||||||
|
##__VA_ARGS__); \
|
||||||
|
} while (0)
|
||||||
|
/** \brief Output an error message. */
|
||||||
|
#define XML_ERROR1(msg) do { \
|
||||||
|
__driUtilMessage("Error in %s line %d, column %d: "msg, data->name, \
|
||||||
|
(int) XML_GetCurrentLineNumber(data->parser), \
|
||||||
|
(int) XML_GetCurrentColumnNumber(data->parser)); \
|
||||||
|
} while (0)
|
||||||
|
#define XML_ERROR(msg, ...) do { \
|
||||||
|
__driUtilMessage("Error in %s line %d, column %d: "msg, data->name, \
|
||||||
|
(int) XML_GetCurrentLineNumber(data->parser), \
|
||||||
|
(int) XML_GetCurrentColumnNumber(data->parser), \
|
||||||
|
##__VA_ARGS__); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/** \brief Parser context for configuration files. */
|
||||||
|
struct OptConfData {
|
||||||
|
const char *name;
|
||||||
|
XML_Parser parser;
|
||||||
|
driOptionCache *cache;
|
||||||
|
int screenNum;
|
||||||
|
const char *driverName, *execName;
|
||||||
|
const char *kernelDriverName;
|
||||||
|
const char *engineName;
|
||||||
|
const char *applicationName;
|
||||||
|
uint32_t engineVersion;
|
||||||
|
uint32_t applicationVersion;
|
||||||
|
uint32_t ignoringDevice;
|
||||||
|
uint32_t ignoringApp;
|
||||||
|
uint32_t inDriConf;
|
||||||
|
uint32_t inDevice;
|
||||||
|
uint32_t inApp;
|
||||||
|
uint32_t inOption;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** \brief Elements in configuration files. */
|
||||||
|
enum OptConfElem {
|
||||||
|
OC_APPLICATION = 0, OC_DEVICE, OC_DRICONF, OC_ENGINE, OC_OPTION, OC_COUNT
|
||||||
|
};
|
||||||
|
static const char *OptConfElems[] = {
|
||||||
|
[OC_APPLICATION] = "application",
|
||||||
|
[OC_DEVICE] = "device",
|
||||||
|
[OC_DRICONF] = "driconf",
|
||||||
|
[OC_ENGINE] = "engine",
|
||||||
|
[OC_OPTION] = "option",
|
||||||
|
};
|
||||||
|
|
||||||
|
static int compare(const void *a, const void *b) {
|
||||||
|
return strcmp(*(char *const*)a, *(char *const*)b);
|
||||||
|
}
|
||||||
|
/** \brief Binary search in a string array. */
|
||||||
|
static uint32_t
|
||||||
|
bsearchStr(const char *name, const char *elems[], uint32_t count)
|
||||||
|
{
|
||||||
|
const char **found;
|
||||||
|
found = bsearch(&name, elems, count, sizeof(char *), compare);
|
||||||
|
if (found)
|
||||||
|
return found - elems;
|
||||||
|
else
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
/** \brief Parse a list of ranges of type info->type. */
|
/** \brief Parse a list of ranges of type info->type. */
|
||||||
static unsigned char
|
static unsigned char
|
||||||
parseRange(driOptionInfo *info, const char *string)
|
parseRange(driOptionInfo *info, const char *string)
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue