/*
* Copyright 2002-2003 Red Hat Inc., Durham, North Carolina.
*
* All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation on the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NON-INFRINGEMENT. IN NO EVENT SHALL RED HAT AND/OR THEIR SUPPLIERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/*
* Authors:
* Rickard E. (Rik) Faith <[email protected]>
*
*/
/** \file
* Provides interface for reading DMX configuration files and for
* combining that information with command-line configuration parameters. */
#ifdef HAVE_DMX_CONFIG_H
#include <dmx-config.h>
#endif
#include "dmx.h"
#include "dmxinput.h"
#include "dmxconfig.h"
#include "dmxparse.h"
#include "dmxlog.h"
#include "dmxcb.h"
#include "dmxstat.h"
#include "parser.h"
extern int yyparse(void);
extern FILE *yyin;
static char *dmxXkbRules;
static char *dmxXkbModel;
static char *dmxXkbLayout;
static char *dmxXkbVariant;
static char *dmxXkbOptions;
/** Stores lists of configuration information. */
typedef struct DMXConfigListStruct {
const char *name;
struct DMXConfigListStruct *next;
} DMXConfigList, *DMXConfigListPtr;
/** This stucture stores the parsed configuration information. */
typedef struct DMXConfigCmdStruct {
const char *filename;
const char *config;
DMXConfigList *displays;
DMXConfigList *inputs;
DMXConfigList *xinputs;
} DMXConfigCmd, *DMXConfigCmdPtr;
DMXConfigEntryPtr dmxConfigEntry;
static DMXConfigCmd dmxConfigCmd;
static int dmxDisplaysFromCommandLine;
/** Make a note that \a display is the name of an X11 display that
* should be initialized as a backend (output) display. Called from
* #ddxProcessArgument. */
void dmxConfigStoreDisplay(const char *display)
{
DMXConfigListPtr entry = malloc(sizeof(*entry));
entry->name = strdup(display);
entry->next = NULL;
if (!dmxConfigCmd.displays) dmxConfigCmd.displays = entry;
else {
DMXConfigList *pt;
for (pt = dmxConfigCmd.displays; pt->next; pt = pt->next);
if (!pt)
dmxLog(dmxFatal, "dmxConfigStoreDisplay: end of list non-NULL\n");
pt->next = entry;
}
++dmxDisplaysFromCommandLine;
}
/** Make a note that \a input is the name of an X11 display that should
* be used for input (either a backend or a console input device). */
void dmxConfigStoreInput(const char *input)
{
DMXConfigListPtr entry = malloc(sizeof(*entry));
entry->name = strdup(input);
entry->next = NULL;
if (!dmxConfigCmd.inputs) dmxConfigCmd.inputs = entry;
else {
DMXConfigList *pt;
for (pt = dmxConfigCmd.inputs; pt->next; pt = pt->next);
if (!pt)
dmxLog(dmxFatal, "dmxConfigStoreInput: end of list non-NULL\n");
pt->next = entry;
}
}
/** Make a note that \a input is the name of an X11 display that should
* be used for input from XInput extension devices. */
void dmxConfigStoreXInput(const char *input)
{
DMXConfigListPtr entry = malloc(sizeof(*entry));
entry->name = strdup(input);
entry->next = NULL;
if (!dmxConfigCmd.xinputs) dmxConfigCmd.xinputs = entry;
else {
DMXConfigList *pt;
for (pt = dmxConfigCmd.xinputs; pt->next; pt = pt->next);
if (!pt)
dmxLog(dmxFatal, "dmxConfigStoreXInput: end of list non-NULL\n");
pt->next = entry;
}
}
/** Make a note that \a file is the configuration file. */
void dmxConfigStoreFile(const char *file)
{
if (dmxConfigCmd.filename)
dmxLog(dmxFatal, "Only one -configfile allowed\n");
dmxConfigCmd.filename = strdup(file);
}
/** Make a note that \a config should be used as the configuration for
* current instantiation of the DMX server. */
void dmxConfigStoreConfig(const char *config)
{
if (dmxConfigCmd.config) dmxLog(dmxFatal, "Only one -config allowed\n");
dmxConfigCmd.config = strdup(config);
}
static int dmxConfigReadFile(const char *filename, int debug)
{
FILE *str;
if (!(str = fopen(filename, "r"))) return -1;
dmxLog(dmxInfo, "Reading configuration file \"%s\"\n", filename);
yyin = str;
yydebug = debug;
yyparse();
fclose(str);
return 0;
}
static const char *dmxConfigMatch(const char *target, DMXConfigEntryPtr entry)
{
DMXConfigVirtualPtr v = entry->virtual;
const char *name = NULL;
if (v && v->name) name = v->name;
if (v && !dmxConfigCmd.config) return v->name ? v->name : "<noname>";
if (!name) return NULL;
if (!strcmp(name, target)) return name;
return NULL;
}
static DMXScreenInfo *dmxConfigAddDisplay(const char *name,
int scrnWidth, int scrnHeight,
int scrnX, int scrnY,
int scrnXSign, int scrnYSign,
int rootWidth, int rootHeight,
int rootX, int rootY,
int rootXSign, int rootYSign)
{
DMXScreenInfo *dmxScreen;
if (!(dmxScreens = realloc(dmxScreens,
(dmxNumScreens+1) * sizeof(*dmxScreens))))
dmxLog(dmxFatal,
"dmxConfigAddDisplay: realloc failed for screen %d (%s)\n",
dmxNumScreens, name);
dmxScreen = &dmxScreens[dmxNumScreens];
memset(dmxScreen, 0, sizeof(*dmxScreen));
dmxScreen->name = name;
dmxScreen->index = dmxNumScreens;
dmxScreen->scrnWidth = scrnWidth;
dmxScreen->scrnHeight = scrnHeight;
dmxScreen->scrnX = scrnX;
dmxScreen->scrnY = scrnY;
dmxScreen->scrnXSign = scrnXSign;
dmxScreen->scrnYSign = scrnYSign;
dmxScreen->rootWidth = rootWidth;
dmxScreen->rootHeight = rootHeight;
dmxScreen->rootX = rootX;
dmxScreen->rootY = rootY;
dmxScreen->stat = dmxStatAlloc();
++dmxNumScreens;
return dmxScreen;
}
DMXInputInfo *dmxConfigAddInput(const char *name, int core)
{
DMXInputInfo *dmxInput;
if (!(dmxInputs = realloc(dmxInputs,
(dmxNumInputs+1) * sizeof(*dmxInputs))))
dmxLog(dmxFatal,
"dmxConfigAddInput: realloc failed for input %d (%s)\n",
dmxNumInputs, name);
dmxInput = &dmxInputs[dmxNumInputs];
memset(dmxInput, 0, sizeof(*dmxInput));
dmxInput->name = name;
dmxInput->inputIdx = dmxNumInputs;
dmxInput->scrnIdx = -1;
dmxInput->core = core;
++dmxNumInputs;
return dmxInput;
}
static void dmxConfigCopyFromDisplay(DMXConfigDisplayPtr d)
{
DMXScreenInfo *dmxScreen;
dmxScreen = dmxConfigAddDisplay(d->name,
d->scrnWidth, d->scrnHeight,
d->scrnX, d->scrnY,
d->scrnXSign, d->scrnYSign,
d->rootWidth, d->rootHeight,
d->rootX, d->rootY,
d->rootXSign, d->rootXSign);
dmxScreen->where = PosAbsolute;
dmxScreen->whereX = d->rootXOrigin;
dmxScreen->whereY = d->rootYOrigin;
}
static void dmxConfigCopyFromWall(DMXConfigWallPtr w)
{
DMXConfigStringPtr pt;
DMXScreenInfo *dmxScreen;
int edge = dmxNumScreens;
int last = dmxNumScreens;
if (!w->xwall && !w->ywall) { /* Try to make it square */
int count;
for (pt = w->nameList, count = 0; pt; pt = pt->next) ++count;
w->xwall = sqrt(count) + .5;
}
for (pt = w->nameList; pt; pt = pt->next) {
dmxScreen = dmxConfigAddDisplay(pt->string, w->width, w->height,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
if (pt == w->nameList) { /* Upper left */
dmxScreen->where = PosAbsolute;
dmxScreen->whereX = 0;
dmxScreen->whereY = 0;
} else if (w->xwall) { /* Tile left to right, then top to bottom */
if (!((dmxNumScreens-1) % w->xwall)) {
dmxScreen->where = PosBelow;
dmxScreen->whereRefScreen = edge;
edge = dmxNumScreens-1;
} else {
dmxScreen->where = PosRightOf;
dmxScreen->whereRefScreen = last;
}
} else { /* Tile top to bottom, then left to right */
if (!((dmxNumScreens-1) % w->ywall)) {
dmxScreen->where = PosRightOf;
dmxScreen->whereRefScreen = edge;
edge = dmxNumScreens-1;
} else {
dmxScreen->where = PosBelow;
dmxScreen->whereRefScreen = last;
}
}
last = dmxNumScreens-1;
if (dmxScreen->where == PosAbsolute)
dmxLog(dmxInfo, "Added %s at %d %d\n",
pt->string, dmxScreen->whereX, dmxScreen->whereY);
else
dmxLog(dmxInfo, "Added %s %s %s\n",
pt->string,
dmxScreen->where == PosBelow ? "below" : "right of",
dmxScreens[dmxScreen->whereRefScreen].name);
}
}
static void dmxConfigCopyFromOption(DMXConfigOptionPtr o)
{
DMXConfigStringPtr pt;
int argc = 0;
char **argv = NULL;
if (serverGeneration != 1) return; /* FIXME: only do once, for now */
if (!o || !o->string) return;
for (pt = o->option; pt; pt = pt->next) {
if (pt->string) {
++argc;
argv = realloc(argv, (argc+1) * sizeof(*argv));
argv[argc] = (char *)pt->string;
}
}
argv[0] = NULL;
ProcessCommandLine(argc+1, argv);
free(argv);
}
static void dmxConfigCopyFromParam(DMXConfigParamPtr p)
{
const char **argv;
int argc;
if ((argv = dmxConfigLookupParam(p, "xkbrules", &argc)) && argc == 2) {
dmxConfigSetXkbRules(argv[1]);
} else if ((argv = dmxConfigLookupParam(p, "xkbmodel", &argc))
&& argc == 2) {
dmxConfigSetXkbModel(argv[1]);
} else if ((argv = dmxConfigLookupParam(p, "xkblayout", &argc))
&& argc == 2) {
dmxConfigSetXkbLayout(argv[1]);
} else if ((argv = dmxConfigLookupParam(p, "xkbvariant", &argc))
&& argc == 2) {
dmxConfigSetXkbVariant(argv[1]);
} else if ((argv = dmxConfigLookupParam(p, "xkboptions", &argc))
&& argc == 2) {
dmxConfigSetXkbOptions(argv[1]);
}
}
static void dmxConfigCopyData(DMXConfigVirtualPtr v)
{
DMXConfigSubPtr sub;
if (v->dim) dmxSetWidthHeight(v->dim->x, v->dim->y);
else dmxSetWidthHeight(0, 0);
for (sub = v->subentry; sub; sub = sub->next) {
switch (sub->type) {
case dmxConfigDisplay: dmxConfigCopyFromDisplay(sub->display); break;
case dmxConfigWall: dmxConfigCopyFromWall(sub->wall); break;
case dmxConfigOption: dmxConfigCopyFromOption(sub->option); break;
case dmxConfigParam: dmxConfigCopyFromParam(sub->param); break;
default:
dmxLog(dmxFatal,
"dmxConfigCopyData: not a display, wall, or value\n");
}
}
}
static void dmxConfigFromCommandLine(void)
{
DMXConfigListPtr pt;
dmxLog(dmxInfo, "Using configuration from command line\n");
for (pt = dmxConfigCmd.displays; pt; pt = pt->next) {
DMXScreenInfo *dmxScreen = dmxConfigAddDisplay(pt->name,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0);
if (dmxNumScreens == 1) {
dmxScreen->where = PosAbsolute;
dmxScreen->whereX = 0;
dmxScreen->whereY = 0;
dmxLog(dmxInfo, "Added %s at %d %d\n",
dmxScreen->name, dmxScreen->whereX, dmxScreen->whereY);
} else {
dmxScreen->where = PosRightOf;
dmxScreen->whereRefScreen = dmxNumScreens - 2;
if (dmxScreen->whereRefScreen < 0) dmxScreen->whereRefScreen = 0;
dmxLog(dmxInfo, "Added %s %s %s\n",
dmxScreen->name,
dmxScreen->where == PosBelow ? "below" : "right of",
dmxScreens[dmxScreen->whereRefScreen].name);
}
}
}
static void dmxConfigFromConfigFile(void)
{
DMXConfigEntryPtr pt;
const char *name;
for (pt = dmxConfigEntry; pt; pt = pt->next) {
/* FIXME -- if an input is specified, use it */
if (pt->type != dmxConfigVirtual) continue;
if ((name = dmxConfigMatch(dmxConfigCmd.config, pt))) {
dmxLog(dmxInfo, "Using configuration \"%s\"\n", name);
dmxConfigCopyData(pt->virtual);
return;
}
}
dmxLog(dmxFatal, "Could not find configuration \"%s\" in \"%s\"\n",
dmxConfigCmd.config, dmxConfigCmd.filename);
}
static void dmxConfigConfigInputs(void)
{
DMXConfigListPtr pt;
if (dmxNumInputs) return;
if (dmxConfigCmd.inputs) { /* Use command line */
for (pt = dmxConfigCmd.inputs; pt; pt = pt->next)
dmxConfigAddInput(pt->name, TRUE);
} else if (dmxNumScreens) { /* Use first display */
dmxConfigAddInput(dmxScreens[0].name, TRUE);
} else { /* Use dummy */
dmxConfigAddInput("dummy", TRUE);
}
if (dmxConfigCmd.xinputs) { /* Non-core devices from command line */
for (pt = dmxConfigCmd.xinputs; pt; pt = pt->next)
dmxConfigAddInput(pt->name, FALSE);
}
}
/** Set up the appropriate global variables so that the DMX server will
* be initialized using the configuration specified in the config file
* and on the command line. */
void dmxConfigConfigure(void)
{
if (dmxConfigEntry) {
dmxConfigFreeEntry(dmxConfigEntry);
dmxConfigEntry = NULL;
}
if (dmxConfigCmd.filename) {
if (dmxConfigCmd.displays)
dmxLog(dmxWarning,
"Using configuration file \"%s\" instead of command line\n",
dmxConfigCmd.filename);
dmxConfigReadFile(dmxConfigCmd.filename, 0);
dmxConfigFromConfigFile();
} else {
if (dmxConfigCmd.config)
dmxLog(dmxWarning,
"Configuration name (%s) without configuration file\n",
dmxConfigCmd.config);
dmxConfigFromCommandLine();
}
dmxConfigConfigInputs();
}
/** This function determines the number of displays we WILL have and
* sets MAXSCREENS to that value. This is difficult since the number
* depends on the command line (which is easy to count) or on the config
* file, which has to be parsed. */
void dmxConfigSetMaxScreens(void)
{
static int processing = 0;
if (processing) return; /* Prevent reentry via ProcessCommandLine */
processing = 1;
if (dmxConfigCmd.filename) {
if (!dmxNumScreens)
dmxConfigConfigure();
#ifndef MAXSCREENS
SetMaxScreens(dmxNumScreens);
#endif
} else
#ifndef MAXSCREENS
SetMaxScreens(dmxDisplaysFromCommandLine);
#endif
processing = 0;
}
/** This macro is used to generate the following access methods:
* - dmxConfig{Set,Get}rules
* - dmxConfig{Set,Get}model
* - dmxConfig{Set,Get}layout
* - dmxConfig{Set,Get}variant
* - dmxConfig{Set,Get}options
* These methods are used to read and write information about the keyboard. */
#define GEN(param,glob,def) \
void dmxConfigSet##glob(const char *param) { \
if (dmx##glob) free((void *)dmx##glob); \
dmx##glob = strdup(param); \
} \
char *dmxConfigGet##glob(void) { \
return (char *)(dmx##glob ? dmx##glob : def); \
}
GEN(rules, XkbRules, DMX_DEFAULT_XKB_RULES)
GEN(model, XkbModel, DMX_DEFAULT_XKB_MODEL)
GEN(layout, XkbLayout, DMX_DEFAULT_XKB_LAYOUT)
GEN(variant, XkbVariant, DMX_DEFAULT_XKB_VARIANT)
GEN(options, XkbOptions, DMX_DEFAULT_XKB_OPTIONS)
|