ddxLoad.c (15314B)
1 /************************************************************ 2 Copyright (c) 1993 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_DIX_CONFIG_H 28 #include <dix-config.h> 29 #endif 30 31 #include <xkb-config.h> 32 33 #include <stdio.h> 34 #include <ctype.h> 35 #include <X11/X.h> 36 #include <X11/Xos.h> 37 #include <X11/Xproto.h> 38 #include <X11/keysym.h> 39 #include <X11/extensions/XKM.h> 40 #include "inputstr.h" 41 #include "scrnintstr.h" 42 #include "windowstr.h" 43 #define XKBSRV_NEED_FILE_FUNCS 44 #include <xkbsrv.h> 45 #include <X11/extensions/XI.h> 46 #include "xkb.h" 47 48 #define PRE_ERROR_MSG "\"The XKEYBOARD keymap compiler (xkbcomp) reports:\"" 49 #define ERROR_PREFIX "\"> \"" 50 #define POST_ERROR_MSG1 "\"Errors from xkbcomp are not fatal to the X server\"" 51 #define POST_ERROR_MSG2 "\"End of messages from xkbcomp\"" 52 53 #if defined(WIN32) 54 #define PATHSEPARATOR "\\" 55 #else 56 #define PATHSEPARATOR "/" 57 #endif 58 59 static unsigned 60 LoadXKM(unsigned want, unsigned need, const char *keymap, XkbDescPtr *xkbRtrn); 61 62 static void 63 OutputDirectory(char *outdir, size_t size) 64 { 65 const char *directory = NULL; 66 const char *pathsep = ""; 67 int r = -1; 68 69 #ifndef WIN32 70 /* Can we write an xkm and then open it too? */ 71 if (access(XKM_OUTPUT_DIR, W_OK | X_OK) == 0) { 72 directory = XKM_OUTPUT_DIR; 73 if (XKM_OUTPUT_DIR[strlen(XKM_OUTPUT_DIR) - 1] != '/') 74 pathsep = "/"; 75 } 76 #else 77 directory = Win32TempDir(); 78 pathsep = "\\"; 79 #endif 80 81 if (directory) 82 r = snprintf(outdir, size, "%s%s", directory, pathsep); 83 if (r < 0 || r >= size) { 84 assert(strlen("/tmp/") < size); 85 strcpy(outdir, "/tmp/"); 86 } 87 } 88 89 /** 90 * Callback invoked by XkbRunXkbComp. Write to out to talk to xkbcomp. 91 */ 92 typedef void (*xkbcomp_buffer_callback)(FILE *out, void *userdata); 93 94 /** 95 * Start xkbcomp, let the callback write into xkbcomp's stdin. When done, 96 * return a strdup'd copy of the file name we've written to. 97 */ 98 static char * 99 RunXkbComp(xkbcomp_buffer_callback callback, void *userdata) 100 { 101 FILE *out; 102 char *buf = NULL, keymap[PATH_MAX], xkm_output_dir[PATH_MAX]; 103 104 const char *emptystring = ""; 105 char *xkbbasedirflag = NULL; 106 const char *xkbbindir = emptystring; 107 const char *xkbbindirsep = emptystring; 108 109 #ifdef WIN32 110 /* WIN32 has no popen. The input must be stored in a file which is 111 used as input for xkbcomp. xkbcomp does not read from stdin. */ 112 char tmpname[PATH_MAX]; 113 const char *xkmfile = tmpname; 114 #else 115 const char *xkmfile = "-"; 116 #endif 117 118 snprintf(keymap, sizeof(keymap), "server-%s", display); 119 120 OutputDirectory(xkm_output_dir, sizeof(xkm_output_dir)); 121 122 #ifdef WIN32 123 strcpy(tmpname, Win32TempDir()); 124 strcat(tmpname, "\\xkb_XXXXXX"); 125 (void) mktemp(tmpname); 126 #endif 127 128 if (XkbBaseDirectory != NULL) { 129 if (asprintf(&xkbbasedirflag, "\"-R%s\"", XkbBaseDirectory) == -1) 130 xkbbasedirflag = NULL; 131 } 132 133 if (XkbBinDirectory != NULL) { 134 int ld = strlen(XkbBinDirectory); 135 int lps = strlen(PATHSEPARATOR); 136 137 xkbbindir = XkbBinDirectory; 138 139 if ((ld >= lps) && (strcmp(xkbbindir + ld - lps, PATHSEPARATOR) != 0)) { 140 xkbbindirsep = PATHSEPARATOR; 141 } 142 } 143 144 if (asprintf(&buf, 145 "\"%s%sxkbcomp\" -w %d %s -xkm \"%s\" " 146 "-em1 %s -emp %s -eml %s \"%s%s.xkm\"", 147 xkbbindir, xkbbindirsep, 148 ((xkbDebugFlags < 2) ? 1 : 149 ((xkbDebugFlags > 10) ? 10 : (int) xkbDebugFlags)), 150 xkbbasedirflag ? xkbbasedirflag : "", xkmfile, 151 PRE_ERROR_MSG, ERROR_PREFIX, POST_ERROR_MSG1, 152 xkm_output_dir, keymap) == -1) 153 buf = NULL; 154 155 free(xkbbasedirflag); 156 157 if (!buf) { 158 LogMessage(X_ERROR, 159 "XKB: Could not invoke xkbcomp: not enough memory\n"); 160 return NULL; 161 } 162 163 #ifndef WIN32 164 out = Popen(buf, "w"); 165 #else 166 out = fopen(tmpname, "w"); 167 #endif 168 169 if (out != NULL) { 170 /* Now write to xkbcomp */ 171 (*callback)(out, userdata); 172 173 #ifndef WIN32 174 if (Pclose(out) == 0) 175 #else 176 if (fclose(out) == 0 && System(buf) >= 0) 177 #endif 178 { 179 if (xkbDebugFlags) 180 DebugF("[xkb] xkb executes: %s\n", buf); 181 free(buf); 182 #ifdef WIN32 183 unlink(tmpname); 184 #endif 185 return xnfstrdup(keymap); 186 } 187 else { 188 LogMessage(X_ERROR, "Error compiling keymap (%s) executing '%s'\n", 189 keymap, buf); 190 } 191 #ifdef WIN32 192 /* remove the temporary file */ 193 unlink(tmpname); 194 #endif 195 } 196 else { 197 #ifndef WIN32 198 LogMessage(X_ERROR, "XKB: Could not invoke xkbcomp\n"); 199 #else 200 LogMessage(X_ERROR, "Could not open file %s\n", tmpname); 201 #endif 202 } 203 free(buf); 204 return NULL; 205 } 206 207 typedef struct { 208 XkbDescPtr xkb; 209 XkbComponentNamesPtr names; 210 unsigned int want; 211 unsigned int need; 212 } XkbKeymapNamesCtx; 213 214 static void 215 xkb_write_keymap_for_names_cb(FILE *out, void *userdata) 216 { 217 XkbKeymapNamesCtx *ctx = userdata; 218 #ifdef DEBUG 219 if (xkbDebugFlags) { 220 ErrorF("[xkb] XkbDDXCompileKeymapByNames compiling keymap:\n"); 221 XkbWriteXKBKeymapForNames(stderr, ctx->names, ctx->xkb, ctx->want, ctx->need); 222 } 223 #endif 224 XkbWriteXKBKeymapForNames(out, ctx->names, ctx->xkb, ctx->want, ctx->need); 225 } 226 227 static Bool 228 XkbDDXCompileKeymapByNames(XkbDescPtr xkb, 229 XkbComponentNamesPtr names, 230 unsigned want, 231 unsigned need, char *nameRtrn, int nameRtrnLen) 232 { 233 char *keymap; 234 Bool rc = FALSE; 235 XkbKeymapNamesCtx ctx = { 236 .xkb = xkb, 237 .names = names, 238 .want = want, 239 .need = need 240 }; 241 242 keymap = RunXkbComp(xkb_write_keymap_for_names_cb, &ctx); 243 244 if (keymap) { 245 if(nameRtrn) 246 strlcpy(nameRtrn, keymap, nameRtrnLen); 247 248 free(keymap); 249 rc = TRUE; 250 } else if (nameRtrn) 251 *nameRtrn = '\0'; 252 253 return rc; 254 } 255 256 typedef struct { 257 const char *keymap; 258 size_t len; 259 } XkbKeymapString; 260 261 static void 262 xkb_write_keymap_string_cb(FILE *out, void *userdata) 263 { 264 XkbKeymapString *s = userdata; 265 fwrite(s->keymap, s->len, 1, out); 266 } 267 268 static unsigned int 269 XkbDDXLoadKeymapFromString(DeviceIntPtr keybd, 270 const char *keymap, int keymap_length, 271 unsigned int want, 272 unsigned int need, 273 XkbDescPtr *xkbRtrn) 274 { 275 unsigned int have; 276 char *map_name; 277 XkbKeymapString map = { 278 .keymap = keymap, 279 .len = keymap_length 280 }; 281 282 *xkbRtrn = NULL; 283 284 map_name = RunXkbComp(xkb_write_keymap_string_cb, &map); 285 if (!map_name) { 286 LogMessage(X_ERROR, "XKB: Couldn't compile keymap\n"); 287 return 0; 288 } 289 290 have = LoadXKM(want, need, map_name, xkbRtrn); 291 free(map_name); 292 293 return have; 294 } 295 296 static FILE * 297 XkbDDXOpenConfigFile(const char *mapName, char *fileNameRtrn, int fileNameRtrnLen) 298 { 299 char buf[PATH_MAX], xkm_output_dir[PATH_MAX]; 300 FILE *file; 301 302 buf[0] = '\0'; 303 if (mapName != NULL) { 304 OutputDirectory(xkm_output_dir, sizeof(xkm_output_dir)); 305 if ((XkbBaseDirectory != NULL) && (xkm_output_dir[0] != '/') 306 #ifdef WIN32 307 && (!isalpha(xkm_output_dir[0]) || xkm_output_dir[1] != ':') 308 #endif 309 ) { 310 if (snprintf(buf, PATH_MAX, "%s/%s%s.xkm", XkbBaseDirectory, 311 xkm_output_dir, mapName) >= PATH_MAX) 312 buf[0] = '\0'; 313 } 314 else { 315 if (snprintf(buf, PATH_MAX, "%s%s.xkm", xkm_output_dir, mapName) 316 >= PATH_MAX) 317 buf[0] = '\0'; 318 } 319 if (buf[0] != '\0') 320 file = fopen(buf, "rb"); 321 else 322 file = NULL; 323 } 324 else 325 file = NULL; 326 if ((fileNameRtrn != NULL) && (fileNameRtrnLen > 0)) { 327 strlcpy(fileNameRtrn, buf, fileNameRtrnLen); 328 } 329 return file; 330 } 331 332 static unsigned 333 LoadXKM(unsigned want, unsigned need, const char *keymap, XkbDescPtr *xkbRtrn) 334 { 335 FILE *file; 336 char fileName[PATH_MAX]; 337 unsigned missing; 338 339 file = XkbDDXOpenConfigFile(keymap, fileName, PATH_MAX); 340 if (file == NULL) { 341 LogMessage(X_ERROR, "Couldn't open compiled keymap file %s\n", 342 fileName); 343 return 0; 344 } 345 missing = XkmReadFile(file, need, want, xkbRtrn); 346 if (*xkbRtrn == NULL) { 347 LogMessage(X_ERROR, "Error loading keymap %s\n", fileName); 348 fclose(file); 349 (void) unlink(fileName); 350 return 0; 351 } 352 else { 353 DebugF("Loaded XKB keymap %s, defined=0x%x\n", fileName, 354 (*xkbRtrn)->defined); 355 } 356 fclose(file); 357 (void) unlink(fileName); 358 return (need | want) & (~missing); 359 } 360 361 unsigned 362 XkbDDXLoadKeymapByNames(DeviceIntPtr keybd, 363 XkbComponentNamesPtr names, 364 unsigned want, 365 unsigned need, 366 XkbDescPtr *xkbRtrn, char *nameRtrn, int nameRtrnLen) 367 { 368 XkbDescPtr xkb; 369 370 *xkbRtrn = NULL; 371 if ((keybd == NULL) || (keybd->key == NULL) || 372 (keybd->key->xkbInfo == NULL)) 373 xkb = NULL; 374 else 375 xkb = keybd->key->xkbInfo->desc; 376 if ((names->keycodes == NULL) && (names->types == NULL) && 377 (names->compat == NULL) && (names->symbols == NULL) && 378 (names->geometry == NULL)) { 379 LogMessage(X_ERROR, "XKB: No components provided for device %s\n", 380 keybd->name ? keybd->name : "(unnamed keyboard)"); 381 return 0; 382 } 383 else if (!XkbDDXCompileKeymapByNames(xkb, names, want, need, 384 nameRtrn, nameRtrnLen)) { 385 LogMessage(X_ERROR, "XKB: Couldn't compile keymap\n"); 386 return 0; 387 } 388 389 return LoadXKM(want, need, nameRtrn, xkbRtrn); 390 } 391 392 Bool 393 XkbDDXNamesFromRules(DeviceIntPtr keybd, 394 const char *rules_name, 395 XkbRF_VarDefsPtr defs, XkbComponentNamesPtr names) 396 { 397 char buf[PATH_MAX]; 398 FILE *file; 399 Bool complete; 400 XkbRF_RulesPtr rules; 401 402 if (!rules_name) 403 return FALSE; 404 405 if (snprintf(buf, PATH_MAX, "%s/rules/%s", XkbBaseDirectory, rules_name) 406 >= PATH_MAX) { 407 LogMessage(X_ERROR, "XKB: Rules name is too long\n"); 408 return FALSE; 409 } 410 411 file = fopen(buf, "r"); 412 if (!file) { 413 LogMessage(X_ERROR, "XKB: Couldn't open rules file %s\n", buf); 414 return FALSE; 415 } 416 417 rules = XkbRF_Create(); 418 if (!rules) { 419 LogMessage(X_ERROR, "XKB: Couldn't create rules struct\n"); 420 fclose(file); 421 return FALSE; 422 } 423 424 if (!XkbRF_LoadRules(file, rules)) { 425 LogMessage(X_ERROR, "XKB: Couldn't parse rules file %s\n", rules_name); 426 fclose(file); 427 XkbRF_Free(rules, TRUE); 428 return FALSE; 429 } 430 431 memset(names, 0, sizeof(*names)); 432 complete = XkbRF_GetComponents(rules, defs, names); 433 fclose(file); 434 XkbRF_Free(rules, TRUE); 435 436 if (!complete) 437 LogMessage(X_ERROR, "XKB: Rules returned no components\n"); 438 439 return complete; 440 } 441 442 static Bool 443 XkbRMLVOtoKcCGST(DeviceIntPtr dev, XkbRMLVOSet * rmlvo, 444 XkbComponentNamesPtr kccgst) 445 { 446 XkbRF_VarDefsRec mlvo; 447 448 mlvo.model = rmlvo->model; 449 mlvo.layout = rmlvo->layout; 450 mlvo.variant = rmlvo->variant; 451 mlvo.options = rmlvo->options; 452 453 return XkbDDXNamesFromRules(dev, rmlvo->rules, &mlvo, kccgst); 454 } 455 456 /** 457 * Compile the given RMLVO keymap and return it. Returns the XkbDescPtr on 458 * success or NULL on failure. If the components compiled are not a superset 459 * or equal to need, the compilation is treated as failure. 460 */ 461 static XkbDescPtr 462 XkbCompileKeymapForDevice(DeviceIntPtr dev, XkbRMLVOSet * rmlvo, int need) 463 { 464 XkbDescPtr xkb = NULL; 465 unsigned int provided; 466 XkbComponentNamesRec kccgst = { 0 }; 467 char name[PATH_MAX]; 468 469 if (XkbRMLVOtoKcCGST(dev, rmlvo, &kccgst)) { 470 provided = 471 XkbDDXLoadKeymapByNames(dev, &kccgst, XkmAllIndicesMask, need, &xkb, 472 name, PATH_MAX); 473 if ((need & provided) != need) { 474 if (xkb) { 475 XkbFreeKeyboard(xkb, 0, TRUE); 476 xkb = NULL; 477 } 478 } 479 } 480 481 XkbFreeComponentNames(&kccgst, FALSE); 482 return xkb; 483 } 484 485 static XkbDescPtr 486 KeymapOrDefaults(DeviceIntPtr dev, XkbDescPtr xkb) 487 { 488 XkbRMLVOSet dflts; 489 490 if (xkb) 491 return xkb; 492 493 /* we didn't get what we really needed. And that will likely leave 494 * us with a keyboard that doesn't work. Use the defaults instead */ 495 LogMessage(X_ERROR, "XKB: Failed to load keymap. Loading default " 496 "keymap instead.\n"); 497 498 XkbGetRulesDflts(&dflts); 499 500 xkb = XkbCompileKeymapForDevice(dev, &dflts, 0); 501 502 XkbFreeRMLVOSet(&dflts, FALSE); 503 504 return xkb; 505 } 506 507 508 XkbDescPtr 509 XkbCompileKeymap(DeviceIntPtr dev, XkbRMLVOSet * rmlvo) 510 { 511 XkbDescPtr xkb; 512 unsigned int need; 513 514 if (!dev || !rmlvo) { 515 LogMessage(X_ERROR, "XKB: No device or RMLVO specified\n"); 516 return NULL; 517 } 518 519 /* These are the components we really really need */ 520 need = XkmSymbolsMask | XkmCompatMapMask | XkmTypesMask | 521 XkmKeyNamesMask | XkmVirtualModsMask; 522 523 xkb = XkbCompileKeymapForDevice(dev, rmlvo, need); 524 525 return KeymapOrDefaults(dev, xkb); 526 } 527 528 XkbDescPtr 529 XkbCompileKeymapFromString(DeviceIntPtr dev, 530 const char *keymap, int keymap_length) 531 { 532 XkbDescPtr xkb; 533 unsigned int need, provided; 534 535 if (!dev || !keymap) { 536 LogMessage(X_ERROR, "XKB: No device or keymap specified\n"); 537 return NULL; 538 } 539 540 /* These are the components we really really need */ 541 need = XkmSymbolsMask | XkmCompatMapMask | XkmTypesMask | 542 XkmKeyNamesMask | XkmVirtualModsMask; 543 544 provided = 545 XkbDDXLoadKeymapFromString(dev, keymap, keymap_length, 546 XkmAllIndicesMask, need, &xkb); 547 if ((need & provided) != need) { 548 if (xkb) { 549 XkbFreeKeyboard(xkb, 0, TRUE); 550 xkb = NULL; 551 } 552 } 553 554 return KeymapOrDefaults(dev, xkb); 555 }