1/************************************************************ 2 Copyright (c) 1996 by Silicon Graphics Computer Systems, Inc. 3 4 Permission to use, copy, modify, and distribute this 5 software and its documentation for any purpose and without 6 fee is hereby granted, provided that the above copyright 7 notice appear in all copies and that both that copyright 8 notice and this permission notice appear in supporting 9 documentation, and that the name of Silicon Graphics not be 10 used in advertising or publicity pertaining to distribution 11 of the software without specific prior written permission. 12 Silicon Graphics makes no representation about the suitability 13 of this software for any purpose. It is provided "as is" 14 without any express or implied warranty. 15 16 SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS 17 SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 18 AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON 19 GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL 20 DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 21 DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 22 OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH 23 THE USE OR PERFORMANCE OF THIS SOFTWARE. 24 25 ********************************************************/ 26 27#ifdef HAVE_CONFIG_H 28#include "config.h" 29#endif 30#include <stdio.h> 31#include <stdlib.h> 32#include <locale.h> 33#include <limits.h> 34#include <ctype.h> 35#include <X11/Xlib.h> 36#include <X11/Xos.h> 37#include <X11/XKBlib.h> 38#include <X11/extensions/XKBfile.h> 39#include <X11/extensions/XKBconfig.h> 40#include <X11/extensions/XKBrules.h> 41#include <X11/extensions/Xrandr.h> 42 43#ifndef PATH_MAX 44#ifdef MAXPATHLEN 45#define PATH_MAX MAXPATHLEN 46#else 47#define PATH_MAX 1024 48#endif 49#endif 50 51#ifndef DFLT_XKB_CONFIG_ROOT 52#define DFLT_XKB_CONFIG_ROOT "/usr/share/X11/xkb" 53#endif 54#ifndef DFLT_XKB_RULES_FILE 55#define DFLT_XKB_RULES_FILE "base" 56#endif 57#ifndef DFLT_XKB_LAYOUT 58#define DFLT_XKB_LAYOUT "us" 59#endif 60#ifndef DFLT_XKB_MODEL 61#define DFLT_XKB_MODEL "pc105" 62#endif 63 64/* Constants to state how a value was obtained. The order of these 65 * is important, the bigger the higher the priority. 66 * e.g. FROM_CONFIG overrides FROM_SERVER */ 67enum source { 68 UNDEFINED = 0, 69 FROM_SERVER, /* Retrieved from server at runtime. */ 70 FROM_RULES, /* Xkb rules file. */ 71 FROM_CONFIG, /* Command-line specified config file. */ 72 FROM_CMD_LINE, /* Specified at the cmdline. */ 73 NUM_SOURCES 74}; 75 76/***====================================================================***/ 77static Bool print = False; 78static Bool query = False; 79static Bool synch = False; 80static int verbose = 5; 81 82static Display *dpy; 83 84/** 85 * human-readable versions of FROM_CONFIG, FROM_SERVER, etc. Used for error 86 * reporting. 87 */ 88static const char *srcName[NUM_SOURCES] = { 89 "undefined", "X server", "rules file", "config file", "command line" 90}; 91 92struct setting { 93 char const *name; /* Human-readable setting name. Used for error reporting. */ 94 char *value; /* Holds the value. */ 95 enum source src; /* Holds the source. */ 96}; 97 98typedef struct setting setting_t; 99 100struct settings { 101 setting_t rules; /* Rules file */ 102 setting_t config; /* Config file (if used) */ 103 setting_t display; /* X display name */ 104 setting_t locale; /* Machine's locale */ 105 setting_t model; 106 setting_t layout; 107 setting_t variant; 108 setting_t keycodes; 109 setting_t types; 110 setting_t compat; 111 setting_t symbols; 112 setting_t geometry; 113 setting_t keymap; 114}; 115 116typedef struct settings settings_t; 117 118static settings_t settings = { 119 { "rules file", NULL, UNDEFINED }, 120 { "config file", NULL, UNDEFINED }, 121 { "X display", NULL, UNDEFINED }, 122 { "locale", NULL, UNDEFINED }, 123 { "keyboard model", NULL, UNDEFINED }, 124 { "keyboard layout", NULL, UNDEFINED }, 125 { "layout variant", NULL, UNDEFINED }, 126 { "keycodes", NULL, UNDEFINED }, 127 { "types", NULL, UNDEFINED }, 128 { "compatibility map", NULL, UNDEFINED }, 129 { "symbols", NULL, UNDEFINED }, 130 { "geometry", NULL, UNDEFINED }, 131 { "keymap", NULL, UNDEFINED } 132}; 133 134static XkbConfigRtrnRec cfgResult; 135 136static XkbRF_VarDefsRec rdefs; 137 138static Bool clearOptions = False; 139 140struct list { 141 char **item; /* Array of items. */ 142 int sz; /* Size of array. */ 143 int num; /* Number of used elements. */ 144}; 145 146typedef struct list list_t; 147 148static list_t options = { NULL, 0, 0 }; 149 150static list_t inclPath = { NULL, 0, 0 }; 151 152static XkbDescPtr xkb = NULL; 153 154static int deviceSpec = XkbUseCoreKbd; 155 156/***====================================================================***/ 157 158#define streq(s1,s2) (strcmp(s1,s2)==0) 159#define strpfx(s1,s2) (strncmp(s1,s2,strlen(s2))==0) 160 161#define MSG(s) printf(s) 162#define MSG1(s,a) printf(s,a) 163#define MSG2(s,a,b) printf(s,a,b) 164#define MSG3(s,a,b,c) printf(s,a,b,c) 165 166#define VMSG(l,s) if (verbose>(l)) printf(s) 167#define VMSG1(l,s,a) if (verbose>(l)) printf(s,a) 168#define VMSG2(l,s,a,b) if (verbose>(l)) printf(s,a,b) 169#define VMSG3(l,s,a,b,c) if (verbose>(l)) printf(s,a,b,c) 170 171#define ERR(s) fprintf(stderr,s) 172#define ERR1(s,a) fprintf(stderr,s,a) 173#define ERR2(s,a,b) fprintf(stderr,s,a,b) 174#define ERR3(s,a,b,c) fprintf(stderr,s,a,b,c) 175 176#define OOM(ptr) do { if ((ptr) == NULL) { ERR("Out of memory.\n"); exit(1); } } while (0) 177 178/***====================================================================***/ 179 180static Bool addToList(list_t *list, const char *newVal); 181static void usage(int argc, char **argv); 182static void dumpNames(Bool wantRules, Bool wantCNames); 183static void trySetString(setting_t * setting, char *newVal, enum source src); 184static Bool setOptString(int *arg, int argc, char **argv, setting_t *setting, enum source src); 185static int parseArgs(int argc, char **argv); 186static Bool getDisplay(int argc, char **argv); 187static Bool getServerValues(void); 188static FILE *findFileInPath(char *name); 189static Bool addStringToOptions(char *opt_str, list_t *opts); 190static char *stringFromOptions(char *orig, list_t *newOpts); 191static Bool applyConfig(char *name); 192static XkbRF_RulesPtr tryLoadRules(char *name, char *locale, Bool wantDesc, Bool wantRules); 193static Bool applyRules(void); 194static Bool applyComponentNames(void); 195static void printKeymap(void); 196 197/***====================================================================***/ 198 199/* 200 If newVal is NULL or empty string, the list is cleared. 201 Otherwise newVal is added to the end of the list (if it is not present in the list yet). 202*/ 203 204static Bool 205addToList(list_t *list, const char *newVal) 206{ 207 register int i; 208 209 if ((!newVal) || (!newVal[0])) 210 { 211 list->num = 0; 212 return True; 213 } 214 for (i = 0; i < list->num; i++) 215 { 216 if (streq(list->item[i], newVal)) 217 return True; 218 } 219 if ((list->item == NULL) || (list->sz < 1)) 220 { 221 list->num = 0; 222 list->sz = 4; 223 list->item = (char **) calloc(list->sz, sizeof(char *)); 224 OOM(list->item); 225 } 226 else if (list->num >= list->sz) 227 { 228 list->sz *= 2; 229 list->item = (char **) realloc(list->item, (list->sz) * sizeof(char *)); 230 OOM(list->item); 231 } 232 list->item[list->num] = strdup(newVal); 233 OOM(list->item[list->num]); 234 list->num += 1; 235 return True; 236} 237 238/***====================================================================***/ 239 240static void 241usage(int argc, char **argv) 242{ 243 MSG1( 244 "Usage: %s [options] [<layout> [<variant> [<option> ... ]]]\n" 245 "Options:\n" 246 " -?, -help Print this message\n" 247 " -compat <name> Specifies compatibility map component name\n" 248 " -config <file> Specifies configuration file to use\n" 249 " -device <deviceid> Specifies the device ID to use\n" 250 " -display <dpy> Specifies display to use\n" 251 " -geometry <name> Specifies geometry component name\n" 252 " -I <dir> Add <dir> to list of directories to be used\n" 253 " -keycodes <name> Specifies keycodes component name\n" 254 " -keymap <name> Specifies name of keymap to load\n" 255 " -layout <name> Specifies layout used to choose component names\n" 256 " -model <name> Specifies model used to choose component names\n" 257 " -option <name> Adds an option used to choose component names\n" 258 " -print Print a complete xkb_keymap description and exit\n" 259 " -query Print the current layout settings and exit\n" 260 " -rules <name> Name of rules file to use\n" 261 " -symbols <name> Specifies symbols component name\n" 262 " -synch Synchronize request with X server\n" 263 " -types <name> Specifies types component name\n" 264 " -v[erbose] [<lvl>] Sets verbosity (1..10); higher values yield more messages\n" 265 " -version Print the program's version number\n" 266 " -variant <name> Specifies layout variant used to choose component names\n", 267 argv[0] 268 ); 269} 270 271static void 272dumpNames(Bool wantRules, Bool wantCNames) 273{ 274 if (wantRules) 275 { 276 if (settings.rules.value) 277 MSG1("rules: %s\n", settings.rules.value); 278 if (settings.model.value) 279 MSG1("model: %s\n", settings.model.value); 280 if (settings.layout.value) 281 MSG1("layout: %s\n", settings.layout.value); 282 if (settings.variant.value) 283 MSG1("variant: %s\n", settings.variant.value); 284 if (options.item) 285 { 286 char *opt_str = stringFromOptions(NULL, &options); 287 MSG1("options: %s\n", opt_str); 288 free(opt_str); 289 } 290 } 291 if (wantCNames) 292 { 293 if (settings.keymap.value) 294 MSG1("keymap: %s\n", settings.keymap.value); 295 if (settings.keycodes.value) 296 MSG1("keycodes: %s\n", settings.keycodes.value); 297 if (settings.types.value) 298 MSG1("types: %s\n", settings.types.value); 299 if (settings.compat.value) 300 MSG1("compat: %s\n", settings.compat.value); 301 if (settings.symbols.value) 302 MSG1("symbols: %s\n", settings.symbols.value); 303 if (settings.geometry.value) 304 MSG1("geometry: %s\n", settings.geometry.value); 305 } 306 return; 307} 308 309/***====================================================================***/ 310 311/** 312 * Set the given string (obtained from src) in the svValue/svSrc globals. 313 * If the given item is already set, it is overridden if the original source 314 * is less significant than the given one. 315 * 316 * @param which What value is it (one of RULES_NDX, CONFIG_NDX, ...) 317 */ 318static void 319trySetString(setting_t *setting, char *newVal, enum source src) 320{ 321 if (setting->value != NULL) 322 { 323 if (setting->src == src) 324 { 325 VMSG2(0, "Warning! More than one %s from %s\n", 326 setting->name, srcName[src]); 327 VMSG2(0, " Using \"%s\", ignoring \"%s\"\n", 328 setting->value, newVal); 329 return; 330 } 331 else if (setting->src > src) 332 { 333 VMSG1(5, "Warning! Multiple definitions of %s\n", setting->name); 334 VMSG2(5, " Using %s, ignoring %s\n", 335 srcName[setting->src], srcName[src]); 336 return; 337 } 338 } 339 setting->src = src; 340 setting->value = newVal; 341 return; 342} 343 344static Bool 345setOptString(int *arg, int argc, char **argv, setting_t *setting, enum source src) 346{ 347 int ndx; 348 char *opt; 349 350 ndx = *arg; 351 opt = argv[ndx]; 352 if (ndx >= argc - 1) 353 { 354 VMSG1(0, "No %s specified on the command line\n", setting->name); 355 VMSG1(0, "Trailing %s option ignored\n", opt); 356 return True; 357 } 358 ndx++; 359 *arg = ndx; 360 if (setting->value != NULL) 361 { 362 if (setting->src == src) 363 { 364 VMSG2(0, "More than one %s on %s\n", setting->name, srcName[src]); 365 VMSG2(0, "Using \"%s\", ignoring \"%s\"\n", setting->value, 366 argv[ndx]); 367 return True; 368 } 369 else if (setting->src > src) 370 { 371 VMSG1(5, "Multiple definitions of %s\n", setting->name); 372 VMSG2(5, "Using %s, ignoring %s\n", srcName[setting->src], 373 srcName[src]); 374 return True; 375 } 376 } 377 setting->src = src; 378 setting->value = argv[ndx]; 379 return True; 380} 381 382/***====================================================================***/ 383 384/** 385 * Parse commandline arguments. 386 * Return True on success or False if an unrecognized option has been 387 * specified. 388 */ 389static int 390parseArgs(int argc, char **argv) 391{ 392 int i; 393 Bool ok; 394 unsigned present; 395 396 ok = True; 397 addToList(&inclPath, "."); 398 addToList(&inclPath, DFLT_XKB_CONFIG_ROOT); 399 for (i = 1; (i < argc) && ok; i++) 400 { 401 if (argv[i][0] != '-') 402 { 403 /* Allow a call like "setxkbmap us" to work. Layout is default, 404 if -layout is given, then try parsing variant, then options */ 405 if (!settings.layout.src) 406 trySetString(&settings.layout, argv[i], FROM_CMD_LINE); 407 else if (!settings.variant.src) 408 trySetString(&settings.variant, argv[i], FROM_CMD_LINE); 409 else 410 ok = addToList(&options, argv[i]); 411 } 412 else if (streq(argv[i], "-compat")) 413 ok = setOptString(&i, argc, argv, &settings.compat, FROM_CMD_LINE); 414 else if (streq(argv[i], "-config")) 415 ok = setOptString(&i, argc, argv, &settings.config, FROM_CMD_LINE); 416 else if (streq(argv[i], "-device")) 417 { 418 if ( ++i < argc ) { 419 deviceSpec = atoi(argv[i]); /* only allow device IDs, not names */ 420 } else { 421 usage(argc, argv); 422 exit(1); 423 } 424 } 425 else if (streq(argv[i], "-display")) 426 ok = setOptString(&i, argc, argv, &settings.display, FROM_CMD_LINE); 427 else if (streq(argv[i], "-geometry")) 428 ok = setOptString(&i, argc, argv, &settings.geometry, FROM_CMD_LINE); 429 else if (streq(argv[i], "-help") || streq(argv[i], "-?")) 430 { 431 usage(argc, argv); 432 exit(0); 433 } 434 else if (streq(argv[i], "-I")) /* space between -I and path */ 435 { 436 if ( ++i < argc ) 437 ok = addToList(&inclPath, argv[i]); 438 else 439 VMSG(0, "No directory specified on the command line\n" 440 "Trailing -I option ignored\n"); 441 } 442 else if (strpfx(argv[i], "-I")) /* no space between -I and path */ 443 ok = addToList(&inclPath, &argv[i][2]); 444 else if (streq(argv[i], "-keycodes")) 445 ok = setOptString(&i, argc, argv, &settings.keycodes, FROM_CMD_LINE); 446 else if (streq(argv[i], "-keymap")) 447 ok = setOptString(&i, argc, argv, &settings.keymap, FROM_CMD_LINE); 448 else if (streq(argv[i], "-layout")) 449 ok = setOptString(&i, argc, argv, &settings.layout, FROM_CMD_LINE); 450 else if (streq(argv[i], "-model")) 451 ok = setOptString(&i, argc, argv, &settings.model, FROM_CMD_LINE); 452 else if (streq(argv[i], "-option")) 453 { 454 if ((i == argc - 1) || (argv[i + 1][0] == '\0') 455 || (argv[i + 1][0] == '-')) 456 { 457 clearOptions = True; 458 ok = addToList(&options, ""); 459 if (i < argc - 1 && argv[i + 1][0] == '\0') 460 i++; 461 } 462 else 463 { 464 ok = addToList(&options, argv[++i]); 465 } 466 } 467 else if (streq(argv[i], "-print")) 468 print = True; 469 else if (streq(argv[i], "-query")) 470 query = True; 471 else if (streq(argv[i], "-rules")) 472 ok = setOptString(&i, argc, argv, &settings.rules, FROM_CMD_LINE); 473 else if (streq(argv[i], "-symbols")) 474 ok = setOptString(&i, argc, argv, &settings.symbols, FROM_CMD_LINE); 475 else if (streq(argv[i], "-synch")) 476 synch = True; 477 else if (streq(argv[i], "-types")) 478 ok = setOptString(&i, argc, argv, &settings.types, FROM_CMD_LINE); 479 else if (streq(argv[i], "-version")) 480 { 481 MSG1("setxkbmap %s\n", PACKAGE_VERSION); 482 exit(0); 483 } 484 else if (streq(argv[i], "-verbose") || (streq(argv[i], "-v"))) 485 { 486 if ((i < argc - 1) && (isdigit(argv[i + 1][0]))) 487 verbose = atoi(argv[++i]); 488 else 489 verbose++; 490 if (verbose < 0) 491 { 492 ERR1("Illegal verbose level %d. Reset to 0\n", verbose); 493 verbose = 0; 494 } 495 else if (verbose > 10) 496 { 497 ERR1("Illegal verbose level %d. Reset to 10\n", verbose); 498 verbose = 10; 499 } 500 VMSG1(7, "Setting verbose level to %d\n", verbose); 501 } 502 else if (streq(argv[i], "-variant")) 503 ok = setOptString(&i, argc, argv, &settings.variant, FROM_CMD_LINE); 504 else 505 { 506 ERR1("Error! Option \"%s\" not recognized\n", argv[i]); 507 ok = False; 508 } 509 } 510 511 present = 0; 512 if (settings.types.value) 513 present++; 514 if (settings.compat.value) 515 present++; 516 if (settings.symbols.value) 517 present++; 518 if (settings.keycodes.value) 519 present++; 520 if (settings.geometry.value) 521 present++; 522 if (settings.config.value) 523 present++; 524 if (settings.model.value) 525 present++; 526 if (settings.layout.value) 527 present++; 528 if (settings.variant.value) 529 present++; 530 if (settings.keymap.value && present) 531 { 532 ERR("No other components can be specified when a keymap is present\n"); 533 return False; 534 } 535 return ok; 536} 537 538/** 539 * Open a connection to the display and print error if it fails. 540 * 541 * @return True on success or False otherwise. 542 */ 543static Bool 544getDisplay(int argc, char **argv) 545{ 546 int major, minor, why; 547 548 major = XkbMajorVersion; 549 minor = XkbMinorVersion; 550 dpy = 551 XkbOpenDisplay(settings.display.value, NULL, NULL, &major, &minor, 552 &why); 553 if (!dpy) 554 { 555 if (settings.display.value == NULL) 556 settings.display.value = getenv("DISPLAY"); 557 if (settings.display.value == NULL) 558 settings.display.value = (char *) "default display"; 559 switch (why) 560 { 561 case XkbOD_BadLibraryVersion: 562 ERR3("%s was compiled with XKB version %d.%02d\n", argv[0], 563 XkbMajorVersion, XkbMinorVersion); 564 ERR2("Xlib supports incompatible version %d.%02d\n", 565 major, minor); 566 break; 567 case XkbOD_ConnectionRefused: 568 ERR1("Cannot open display \"%s\"\n", settings.display.value); 569 break; 570 case XkbOD_NonXkbServer: 571 ERR1("XKB extension not present on %s\n", settings.display.value); 572 break; 573 case XkbOD_BadServerVersion: 574 ERR3("%s was compiled with XKB version %d.%02d\n", argv[0], 575 XkbMajorVersion, XkbMinorVersion); 576 ERR3("Server %s uses incompatible version %d.%02d\n", 577 settings.display.value, major, minor); 578 break; 579 default: 580 ERR1("Unknown error %d from XkbOpenDisplay\n", why); 581 break; 582 } 583 return False; 584 } 585 if (synch) 586 XSynchronize(dpy, True); 587 return True; 588} 589 590/***====================================================================***/ 591 592/** 593 * Retrieve xkb values from the XKB_RULES_NAMES property and store their 594 * contents in svValues. 595 * If the property cannot be read, the built-in defaults are used. 596 * 597 * @return True. 598 */ 599static Bool 600getServerValues(void) 601{ 602 XkbRF_VarDefsRec vd; 603 char *tmp = NULL; 604 605 if (!XkbRF_GetNamesProp(dpy, &tmp, &vd) || !tmp) 606 { 607 VMSG1(3, "Couldn't interpret %s property\n", _XKB_RF_NAMES_PROP_ATOM); 608 tmp = (char *) DFLT_XKB_RULES_FILE; 609 vd.model = (char *) DFLT_XKB_MODEL; 610 vd.layout = (char *) DFLT_XKB_LAYOUT; 611 vd.variant = NULL; 612 vd.options = NULL; 613 VMSG3(3, "Use defaults: rules - '%s' model - '%s' layout - '%s'\n", 614 tmp, vd.model, vd.layout); 615 } 616 if (tmp) 617 trySetString(&settings.rules, tmp, FROM_SERVER); 618 if (vd.model) 619 trySetString(&settings.model, vd.model, FROM_SERVER); 620 if (vd.layout) 621 trySetString(&settings.layout, vd.layout, FROM_SERVER); 622 if (vd.variant) 623 trySetString(&settings.variant, vd.variant, FROM_SERVER); 624 if ((vd.options) && (!clearOptions)) 625 { 626 addStringToOptions(vd.options, &options); 627 XFree(vd.options); 628 } 629 return True; 630} 631 632/***====================================================================***/ 633 634static FILE * 635findFileInPath(char *name) 636{ 637 register int i; 638 char buf[PATH_MAX]; 639 FILE *fp; 640 641 if (name[0] == '/') 642 { 643 fp = fopen(name, "r"); 644 if ((verbose > 7) || ((!fp) && (verbose > 0))) 645 MSG2("%s file %s\n", (fp ? "Found" : "Didn't find"), name); 646 return fp; 647 } 648 for (i = 0; (i < inclPath.num); i++) 649 { 650 if (snprintf(buf, PATH_MAX, "%s/%s", inclPath.item[i], name) >= 651 PATH_MAX) 652 { 653 VMSG2(0, "Path too long (%s/%s). Ignored.\n", inclPath.item[i], 654 name); 655 continue; 656 } 657 fp = fopen(buf, "r"); 658 if ((verbose > 7) || ((!fp) && (verbose > 5))) 659 MSG2("%s file %s\n", (fp ? "Found" : "Didn't find"), buf); 660 if (fp != NULL) 661 return fp; 662 } 663 return NULL; 664} 665 666/***====================================================================***/ 667 668static Bool 669addStringToOptions(char *opt_str, list_t *opts) 670{ 671 char *tmp, *str, *next; 672 Bool ok = True; 673 674 str = strdup(opt_str); 675 OOM(str); 676 for (tmp = str; (tmp && *tmp != '\0') && ok; tmp = next) 677 { 678 next = strchr(str, ','); 679 if (next) 680 { 681 *next = '\0'; 682 next++; 683 } 684 ok = addToList(opts, tmp) && ok; 685 } 686 free(str); 687 return ok; 688} 689 690/***====================================================================***/ 691 692static char * 693stringFromOptions(char *orig, list_t *newOpts) 694{ 695 size_t len; 696 int i, nOut; 697 698 if (orig) 699 len = strlen(orig) + 1; 700 else 701 len = 0; 702 for (i = 0; i < newOpts->num; i++) 703 { 704 if (newOpts->item[i]) 705 len += strlen(newOpts->item[i]) + 1; 706 } 707 if (len < 1) 708 return NULL; 709 if (orig) 710 { 711 orig = (char *) realloc(orig, len); 712 OOM(orig); 713 nOut = 1; 714 } 715 else 716 { 717 orig = (char *) calloc(len, 1); 718 OOM(orig); 719 nOut = 0; 720 } 721 for (i = 0; i < newOpts->num; i++) 722 { 723 if (!newOpts->item[i]) 724 continue; 725 if (nOut > 0) 726 { 727 strcat(orig, ","); 728 strcat(orig, newOpts->item[i]); 729 } 730 else 731 strcpy(orig, newOpts->item[i]); 732 nOut++; 733 } 734 return orig; 735} 736 737/***====================================================================***/ 738 739static Bool 740applyConfig(char *name) 741{ 742 FILE *fp; 743 Bool ok; 744 745 if ((fp = findFileInPath(name)) == NULL) 746 { 747 ERR1("Couldn't find configuration file \"%s\"\n", name); 748 return False; 749 } 750 ok = XkbCFParse(fp, XkbCFDflts, NULL, &cfgResult); 751 fclose(fp); 752 if (!ok) 753 { 754 ERR1("Couldn't parse configuration file \"%s\"\n", name); 755 return False; 756 } 757 if (cfgResult.rules_file) 758 { 759 trySetString(&settings.rules, cfgResult.rules_file, FROM_CONFIG); 760 cfgResult.rules_file = NULL; 761 } 762 if (cfgResult.model) 763 { 764 trySetString(&settings.model, cfgResult.model, FROM_CONFIG); 765 cfgResult.model = NULL; 766 } 767 if (cfgResult.layout) 768 { 769 trySetString(&settings.layout, cfgResult.layout, FROM_CONFIG); 770 cfgResult.layout = NULL; 771 } 772 if (cfgResult.variant) 773 { 774 trySetString(&settings.variant, cfgResult.variant, FROM_CONFIG); 775 cfgResult.variant = NULL; 776 } 777 if (cfgResult.options) 778 { 779 addStringToOptions(cfgResult.options, &options); 780 cfgResult.options = NULL; 781 } 782 if (cfgResult.keymap) 783 { 784 trySetString(&settings.keymap, cfgResult.keymap, FROM_CONFIG); 785 cfgResult.keymap = NULL; 786 } 787 if (cfgResult.keycodes) 788 { 789 trySetString(&settings.keycodes, cfgResult.keycodes, FROM_CONFIG); 790 cfgResult.keycodes = NULL; 791 } 792 if (cfgResult.geometry) 793 { 794 trySetString(&settings.geometry, cfgResult.geometry, FROM_CONFIG); 795 cfgResult.geometry = NULL; 796 } 797 if (cfgResult.symbols) 798 { 799 trySetString(&settings.symbols, cfgResult.symbols, FROM_CONFIG); 800 cfgResult.symbols = NULL; 801 } 802 if (cfgResult.types) 803 { 804 trySetString(&settings.types, cfgResult.types, FROM_CONFIG); 805 cfgResult.types = NULL; 806 } 807 if (cfgResult.compat) 808 { 809 trySetString(&settings.compat, cfgResult.compat, FROM_CONFIG); 810 cfgResult.compat = NULL; 811 } 812 if (verbose > 5) 813 { 814 MSG("After config file:\n"); 815 dumpNames(True, True); 816 } 817 return True; 818} 819 820static XkbRF_RulesPtr 821tryLoadRules(char *name, char *locale, Bool wantDesc, Bool wantRules) 822{ 823 XkbRF_RulesPtr rules = NULL; 824 VMSG1(7, "Trying to load rules file %s...\n", name); 825 rules = XkbRF_Load(name, locale, wantDesc, wantRules); 826 if (rules) 827 { 828 VMSG(7, "Success.\n"); 829 } 830 return rules; 831} 832 833/** 834 * If any of model, layout, variant or options is specified, then compile the 835 * options into the 836 * 837 * @return True on success or false otherwise. 838 */ 839static Bool 840applyRules(void) 841{ 842 int i; 843 char *rfName; 844 XkbRF_RulesPtr rules = NULL; 845 846 if (settings.model.src || settings.layout.src || settings.variant.src 847 || options.item) 848 { 849 char buf[PATH_MAX]; 850 XkbComponentNamesRec rnames; 851 852 if (settings.variant.src < settings.layout.src) 853 settings.variant.value = NULL; 854 855 rdefs.model = settings.model.value; 856 rdefs.layout = settings.layout.value; 857 rdefs.variant = settings.variant.value; 858 if (options.item) 859 rdefs.options = 860 stringFromOptions(rdefs.options, &options); 861 862 if (settings.rules.src) 863 rfName = settings.rules.value; 864 else 865 rfName = (char *) DFLT_XKB_RULES_FILE; 866 867 if (rfName[0] == '/') 868 { 869 rules = tryLoadRules(rfName, settings.locale.value, True, True); 870 } 871 else 872 { 873 /* try to load rules files from all include paths until the first 874 * we succeed with */ 875 for (i = 0; (i < inclPath.num) && (!rules); i++) 876 { 877 if (snprintf(buf, PATH_MAX, "%s/rules/%s", 878 inclPath.item[i], rfName) >= PATH_MAX) 879 { 880 VMSG2(0, "Path too long (%s/rules/%s). Ignored.\n", 881 inclPath.item[i], rfName); 882 continue; 883 } 884 rules = tryLoadRules(buf, settings.locale.value, True, True); 885 } 886 } 887 if (!rules) 888 { 889 ERR1("Couldn't find rules file (%s) \n", rfName); 890 return False; 891 } 892 /* Let the rules file to the magic, then update the svValues with 893 * those returned after processing the rules */ 894 XkbRF_GetComponents(rules, &rdefs, &rnames); 895 if (rnames.keycodes) 896 { 897 trySetString(&settings.keycodes, rnames.keycodes, FROM_RULES); 898 rnames.keycodes = NULL; 899 } 900 if (rnames.symbols) 901 { 902 trySetString(&settings.symbols, rnames.symbols, FROM_RULES); 903 rnames.symbols = NULL; 904 } 905 if (rnames.types) 906 { 907 trySetString(&settings.types, rnames.types, FROM_RULES); 908 rnames.types = NULL; 909 } 910 if (rnames.compat) 911 { 912 trySetString(&settings.compat, rnames.compat, FROM_RULES); 913 rnames.compat = NULL; 914 } 915 if (rnames.geometry) 916 { 917 trySetString(&settings.geometry, rnames.geometry, FROM_RULES); 918 rnames.geometry = NULL; 919 } 920 if (rnames.keymap) 921 { 922 trySetString(&settings.keymap, rnames.keymap, FROM_RULES); 923 rnames.keymap = NULL; 924 } 925 if (verbose > 6) 926 { 927 MSG1("Applied rules from %s:\n", rfName); 928 dumpNames(True, False); 929 } 930 } 931 else if (verbose > 6) 932 { 933 MSG("No rules variables specified. Rules file ignored\n"); 934 } 935 return True; 936} 937 938/* Primitive sanity check - filter out 'map names' (inside parenthesis) */ 939/* that can confuse xkbcomp parser */ 940static Bool 941checkName(char *name, const char *string) 942{ 943 char *i = name, *opar = NULL; 944 Bool ret = True; 945 946 if (!name) 947 return True; 948 949 while (*i) 950 { 951 if (opar == NULL) 952 { 953 if (*i == '(') 954 opar = i; 955 } 956 else 957 { 958 if ((*i == '(') || (*i == '|') || (*i == '+')) 959 { 960 ret = False; 961 break; 962 } 963 if (*i == ')') 964 opar = NULL; 965 } 966 i++; 967 } 968 if (opar) 969 ret = False; 970 if (!ret) 971 { 972 char c; 973 int n = 1; 974 for (i = opar + 1; *i && n; i++) 975 { 976 if (*i == '(') 977 n++; 978 if (*i == ')') 979 n--; 980 } 981 if (*i) 982 i++; 983 c = *i; 984 *i = '\0'; 985 ERR1("Illegal map name '%s' ", opar); 986 *i = c; 987 ERR2("in %s name '%s'\n", string, name); 988 } 989 return ret; 990} 991 992static void 993printKeymap(void) 994{ 995 MSG("xkb_keymap {\n"); 996 if (settings.keycodes.value) 997 MSG1("\txkb_keycodes { include \"%s\"\t};\n", settings.keycodes.value); 998 if (settings.types.value) 999 MSG1("\txkb_types { include \"%s\"\t};\n", settings.types.value); 1000 if (settings.compat.value) 1001 MSG1("\txkb_compat { include \"%s\"\t};\n", settings.compat.value); 1002 if (settings.symbols.value) 1003 MSG1("\txkb_symbols { include \"%s\"\t};\n", settings.symbols.value); 1004 if (settings.geometry.value) 1005 MSG1("\txkb_geometry { include \"%s\"\t};\n", settings.geometry.value); 1006 MSG("};\n"); 1007} 1008 1009static Bool 1010applyComponentNames(void) 1011{ 1012 if (!checkName(settings.types.value, "types")) 1013 return False; 1014 if (!checkName(settings.compat.value, "compat")) 1015 return False; 1016 if (!checkName(settings.symbols.value, "symbols")) 1017 return False; 1018 if (!checkName(settings.keycodes.value, "keycodes")) 1019 return False; 1020 if (!checkName(settings.geometry.value, "geometry")) 1021 return False; 1022 if (!checkName(settings.keymap.value, "keymap")) 1023 return False; 1024 1025 if (verbose > 5) 1026 { 1027 MSG("Trying to build keymap using the following components:\n"); 1028 dumpNames(False, True); 1029 } 1030 /* Upload the new description to the server. */ 1031 if (dpy && !print && !query) 1032 { 1033 XkbComponentNamesRec cmdNames = { 1034 .keymap = settings.keymap.value, 1035 .keycodes = settings.keycodes.value, 1036 .types = settings.types.value, 1037 .compat = settings.compat.value, 1038 .symbols = settings.symbols.value, 1039 .geometry = settings.geometry.value 1040 }; 1041 1042 xkb = XkbGetKeyboardByName(dpy, deviceSpec, &cmdNames, 1043 XkbGBN_AllComponentsMask, 1044 XkbGBN_AllComponentsMask & 1045 (~XkbGBN_GeometryMask), True); 1046 if (!xkb) 1047 { 1048 ERR("Error loading new keyboard description\n"); 1049 return False; 1050 } 1051 /* update the XKB root property */ 1052 if (settings.rules.value && (rdefs.model || rdefs.layout)) 1053 { 1054 if (!XkbRF_SetNamesProp(dpy, settings.rules.value, &rdefs)) 1055 { 1056 VMSG(0, "Error updating the XKB names property\n"); 1057 } 1058 } 1059 } 1060 if (print) 1061 { 1062 printKeymap(); 1063 } 1064 if (query) 1065 { 1066 dumpNames(True, False); 1067 } 1068 return True; 1069} 1070 1071static Bool 1072is_xwayland(void) 1073{ 1074 /* Code copied from xisxwayland.c */ 1075 Bool rc = False; 1076 XRRScreenResources *resources = NULL; 1077 XRROutputInfo *output = NULL; 1078 int opcode, event_base, error_base, major, minor; 1079 1080 /* With Xwayland 23.1 and above, the definitive way of checking for an 1081 * Xwayland server is to check for the "XWAYLAND" extension. 1082 */ 1083 if (XQueryExtension(dpy, "XWAYLAND", &opcode, &event_base, &error_base)) { 1084 rc = True; 1085 goto out; 1086 } 1087 1088 /* For previous versions of Xwayland, there is no definitive way of checking 1089 * for an Xwayland server, but the two working methods are: 1090 * - RandR output names in Xwayland are XWAYLAND0, XWAYLAND1, etc. 1091 * - XI devices are xwayland-pointer:10, xwayland-keyboard:11 1092 * Let's go with the XRandR check here because it's slightly less 1093 * code to write. 1094 */ 1095 1096 if (!XRRQueryExtension(dpy, &event_base, &error_base) || 1097 !XRRQueryVersion(dpy, &major, &minor)) { 1098 /* e.g. Xnest, but definitely not Xwayland */ 1099 goto out; 1100 } 1101 if (major < 1 || (major == 1 && minor < 3)) { 1102 goto out; 1103 } 1104 1105 resources = XRRGetScreenResourcesCurrent(dpy, DefaultRootWindow(dpy)); 1106 if (!resources || resources->noutput < 1) { 1107 goto out; 1108 } 1109 1110 output = XRRGetOutputInfo(dpy, resources, resources->outputs[0]); 1111 if (!output) { 1112 goto out; 1113 } 1114 1115 if (strncmp(output->name, "XWAYLAND", 8) == 0) 1116 rc = True; 1117 1118 XRRFreeOutputInfo(output); 1119out: 1120 if (resources) 1121 XRRFreeScreenResources(resources); 1122 1123 return rc; 1124} 1125 1126int 1127main(int argc, char **argv) 1128{ 1129 if ((!parseArgs(argc, argv)) || (!getDisplay(argc, argv))) 1130 exit(1); 1131 1132 if (is_xwayland()) 1133 ERR("WARNING: Running setxkbmap against an Xwayland server\n"); 1134 1135 settings.locale.value = setlocale(LC_ALL, settings.locale.value); 1136 settings.locale.src = FROM_SERVER; 1137 VMSG1(7, "locale is %s\n", settings.locale.value); 1138 if (dpy) 1139 getServerValues(); 1140 if (settings.config.value && (!applyConfig(settings.config.value))) 1141 exit(3); 1142 if (!applyRules()) 1143 exit(4); 1144 if (!applyComponentNames()) 1145 exit(5); 1146 if (dpy) 1147 XCloseDisplay(dpy); 1148 exit(0); 1149} 1150