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