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 #ifdef HAVE_DMX_CONFIG_H
39 #include <dmx-config.h>
40 #endif
41 
42 #include "dmx.h"
43 #include "dmxinput.h"
44 #include "dmxconfig.h"
45 #include "dmxparse.h"
46 #include "dmxlog.h"
47 #include "dmxcb.h"
48 #include "dmxstat.h"
49 #include "parser.h"
50 
51 extern int yydebug;
52 extern FILE *yyin;
53 
54 static char *dmxXkbRules;
55 static char *dmxXkbModel;
56 static char *dmxXkbLayout;
57 static char *dmxXkbVariant;
58 static char *dmxXkbOptions;
59 
60 /** Stores lists of configuration information. */
61 typedef struct DMXConfigListStruct {
62     const char *name;
63     struct DMXConfigListStruct *next;
64 } DMXConfigList, *DMXConfigListPtr;
65 
66 /** This stucture stores the parsed configuration information. */
67 typedef struct DMXConfigCmdStruct {
68     const char *filename;
69     const char *config;
70     DMXConfigList *displays;
71     DMXConfigList *inputs;
72     DMXConfigList *xinputs;
73 } DMXConfigCmd, *DMXConfigCmdPtr;
74 
75 extern DMXConfigEntryPtr dmxConfigEntry;
76 static DMXConfigCmd dmxConfigCmd;
77 
78 static int dmxDisplaysFromCommandLine;
79 
80 /** Make a note that \a display is the name of an X11 display that
81  * should be initialized as a backend (output) display.  Called from
82  * #ddxProcessArgument. */
83 void
dmxConfigStoreDisplay(const char * display)84 dmxConfigStoreDisplay(const char *display)
85 {
86     DMXConfigListPtr entry = malloc(sizeof(*entry));
87 
88     entry->name = strdup(display);
89     entry->next = NULL;
90     if (!dmxConfigCmd.displays)
91         dmxConfigCmd.displays = entry;
92     else {
93         DMXConfigList *pt;
94 
95         for (pt = dmxConfigCmd.displays; pt->next; pt = pt->next);
96         if (!pt)
97             dmxLog(dmxFatal, "dmxConfigStoreDisplay: end of list non-NULL\n");
98         pt->next = entry;
99     }
100     ++dmxDisplaysFromCommandLine;
101 }
102 
103 /** Make a note that \a input is the name of an X11 display that should
104  * be used for input (either a backend or a console input device). */
105 void
dmxConfigStoreInput(const char * input)106 dmxConfigStoreInput(const char *input)
107 {
108     DMXConfigListPtr entry = malloc(sizeof(*entry));
109 
110     entry->name = strdup(input);
111     entry->next = NULL;
112     if (!dmxConfigCmd.inputs)
113         dmxConfigCmd.inputs = entry;
114     else {
115         DMXConfigList *pt;
116 
117         for (pt = dmxConfigCmd.inputs; pt->next; pt = pt->next);
118         if (!pt)
119             dmxLog(dmxFatal, "dmxConfigStoreInput: end of list non-NULL\n");
120         pt->next = entry;
121     }
122 }
123 
124 /** Make a note that \a input is the name of an X11 display that should
125  * be used for input from XInput extension devices. */
126 void
dmxConfigStoreXInput(const char * input)127 dmxConfigStoreXInput(const char *input)
128 {
129     DMXConfigListPtr entry = malloc(sizeof(*entry));
130 
131     entry->name = strdup(input);
132     entry->next = NULL;
133     if (!dmxConfigCmd.xinputs)
134         dmxConfigCmd.xinputs = entry;
135     else {
136         DMXConfigList *pt;
137 
138         for (pt = dmxConfigCmd.xinputs; pt->next; pt = pt->next);
139         if (!pt)
140             dmxLog(dmxFatal, "dmxConfigStoreXInput: end of list non-NULL\n");
141         pt->next = entry;
142     }
143 }
144 
145 /** Make a note that \a file is the configuration file. */
146 void
dmxConfigStoreFile(const char * file)147 dmxConfigStoreFile(const char *file)
148 {
149     if (dmxConfigCmd.filename)
150         dmxLog(dmxFatal, "Only one -configfile allowed\n");
151     dmxConfigCmd.filename = strdup(file);
152 }
153 
154 /** Make a note that \a config should be used as the configuration for
155  * current instantiation of the DMX server. */
156 void
dmxConfigStoreConfig(const char * config)157 dmxConfigStoreConfig(const char *config)
158 {
159     if (dmxConfigCmd.config)
160         dmxLog(dmxFatal, "Only one -config allowed\n");
161     dmxConfigCmd.config = strdup(config);
162 }
163 
164 static int
dmxConfigReadFile(const char * filename,int debug)165 dmxConfigReadFile(const char *filename, int debug)
166 {
167     FILE *str;
168 
169     if (!(str = fopen(filename, "r")))
170         return -1;
171     dmxLog(dmxInfo, "Reading configuration file \"%s\"\n", filename);
172     yyin = str;
173     yydebug = debug;
174     yyparse();
175     fclose(str);
176     return 0;
177 }
178 
179 static const char *
dmxConfigMatch(const char * target,DMXConfigEntryPtr entry)180 dmxConfigMatch(const char *target, DMXConfigEntryPtr entry)
181 {
182     DMXConfigVirtualPtr v = entry->virtual;
183     const char *name = NULL;
184 
185     if (v && v->name)
186         name = v->name;
187 
188     if (v && !dmxConfigCmd.config)
189         return v->name ? v->name : "<noname>";
190     if (!name)
191         return NULL;
192     if (!strcmp(name, target))
193         return name;
194     return NULL;
195 }
196 
197 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)198 dmxConfigAddDisplay(const char *name,
199                     int scrnWidth, int scrnHeight,
200                     int scrnX, int scrnY,
201                     int scrnXSign, int scrnYSign,
202                     int rootWidth, int rootHeight,
203                     int rootX, int rootY, int rootXSign, int rootYSign)
204 {
205     DMXScreenInfo *dmxScreen;
206 
207     if (!(dmxScreens = reallocarray(dmxScreens, dmxNumScreens + 1,
208                                     sizeof(*dmxScreens))))
209         dmxLog(dmxFatal,
210                "dmxConfigAddDisplay: realloc failed for screen %d (%s)\n",
211                dmxNumScreens, name);
212 
213     dmxScreen = &dmxScreens[dmxNumScreens];
214     memset(dmxScreen, 0, sizeof(*dmxScreen));
215     dmxScreen->name = name;
216     dmxScreen->index = dmxNumScreens;
217     dmxScreen->scrnWidth = scrnWidth;
218     dmxScreen->scrnHeight = scrnHeight;
219     dmxScreen->scrnX = scrnX;
220     dmxScreen->scrnY = scrnY;
221     dmxScreen->scrnXSign = scrnXSign;
222     dmxScreen->scrnYSign = scrnYSign;
223     dmxScreen->rootWidth = rootWidth;
224     dmxScreen->rootHeight = rootHeight;
225     dmxScreen->rootX = rootX;
226     dmxScreen->rootY = rootY;
227     dmxScreen->stat = dmxStatAlloc();
228     ++dmxNumScreens;
229     return dmxScreen;
230 }
231 
232 DMXInputInfo *
dmxConfigAddInput(const char * name,int core)233 dmxConfigAddInput(const char *name, int core)
234 {
235     DMXInputInfo *dmxInput;
236 
237     if (!(dmxInputs = reallocarray(dmxInputs, dmxNumInputs + 1,
238                                    sizeof(*dmxInputs))))
239         dmxLog(dmxFatal,
240                "dmxConfigAddInput: realloc failed for input %d (%s)\n",
241                dmxNumInputs, name);
242 
243     dmxInput = &dmxInputs[dmxNumInputs];
244 
245     memset(dmxInput, 0, sizeof(*dmxInput));
246     dmxInput->name = name;
247     dmxInput->inputIdx = dmxNumInputs;
248     dmxInput->scrnIdx = -1;
249     dmxInput->core = core;
250     ++dmxNumInputs;
251     return dmxInput;
252 }
253 
254 static void
dmxConfigCopyFromDisplay(DMXConfigDisplayPtr d)255 dmxConfigCopyFromDisplay(DMXConfigDisplayPtr d)
256 {
257     DMXScreenInfo *dmxScreen;
258 
259     dmxScreen = dmxConfigAddDisplay(d->name,
260                                     d->scrnWidth, d->scrnHeight,
261                                     d->scrnX, d->scrnY,
262                                     d->scrnXSign, d->scrnYSign,
263                                     d->rootWidth, d->rootHeight,
264                                     d->rootX, d->rootY,
265                                     d->rootXSign, d->rootXSign);
266     dmxScreen->where = PosAbsolute;
267     dmxScreen->whereX = d->rootXOrigin;
268     dmxScreen->whereY = d->rootYOrigin;
269 }
270 
271 static void
dmxConfigCopyFromWall(DMXConfigWallPtr w)272 dmxConfigCopyFromWall(DMXConfigWallPtr w)
273 {
274     DMXConfigStringPtr pt;
275     DMXScreenInfo *dmxScreen;
276     int edge = dmxNumScreens;
277     int last = dmxNumScreens;
278 
279     if (!w->xwall && !w->ywall) {       /* Try to make it square */
280         int count;
281 
282         for (pt = w->nameList, count = 0; pt; pt = pt->next)
283             ++count;
284         w->xwall = sqrt(count) + .5;
285     }
286 
287     for (pt = w->nameList; pt; pt = pt->next) {
288         dmxScreen = dmxConfigAddDisplay(pt->string, w->width, w->height,
289                                         0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
290         if (pt == w->nameList) {        /* Upper left */
291             dmxScreen->where = PosAbsolute;
292             dmxScreen->whereX = 0;
293             dmxScreen->whereY = 0;
294         }
295         else if (w->xwall) {    /* Tile left to right, then top to bottom */
296             if (!((dmxNumScreens - 1) % w->xwall)) {
297                 dmxScreen->where = PosBelow;
298                 dmxScreen->whereRefScreen = edge;
299                 edge = dmxNumScreens - 1;
300             }
301             else {
302                 dmxScreen->where = PosRightOf;
303                 dmxScreen->whereRefScreen = last;
304             }
305         }
306         else {                  /* Tile top to bottom, then left to right */
307             if (!((dmxNumScreens - 1) % w->ywall)) {
308                 dmxScreen->where = PosRightOf;
309                 dmxScreen->whereRefScreen = edge;
310                 edge = dmxNumScreens - 1;
311             }
312             else {
313                 dmxScreen->where = PosBelow;
314                 dmxScreen->whereRefScreen = last;
315             }
316 
317         }
318         last = dmxNumScreens - 1;
319         if (dmxScreen->where == PosAbsolute)
320             dmxLog(dmxInfo, "Added %s at %d %d\n",
321                    pt->string, dmxScreen->whereX, dmxScreen->whereY);
322         else
323             dmxLog(dmxInfo, "Added %s %s %s\n",
324                    pt->string,
325                    dmxScreen->where == PosBelow ? "below" : "right of",
326                    dmxScreens[dmxScreen->whereRefScreen].name);
327     }
328 }
329 
330 static void
dmxConfigCopyFromOption(DMXConfigOptionPtr o)331 dmxConfigCopyFromOption(DMXConfigOptionPtr o)
332 {
333     DMXConfigStringPtr pt;
334     int argc = 0;
335     char **argv = NULL;
336 
337     if (serverGeneration != 1)
338         return;                 /* FIXME: only do once, for now */
339     if (!o || !o->string)
340         return;
341     for (pt = o->option; pt; pt = pt->next) {
342         if (pt->string) {
343             ++argc;
344             argv = reallocarray(argv, argc + 1, sizeof(*argv));
345             argv[argc] = (char *) pt->string;
346         }
347     }
348     argv[0] = NULL;
349     ProcessCommandLine(argc + 1, argv);
350     free(argv);
351 }
352 
353 static void
dmxConfigCopyFromParam(DMXConfigParamPtr p)354 dmxConfigCopyFromParam(DMXConfigParamPtr p)
355 {
356     const char **argv;
357     int argc;
358 
359     if ((argv = dmxConfigLookupParam(p, "xkbrules", &argc)) && argc == 2) {
360         dmxConfigSetXkbRules(argv[1]);
361     }
362     else if ((argv = dmxConfigLookupParam(p, "xkbmodel", &argc))
363              && argc == 2) {
364         dmxConfigSetXkbModel(argv[1]);
365     }
366     else if ((argv = dmxConfigLookupParam(p, "xkblayout", &argc))
367              && argc == 2) {
368         dmxConfigSetXkbLayout(argv[1]);
369     }
370     else if ((argv = dmxConfigLookupParam(p, "xkbvariant", &argc))
371              && argc == 2) {
372         dmxConfigSetXkbVariant(argv[1]);
373     }
374     else if ((argv = dmxConfigLookupParam(p, "xkboptions", &argc))
375              && argc == 2) {
376         dmxConfigSetXkbOptions(argv[1]);
377     }
378 }
379 
380 static void
dmxConfigCopyData(DMXConfigVirtualPtr v)381 dmxConfigCopyData(DMXConfigVirtualPtr v)
382 {
383     DMXConfigSubPtr sub;
384 
385     if (v->dim)
386         dmxSetWidthHeight(v->dim->x, v->dim->y);
387     else
388         dmxSetWidthHeight(0, 0);
389     for (sub = v->subentry; sub; sub = sub->next) {
390         switch (sub->type) {
391         case dmxConfigDisplay:
392             dmxConfigCopyFromDisplay(sub->display);
393             break;
394         case dmxConfigWall:
395             dmxConfigCopyFromWall(sub->wall);
396             break;
397         case dmxConfigOption:
398             dmxConfigCopyFromOption(sub->option);
399             break;
400         case dmxConfigParam:
401             dmxConfigCopyFromParam(sub->param);
402             break;
403         default:
404             dmxLog(dmxFatal,
405                    "dmxConfigCopyData: not a display, wall, or value\n");
406         }
407     }
408 }
409 
410 static void
dmxConfigFromCommandLine(void)411 dmxConfigFromCommandLine(void)
412 {
413     DMXConfigListPtr pt;
414 
415     dmxLog(dmxInfo, "Using configuration from command line\n");
416     for (pt = dmxConfigCmd.displays; pt; pt = pt->next) {
417         DMXScreenInfo *dmxScreen = dmxConfigAddDisplay(pt->name,
418                                                        0, 0, 0, 0, 0, 0,
419                                                        0, 0, 0, 0, 0, 0);
420 
421         if (dmxNumScreens == 1) {
422             dmxScreen->where = PosAbsolute;
423             dmxScreen->whereX = 0;
424             dmxScreen->whereY = 0;
425             dmxLog(dmxInfo, "Added %s at %d %d\n",
426                    dmxScreen->name, dmxScreen->whereX, dmxScreen->whereY);
427         }
428         else {
429             dmxScreen->where = PosRightOf;
430             dmxScreen->whereRefScreen = dmxNumScreens - 2;
431             if (dmxScreen->whereRefScreen < 0)
432                 dmxScreen->whereRefScreen = 0;
433             dmxLog(dmxInfo, "Added %s %s %s\n",
434                    dmxScreen->name,
435                    dmxScreen->where == PosBelow ? "below" : "right of",
436                    dmxScreens[dmxScreen->whereRefScreen].name);
437         }
438     }
439 }
440 
441 static void
dmxConfigFromConfigFile(void)442 dmxConfigFromConfigFile(void)
443 {
444     DMXConfigEntryPtr pt;
445     const char *name;
446 
447     for (pt = dmxConfigEntry; pt; pt = pt->next) {
448         /* FIXME -- if an input is specified, use it */
449         if (pt->type != dmxConfigVirtual)
450             continue;
451         if ((name = dmxConfigMatch(dmxConfigCmd.config, pt))) {
452             dmxLog(dmxInfo, "Using configuration \"%s\"\n", name);
453             dmxConfigCopyData(pt->virtual);
454             return;
455         }
456     }
457     dmxLog(dmxFatal, "Could not find configuration \"%s\" in \"%s\"\n",
458            dmxConfigCmd.config, dmxConfigCmd.filename);
459 }
460 
461 static void
dmxConfigConfigInputs(void)462 dmxConfigConfigInputs(void)
463 {
464     DMXConfigListPtr pt;
465 
466     if (dmxNumInputs)
467         return;
468 
469     if (dmxConfigCmd.inputs) {  /* Use command line */
470         for (pt = dmxConfigCmd.inputs; pt; pt = pt->next)
471             dmxConfigAddInput(pt->name, TRUE);
472     }
473     else if (dmxNumScreens) {   /* Use first display */
474         dmxConfigAddInput(dmxScreens[0].name, TRUE);
475     }
476     else {                      /* Use dummy */
477         dmxConfigAddInput("dummy", TRUE);
478     }
479 
480     if (dmxConfigCmd.xinputs) { /* Non-core devices from command line */
481         for (pt = dmxConfigCmd.xinputs; pt; pt = pt->next)
482             dmxConfigAddInput(pt->name, FALSE);
483     }
484 }
485 
486 /** Set up the appropriate global variables so that the DMX server will
487  * be initialized using the configuration specified in the config file
488  * and on the command line. */
489 void
dmxConfigConfigure(void)490 dmxConfigConfigure(void)
491 {
492     if (dmxConfigEntry) {
493         dmxConfigFreeEntry(dmxConfigEntry);
494         dmxConfigEntry = NULL;
495     }
496     if (dmxConfigCmd.filename) {
497         if (dmxConfigCmd.displays)
498             dmxLog(dmxWarning,
499                    "Using configuration file \"%s\" instead of command line\n",
500                    dmxConfigCmd.filename);
501         dmxConfigReadFile(dmxConfigCmd.filename, 0);
502         dmxConfigFromConfigFile();
503     }
504     else {
505         if (dmxConfigCmd.config)
506             dmxLog(dmxWarning,
507                    "Configuration name (%s) without configuration file\n",
508                    dmxConfigCmd.config);
509         dmxConfigFromCommandLine();
510     }
511     dmxConfigConfigInputs();
512 }
513 
514 /** This function determines the number of displays we WILL have and
515  * sets MAXSCREENS to that value.  This is difficult since the number
516  * depends on the command line (which is easy to count) or on the config
517  * file, which has to be parsed. */
518 void
dmxConfigSetMaxScreens(void)519 dmxConfigSetMaxScreens(void)
520 {
521     static int processing = 0;
522 
523     if (processing)
524         return;                 /* Prevent reentry via ProcessCommandLine */
525     processing = 1;
526     if (dmxConfigCmd.filename) {
527         if (!dmxNumScreens)
528             dmxConfigConfigure();
529 #ifndef MAXSCREENS
530         SetMaxScreens(dmxNumScreens);
531 #endif
532     }
533     else
534 #ifndef MAXSCREENS
535         SetMaxScreens(dmxDisplaysFromCommandLine);
536 #endif
537     processing = 0;
538 }
539 
540 /** This macro is used to generate the following access methods:
541  * - dmxConfig{Set,Get}rules
542  * - dmxConfig{Set,Get}model
543  * - dmxConfig{Set,Get}layout
544  * - dmxConfig{Set,Get}variant
545  * - dmxConfig{Set,Get}options
546  * These methods are used to read and write information about the keyboard. */
547 
548 #define GEN(param,glob,def)                                                   \
549  void dmxConfigSet##glob(const char *param) {                                 \
550      if (dmx##glob) free((void *)dmx##glob);                                  \
551      dmx##glob = strdup(param);                                               \
552  }                                                                            \
553  char *dmxConfigGet##glob(void) {                                             \
554      return (char *)(dmx##glob ? dmx##glob : def);                            \
555  }
556 
557 GEN(rules, XkbRules, XKB_DFLT_RULES)
558     GEN(model, XkbModel, XKB_DFLT_MODEL)
559     GEN(layout, XkbLayout, XKB_DFLT_LAYOUT)
560     GEN(variant, XkbVariant, XKB_DFLT_VARIANT)
561     GEN(options, XkbOptions, XKB_DFLT_OPTIONS)
562