dmxconfig.c revision 706f2543
1/*
2 * Copyright 2002-2003 Red Hat Inc., Durham, North Carolina.
3 *
4 * All Rights Reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining
7 * a copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation on the rights to use, copy, modify, merge,
10 * publish, distribute, sublicense, and/or sell copies of the Software,
11 * and to permit persons to whom the Software is furnished to do so,
12 * subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice (including the
15 * next paragraph) shall be included in all copies or substantial
16 * portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 * NON-INFRINGEMENT.  IN NO EVENT SHALL RED HAT AND/OR THEIR SUPPLIERS
22 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
23 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
24 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25 * SOFTWARE.
26 */
27
28/*
29 * Authors:
30 *   Rickard E. (Rik) Faith <faith@redhat.com>
31 *
32 */
33
34/** \file
35 * Provides interface for reading DMX configuration files and for
36 * combining that information with command-line configuration parameters. */
37
38
39#ifdef HAVE_DMX_CONFIG_H
40#include <dmx-config.h>
41#endif
42
43#include "dmx.h"
44#include "dmxinput.h"
45#include "dmxconfig.h"
46#include "dmxparse.h"
47#include "dmxlog.h"
48#include "dmxcb.h"
49#include "dmxstat.h"
50#include "parser.h"
51
52extern int  yyparse(void);
53extern FILE *yyin;
54
55static char *dmxXkbRules;
56static char *dmxXkbModel;
57static char *dmxXkbLayout;
58static char *dmxXkbVariant;
59static char *dmxXkbOptions;
60
61/** Stores lists of configuration information. */
62typedef struct DMXConfigListStruct {
63    const char                 *name;
64    struct DMXConfigListStruct *next;
65} DMXConfigList, *DMXConfigListPtr;
66
67/** This stucture stores the parsed configuration information. */
68typedef struct DMXConfigCmdStruct {
69    const char    *filename;
70    const char    *config;
71    DMXConfigList *displays;
72    DMXConfigList *inputs;
73    DMXConfigList *xinputs;
74} DMXConfigCmd, *DMXConfigCmdPtr;
75
76DMXConfigEntryPtr    dmxConfigEntry;
77static DMXConfigCmd  dmxConfigCmd;
78
79static int dmxDisplaysFromCommandLine;
80
81/** Make a note that \a display is the name of an X11 display that
82 * should be initialized as a backend (output) display.  Called from
83 * #ddxProcessArgument. */
84void dmxConfigStoreDisplay(const char *display)
85{
86    DMXConfigListPtr entry = malloc(sizeof(*entry));
87    entry->name = strdup(display);
88    entry->next = NULL;
89    if (!dmxConfigCmd.displays) dmxConfigCmd.displays = entry;
90    else {
91        DMXConfigList *pt;
92        for (pt = dmxConfigCmd.displays; pt->next; pt = pt->next);
93        if (!pt)
94            dmxLog(dmxFatal, "dmxConfigStoreDisplay: end of list non-NULL\n");
95        pt->next = entry;
96    }
97    ++dmxDisplaysFromCommandLine;
98}
99
100/** Make a note that \a input is the name of an X11 display that should
101 * be used for input (either a backend or a console input device). */
102void dmxConfigStoreInput(const char *input)
103{
104    DMXConfigListPtr entry = malloc(sizeof(*entry));
105    entry->name = strdup(input);
106    entry->next = NULL;
107    if (!dmxConfigCmd.inputs) dmxConfigCmd.inputs = entry;
108    else {
109        DMXConfigList *pt;
110        for (pt = dmxConfigCmd.inputs; pt->next; pt = pt->next);
111        if (!pt)
112            dmxLog(dmxFatal, "dmxConfigStoreInput: end of list non-NULL\n");
113        pt->next = entry;
114    }
115}
116
117/** Make a note that \a input is the name of an X11 display that should
118 * be used for input from XInput extension devices. */
119void dmxConfigStoreXInput(const char *input)
120{
121    DMXConfigListPtr entry = malloc(sizeof(*entry));
122    entry->name = strdup(input);
123    entry->next = NULL;
124    if (!dmxConfigCmd.xinputs) dmxConfigCmd.xinputs = entry;
125    else {
126        DMXConfigList *pt;
127        for (pt = dmxConfigCmd.xinputs; pt->next; pt = pt->next);
128        if (!pt)
129            dmxLog(dmxFatal, "dmxConfigStoreXInput: end of list non-NULL\n");
130        pt->next = entry;
131    }
132}
133
134/** Make a note that \a file is the configuration file. */
135void dmxConfigStoreFile(const char *file)
136{
137    if (dmxConfigCmd.filename)
138        dmxLog(dmxFatal, "Only one -configfile allowed\n");
139    dmxConfigCmd.filename = strdup(file);
140}
141
142/** Make a note that \a config should be used as the configuration for
143 * current instantiation of the DMX server. */
144void dmxConfigStoreConfig(const char *config)
145{
146    if (dmxConfigCmd.config) dmxLog(dmxFatal, "Only one -config allowed\n");
147    dmxConfigCmd.config = strdup(config);
148}
149
150static int dmxConfigReadFile(const char *filename, int debug)
151{
152    FILE *str;
153
154    if (!(str = fopen(filename, "r"))) return -1;
155    dmxLog(dmxInfo, "Reading configuration file \"%s\"\n", filename);
156    yyin    = str;
157    yydebug = debug;
158    yyparse();
159    fclose(str);
160    return 0;
161}
162
163static const char *dmxConfigMatch(const char *target, DMXConfigEntryPtr entry)
164{
165    DMXConfigVirtualPtr v     = entry->virtual;
166    const char          *name = NULL;
167
168    if (v && v->name) name = v->name;
169
170    if (v && !dmxConfigCmd.config) return v->name ? v->name : "<noname>";
171    if (!name)                     return NULL;
172    if (!strcmp(name, target))     return name;
173    return NULL;
174}
175
176static DMXScreenInfo *dmxConfigAddDisplay(const char *name,
177                                          int scrnWidth,   int scrnHeight,
178                                          int scrnX,       int scrnY,
179                                          int scrnXSign,   int scrnYSign,
180                                          int rootWidth,   int rootHeight,
181                                          int rootX,       int rootY,
182                                          int rootXSign,   int rootYSign)
183{
184    DMXScreenInfo *dmxScreen;
185
186    if (!(dmxScreens = realloc(dmxScreens,
187                               (dmxNumScreens+1) * sizeof(*dmxScreens))))
188        dmxLog(dmxFatal,
189               "dmxConfigAddDisplay: realloc failed for screen %d (%s)\n",
190               dmxNumScreens, name);
191
192    dmxScreen = &dmxScreens[dmxNumScreens];
193    memset(dmxScreen, 0, sizeof(*dmxScreen));
194    dmxScreen->name       = name;
195    dmxScreen->index      = dmxNumScreens;
196    dmxScreen->scrnWidth  = scrnWidth;
197    dmxScreen->scrnHeight = scrnHeight;
198    dmxScreen->scrnX      = scrnX;
199    dmxScreen->scrnY      = scrnY;
200    dmxScreen->scrnXSign  = scrnXSign;
201    dmxScreen->scrnYSign  = scrnYSign;
202    dmxScreen->rootWidth  = rootWidth;
203    dmxScreen->rootHeight = rootHeight;
204    dmxScreen->rootX      = rootX;
205    dmxScreen->rootY      = rootY;
206    dmxScreen->stat       = dmxStatAlloc();
207    ++dmxNumScreens;
208    return dmxScreen;
209}
210
211DMXInputInfo *dmxConfigAddInput(const char *name, int core)
212{
213    DMXInputInfo *dmxInput;
214
215    if (!(dmxInputs = realloc(dmxInputs,
216                              (dmxNumInputs+1) * sizeof(*dmxInputs))))
217        dmxLog(dmxFatal,
218               "dmxConfigAddInput: realloc failed for input %d (%s)\n",
219               dmxNumInputs, name);
220
221    dmxInput = &dmxInputs[dmxNumInputs];
222
223    memset(dmxInput, 0, sizeof(*dmxInput));
224    dmxInput->name     = name;
225    dmxInput->inputIdx = dmxNumInputs;
226    dmxInput->scrnIdx  = -1;
227    dmxInput->core     = core;
228    ++dmxNumInputs;
229    return dmxInput;
230}
231
232static void dmxConfigCopyFromDisplay(DMXConfigDisplayPtr d)
233{
234    DMXScreenInfo *dmxScreen;
235
236    dmxScreen         = dmxConfigAddDisplay(d->name,
237                                            d->scrnWidth, d->scrnHeight,
238                                            d->scrnX,     d->scrnY,
239                                            d->scrnXSign, d->scrnYSign,
240                                            d->rootWidth, d->rootHeight,
241                                            d->rootX,     d->rootY,
242                                            d->rootXSign, d->rootXSign);
243    dmxScreen->where  = PosAbsolute;
244    dmxScreen->whereX = d->rootXOrigin;
245    dmxScreen->whereY = d->rootYOrigin;
246}
247
248static void dmxConfigCopyFromWall(DMXConfigWallPtr w)
249{
250    DMXConfigStringPtr pt;
251    DMXScreenInfo      *dmxScreen;
252    int                edge = dmxNumScreens;
253    int                last = dmxNumScreens;
254
255    if (!w->xwall && !w->ywall) { /* Try to make it square */
256        int count;
257        for (pt = w->nameList, count = 0; pt; pt = pt->next) ++count;
258        w->xwall = sqrt(count) + .5;
259    }
260
261    for (pt = w->nameList; pt; pt = pt->next) {
262        dmxScreen = dmxConfigAddDisplay(pt->string, w->width, w->height,
263                                        0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
264        if (pt == w->nameList) { /* Upper left */
265            dmxScreen->where  = PosAbsolute;
266            dmxScreen->whereX = 0;
267            dmxScreen->whereY = 0;
268        } else if (w->xwall) {  /* Tile left to right, then top to bottom */
269            if (!((dmxNumScreens-1) % w->xwall)) {
270                dmxScreen->where          = PosBelow;
271                dmxScreen->whereRefScreen = edge;
272                edge                      = dmxNumScreens-1;
273            } else {
274                dmxScreen->where          = PosRightOf;
275                dmxScreen->whereRefScreen = last;
276            }
277        } else {                /* Tile top to bottom, then left to right */
278            if (!((dmxNumScreens-1) % w->ywall)) {
279                dmxScreen->where          = PosRightOf;
280                dmxScreen->whereRefScreen = edge;
281                edge                      = dmxNumScreens-1;
282            } else {
283                dmxScreen->where          = PosBelow;
284                dmxScreen->whereRefScreen = last;
285            }
286
287        }
288        last = dmxNumScreens-1;
289        if (dmxScreen->where == PosAbsolute)
290            dmxLog(dmxInfo, "Added %s at %d %d\n",
291                   pt->string, dmxScreen->whereX, dmxScreen->whereY);
292        else
293            dmxLog(dmxInfo, "Added %s %s %s\n",
294                   pt->string,
295                   dmxScreen->where == PosBelow ? "below" : "right of",
296                   dmxScreens[dmxScreen->whereRefScreen].name);
297    }
298}
299
300static void dmxConfigCopyFromOption(DMXConfigOptionPtr o)
301{
302    DMXConfigStringPtr pt;
303    int                argc   = 0;
304    char               **argv = NULL;
305
306    if (serverGeneration != 1) return; /* FIXME: only do once, for now */
307    if (!o || !o->string) return;
308    for (pt = o->option; pt; pt = pt->next) {
309        if (pt->string) {
310            ++argc;
311            argv = realloc(argv, (argc+1) * sizeof(*argv));
312            argv[argc] = (char *)pt->string;
313        }
314    }
315    argv[0] = NULL;
316    ProcessCommandLine(argc+1, argv);
317    free(argv);
318}
319
320static void dmxConfigCopyFromParam(DMXConfigParamPtr p)
321{
322    const char **argv;
323    int        argc;
324
325    if ((argv = dmxConfigLookupParam(p, "xkbrules", &argc)) && argc == 2) {
326        dmxConfigSetXkbRules(argv[1]);
327    } else if ((argv = dmxConfigLookupParam(p, "xkbmodel", &argc))
328               && argc == 2) {
329        dmxConfigSetXkbModel(argv[1]);
330    } else if ((argv = dmxConfigLookupParam(p, "xkblayout", &argc))
331               && argc == 2) {
332        dmxConfigSetXkbLayout(argv[1]);
333    } else if ((argv = dmxConfigLookupParam(p, "xkbvariant", &argc))
334               && argc == 2) {
335        dmxConfigSetXkbVariant(argv[1]);
336    } else if ((argv = dmxConfigLookupParam(p, "xkboptions", &argc))
337               && argc == 2) {
338        dmxConfigSetXkbOptions(argv[1]);
339    }
340}
341
342static void dmxConfigCopyData(DMXConfigVirtualPtr v)
343{
344    DMXConfigSubPtr sub;
345
346    if (v->dim) dmxSetWidthHeight(v->dim->x, v->dim->y);
347    else        dmxSetWidthHeight(0, 0);
348    for (sub = v->subentry; sub; sub = sub->next) {
349        switch (sub->type) {
350        case dmxConfigDisplay: dmxConfigCopyFromDisplay(sub->display); break;
351        case dmxConfigWall:    dmxConfigCopyFromWall(sub->wall);       break;
352        case dmxConfigOption:  dmxConfigCopyFromOption(sub->option);   break;
353        case dmxConfigParam:   dmxConfigCopyFromParam(sub->param);     break;
354        default:
355            dmxLog(dmxFatal,
356                   "dmxConfigCopyData: not a display, wall, or value\n");
357        }
358    }
359}
360
361static void dmxConfigFromCommandLine(void)
362{
363    DMXConfigListPtr pt;
364
365    dmxLog(dmxInfo, "Using configuration from command line\n");
366    for (pt = dmxConfigCmd.displays; pt; pt = pt->next) {
367        DMXScreenInfo *dmxScreen = dmxConfigAddDisplay(pt->name,
368                                                       0, 0, 0, 0, 0, 0,
369                                                       0, 0, 0, 0, 0, 0);
370        if (dmxNumScreens == 1) {
371            dmxScreen->where  = PosAbsolute;
372            dmxScreen->whereX = 0;
373            dmxScreen->whereY = 0;
374            dmxLog(dmxInfo, "Added %s at %d %d\n",
375                   dmxScreen->name, dmxScreen->whereX, dmxScreen->whereY);
376        } else {
377            dmxScreen->where          = PosRightOf;
378            dmxScreen->whereRefScreen = dmxNumScreens - 2;
379            if (dmxScreen->whereRefScreen < 0) dmxScreen->whereRefScreen = 0;
380            dmxLog(dmxInfo, "Added %s %s %s\n",
381                   dmxScreen->name,
382                   dmxScreen->where == PosBelow ? "below" : "right of",
383                   dmxScreens[dmxScreen->whereRefScreen].name);
384        }
385    }
386}
387
388static void dmxConfigFromConfigFile(void)
389{
390    DMXConfigEntryPtr pt;
391    const char        *name;
392
393    for (pt = dmxConfigEntry; pt; pt = pt->next) {
394                                /* FIXME -- if an input is specified, use it */
395        if (pt->type != dmxConfigVirtual) continue;
396        if ((name = dmxConfigMatch(dmxConfigCmd.config, pt))) {
397            dmxLog(dmxInfo, "Using configuration \"%s\"\n", name);
398            dmxConfigCopyData(pt->virtual);
399            return;
400        }
401    }
402    dmxLog(dmxFatal, "Could not find configuration \"%s\" in \"%s\"\n",
403           dmxConfigCmd.config, dmxConfigCmd.filename);
404}
405
406static void dmxConfigConfigInputs(void)
407{
408    DMXConfigListPtr pt;
409
410    if (dmxNumInputs) return;
411
412    if (dmxConfigCmd.inputs) {   /* Use command line */
413        for (pt = dmxConfigCmd.inputs; pt; pt = pt->next)
414            dmxConfigAddInput(pt->name, TRUE);
415    } else if (dmxNumScreens) { /* Use first display */
416        dmxConfigAddInput(dmxScreens[0].name, TRUE);
417    } else {                     /* Use dummy */
418        dmxConfigAddInput("dummy", TRUE);
419    }
420
421    if (dmxConfigCmd.xinputs) {  /* Non-core devices from command line */
422        for (pt = dmxConfigCmd.xinputs; pt; pt = pt->next)
423            dmxConfigAddInput(pt->name, FALSE);
424    }
425}
426
427/** Set up the appropriate global variables so that the DMX server will
428 * be initialized using the configuration specified in the config file
429 * and on the command line. */
430void dmxConfigConfigure(void)
431{
432    if (dmxConfigEntry) {
433        dmxConfigFreeEntry(dmxConfigEntry);
434        dmxConfigEntry = NULL;
435    }
436    if (dmxConfigCmd.filename) {
437        if (dmxConfigCmd.displays)
438            dmxLog(dmxWarning,
439                   "Using configuration file \"%s\" instead of command line\n",
440                   dmxConfigCmd.filename);
441        dmxConfigReadFile(dmxConfigCmd.filename, 0);
442        dmxConfigFromConfigFile();
443    } else {
444        if (dmxConfigCmd.config)
445            dmxLog(dmxWarning,
446                   "Configuration name (%s) without configuration file\n",
447                   dmxConfigCmd.config);
448        dmxConfigFromCommandLine();
449    }
450    dmxConfigConfigInputs();
451}
452
453/** This function determines the number of displays we WILL have and
454 * sets MAXSCREENS to that value.  This is difficult since the number
455 * depends on the command line (which is easy to count) or on the config
456 * file, which has to be parsed. */
457void dmxConfigSetMaxScreens(void)
458{
459    static int processing = 0;
460
461    if (processing) return;     /* Prevent reentry via ProcessCommandLine */
462    processing = 1;
463    if (dmxConfigCmd.filename) {
464        if (!dmxNumScreens)
465            dmxConfigConfigure();
466#ifndef MAXSCREENS
467        SetMaxScreens(dmxNumScreens);
468#endif
469    } else
470#ifndef MAXSCREENS
471        SetMaxScreens(dmxDisplaysFromCommandLine);
472#endif
473    processing = 0;
474}
475
476/** This macro is used to generate the following access methods:
477 * - dmxConfig{Set,Get}rules
478 * - dmxConfig{Set,Get}model
479 * - dmxConfig{Set,Get}layout
480 * - dmxConfig{Set,Get}variant
481 * - dmxConfig{Set,Get}options
482 * These methods are used to read and write information about the keyboard. */
483
484#define GEN(param,glob,def)                                                   \
485 void dmxConfigSet##glob(const char *param) {                                 \
486     if (dmx##glob) free((void *)dmx##glob);                                  \
487     dmx##glob = strdup(param);                                               \
488 }                                                                            \
489 char *dmxConfigGet##glob(void) {                                             \
490     return (char *)(dmx##glob ? dmx##glob : def);                            \
491 }
492
493GEN(rules,   XkbRules,   XKB_DFLT_RULES)
494GEN(model,   XkbModel,   XKB_DFLT_MODEL)
495GEN(layout,  XkbLayout,  XKB_DFLT_LAYOUT)
496GEN(variant, XkbVariant, XKB_DFLT_VARIANT)
497GEN(options, XkbOptions, XKB_DFLT_OPTIONS)
498