1/************************************************************ 2Copyright (c) 1993 by Silicon Graphics Computer Systems, Inc. 3 4Permission to use, copy, modify, and distribute this 5software and its documentation for any purpose and without 6fee is hereby granted, provided that the above copyright 7notice appear in all copies and that both that copyright 8notice and this permission notice appear in supporting 9documentation, and that the name of Silicon Graphics not be 10used in advertising or publicity pertaining to distribution 11of the software without specific prior written permission. 12Silicon Graphics makes no representation about the suitability 13of this software for any purpose. It is provided "as is" 14without any express or implied warranty. 15 16SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS 17SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 18AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON 19GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL 20DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 21DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 22OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH 23THE 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 /* 49 * If XKM_OUTPUT_DIR specifies a path without a leading slash, it is 50 * relative to the top-level XKB configuration directory. 51 * Making the server write to a subdirectory of that directory 52 * requires some work in the general case (install procedure 53 * has to create links to /var or somesuch on many machines), 54 * so we just compile into /usr/tmp for now. 55 */ 56#ifndef XKM_OUTPUT_DIR 57#define XKM_OUTPUT_DIR "compiled/" 58#endif 59 60#define PRE_ERROR_MSG "\"The XKEYBOARD keymap compiler (xkbcomp) reports:\"" 61#define ERROR_PREFIX "\"> \"" 62#define POST_ERROR_MSG1 "\"Errors from xkbcomp are not fatal to the X server\"" 63#define POST_ERROR_MSG2 "\"End of messages from xkbcomp\"" 64 65#if defined(WIN32) 66#define PATHSEPARATOR "\\" 67#else 68#define PATHSEPARATOR "/" 69#endif 70 71#ifdef WIN32 72 73#include <X11/Xwindows.h> 74const char* 75Win32TempDir() 76{ 77 static char buffer[PATH_MAX]; 78 if (GetTempPath(sizeof(buffer), buffer)) 79 { 80 int len; 81 buffer[sizeof(buffer)-1] = 0; 82 len = strlen(buffer); 83 if (len > 0) 84 if (buffer[len-1] == '\\') 85 buffer[len-1] = 0; 86 return buffer; 87 } 88 if (getenv("TEMP") != NULL) 89 return getenv("TEMP"); 90 else if (getenv("TMP") != NULL) 91 return getenv("TEMP"); 92 else 93 return "/tmp"; 94} 95 96int 97Win32System(const char *cmdline) 98{ 99 STARTUPINFO si; 100 PROCESS_INFORMATION pi; 101 DWORD dwExitCode; 102 char *cmd = strdup(cmdline); 103 104 ZeroMemory( &si, sizeof(si) ); 105 si.cb = sizeof(si); 106 ZeroMemory( &pi, sizeof(pi) ); 107 108 if (!CreateProcess(NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) 109 { 110 LPVOID buffer; 111 if (!FormatMessage( 112 FORMAT_MESSAGE_ALLOCATE_BUFFER | 113 FORMAT_MESSAGE_FROM_SYSTEM | 114 FORMAT_MESSAGE_IGNORE_INSERTS, 115 NULL, 116 GetLastError(), 117 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 118 (LPTSTR) &buffer, 119 0, 120 NULL )) 121 { 122 ErrorF("[xkb] Starting '%s' failed!\n", cmdline); 123 } 124 else 125 { 126 ErrorF("[xkb] Starting '%s' failed: %s", cmdline, (char *)buffer); 127 LocalFree(buffer); 128 } 129 130 free(cmd); 131 return -1; 132 } 133 /* Wait until child process exits. */ 134 WaitForSingleObject( pi.hProcess, INFINITE ); 135 136 GetExitCodeProcess( pi.hProcess, &dwExitCode); 137 138 /* Close process and thread handles. */ 139 CloseHandle( pi.hProcess ); 140 CloseHandle( pi.hThread ); 141 free(cmd); 142 143 return dwExitCode; 144} 145#undef System 146#define System(x) Win32System(x) 147#endif 148 149static void 150OutputDirectory( 151 char* outdir, 152 size_t size) 153{ 154#ifndef WIN32 155 /* Can we write an xkm and then open it too? */ 156 if (access(XKM_OUTPUT_DIR, W_OK | X_OK) == 0 && (strlen(XKM_OUTPUT_DIR) < size)) 157 { 158 (void) strcpy (outdir, XKM_OUTPUT_DIR); 159 } else 160#else 161 if (strlen(Win32TempDir()) + 1 < size) 162 { 163 (void) strcpy(outdir, Win32TempDir()); 164 (void) strcat(outdir, "\\"); 165 } else 166#endif 167 if (strlen("/tmp/") < size) 168 { 169 (void) strcpy (outdir, "/tmp/"); 170 } 171} 172 173static Bool 174XkbDDXCompileKeymapByNames( XkbDescPtr xkb, 175 XkbComponentNamesPtr names, 176 unsigned want, 177 unsigned need, 178 char * nameRtrn, 179 int nameRtrnLen) 180{ 181 FILE * out; 182 char *buf = NULL, keymap[PATH_MAX], xkm_output_dir[PATH_MAX]; 183 184 const char *emptystring = ""; 185 char *xkbbasedirflag = NULL; 186 const char *xkbbindir = emptystring; 187 const char *xkbbindirsep = emptystring; 188 189#ifdef WIN32 190 /* WIN32 has no popen. The input must be stored in a file which is 191 used as input for xkbcomp. xkbcomp does not read from stdin. */ 192 char tmpname[PATH_MAX]; 193 const char *xkmfile = tmpname; 194#else 195 const char *xkmfile = "-"; 196#endif 197 198 snprintf(keymap, sizeof(keymap), "server-%s", display); 199 200 OutputDirectory(xkm_output_dir, sizeof(xkm_output_dir)); 201 202#ifdef WIN32 203 strcpy(tmpname, Win32TempDir()); 204 strcat(tmpname, "\\xkb_XXXXXX"); 205 (void) mktemp(tmpname); 206#endif 207 208 if (XkbBaseDirectory != NULL) { 209 if (asprintf(&xkbbasedirflag, "\"-R%s\"", XkbBaseDirectory) == -1) 210 xkbbasedirflag = NULL; 211 } 212 213 if (XkbBinDirectory != NULL) { 214 int ld = strlen(XkbBinDirectory); 215 int lps = strlen(PATHSEPARATOR); 216 217 xkbbindir = XkbBinDirectory; 218 219 if ((ld >= lps) && 220 (strcmp(xkbbindir + ld - lps, PATHSEPARATOR) != 0)) { 221 xkbbindirsep = PATHSEPARATOR; 222 } 223 } 224 225 if (asprintf(&buf, 226 "\"%s%sxkbcomp\" -w %d %s -xkm \"%s\" " 227 "-em1 %s -emp %s -eml %s \"%s%s.xkm\"", 228 xkbbindir, xkbbindirsep, 229 ((xkbDebugFlags < 2) ? 1 : 230 ((xkbDebugFlags > 10) ? 10 : (int) xkbDebugFlags)), 231 xkbbasedirflag ? xkbbasedirflag : "", xkmfile, 232 PRE_ERROR_MSG, ERROR_PREFIX, POST_ERROR_MSG1, 233 xkm_output_dir, keymap) == -1) 234 buf = NULL; 235 236 free(xkbbasedirflag); 237 238 if (!buf) { 239 LogMessage(X_ERROR, "XKB: Could not invoke xkbcomp: not enough memory\n"); 240 return FALSE; 241 } 242 243#ifndef WIN32 244 out= Popen(buf,"w"); 245#else 246 out= fopen(tmpname, "w"); 247#endif 248 249 if (out!=NULL) { 250#ifdef DEBUG 251 if (xkbDebugFlags) { 252 ErrorF("[xkb] XkbDDXCompileKeymapByNames compiling keymap:\n"); 253 XkbWriteXKBKeymapForNames(stderr,names,xkb,want,need); 254 } 255#endif 256 XkbWriteXKBKeymapForNames(out,names,xkb,want,need); 257#ifndef WIN32 258 if (Pclose(out)==0) 259#else 260 if (fclose(out)==0 && System(buf) >= 0) 261#endif 262 { 263 if (xkbDebugFlags) 264 DebugF("[xkb] xkb executes: %s\n",buf); 265 if (nameRtrn) { 266 strncpy(nameRtrn,keymap,nameRtrnLen); 267 nameRtrn[nameRtrnLen-1]= '\0'; 268 } 269 free(buf); 270 return TRUE; 271 } 272 else 273 LogMessage(X_ERROR, "Error compiling keymap (%s)\n", keymap); 274#ifdef WIN32 275 /* remove the temporary file */ 276 unlink(tmpname); 277#endif 278 } 279 else { 280#ifndef WIN32 281 LogMessage(X_ERROR, "XKB: Could not invoke xkbcomp\n"); 282#else 283 LogMessage(X_ERROR, "Could not open file %s\n", tmpname); 284#endif 285 } 286 if (nameRtrn) 287 nameRtrn[0]= '\0'; 288 free(buf); 289 return FALSE; 290} 291 292static FILE * 293XkbDDXOpenConfigFile(char *mapName,char *fileNameRtrn,int fileNameRtrnLen) 294{ 295char buf[PATH_MAX],xkm_output_dir[PATH_MAX]; 296FILE * file; 297 298 buf[0]= '\0'; 299 if (mapName!=NULL) { 300 OutputDirectory(xkm_output_dir, sizeof(xkm_output_dir)); 301 if ((XkbBaseDirectory!=NULL)&&(xkm_output_dir[0]!='/') 302#ifdef WIN32 303 &&(!isalpha(xkm_output_dir[0]) || xkm_output_dir[1]!=':') 304#endif 305 ) { 306 if (strlen(XkbBaseDirectory)+strlen(xkm_output_dir) 307 +strlen(mapName)+6 <= PATH_MAX) 308 { 309 sprintf(buf,"%s/%s%s.xkm",XkbBaseDirectory, 310 xkm_output_dir,mapName); 311 } 312 } 313 else if (strlen(xkm_output_dir)+strlen(mapName)+5 <= PATH_MAX) 314 sprintf(buf,"%s%s.xkm",xkm_output_dir,mapName); 315 if (buf[0] != '\0') 316 file= fopen(buf,"rb"); 317 else file= NULL; 318 } 319 else file= NULL; 320 if ((fileNameRtrn!=NULL)&&(fileNameRtrnLen>0)) { 321 strncpy(fileNameRtrn,buf,fileNameRtrnLen); 322 buf[fileNameRtrnLen-1]= '\0'; 323 } 324 return file; 325} 326 327unsigned 328XkbDDXLoadKeymapByNames( DeviceIntPtr keybd, 329 XkbComponentNamesPtr names, 330 unsigned want, 331 unsigned need, 332 XkbDescPtr * xkbRtrn, 333 char * nameRtrn, 334 int nameRtrnLen) 335{ 336XkbDescPtr xkb; 337FILE * file; 338char fileName[PATH_MAX]; 339unsigned missing; 340 341 *xkbRtrn = NULL; 342 if ((keybd==NULL)||(keybd->key==NULL)||(keybd->key->xkbInfo==NULL)) 343 xkb= NULL; 344 else xkb= keybd->key->xkbInfo->desc; 345 if ((names->keycodes==NULL)&&(names->types==NULL)&& 346 (names->compat==NULL)&&(names->symbols==NULL)&& 347 (names->geometry==NULL)) { 348 LogMessage(X_ERROR, "XKB: No components provided for device %s\n", 349 keybd->name ? keybd->name : "(unnamed keyboard)"); 350 return 0; 351 } 352 else if (!XkbDDXCompileKeymapByNames(xkb,names,want,need, 353 nameRtrn,nameRtrnLen)){ 354 LogMessage(X_ERROR, "XKB: Couldn't compile keymap\n"); 355 return 0; 356 } 357 file= XkbDDXOpenConfigFile(nameRtrn,fileName,PATH_MAX); 358 if (file==NULL) { 359 LogMessage(X_ERROR, "Couldn't open compiled keymap file %s\n",fileName); 360 return 0; 361 } 362 missing= XkmReadFile(file,need,want,xkbRtrn); 363 if (*xkbRtrn==NULL) { 364 LogMessage(X_ERROR, "Error loading keymap %s\n",fileName); 365 fclose(file); 366 (void) unlink (fileName); 367 return 0; 368 } 369 else { 370 DebugF("Loaded XKB keymap %s, defined=0x%x\n",fileName,(*xkbRtrn)->defined); 371 } 372 fclose(file); 373 (void) unlink (fileName); 374 return (need|want)&(~missing); 375} 376 377Bool 378XkbDDXNamesFromRules( DeviceIntPtr keybd, 379 char * rules_name, 380 XkbRF_VarDefsPtr defs, 381 XkbComponentNamesPtr names) 382{ 383char buf[PATH_MAX]; 384FILE * file; 385Bool complete; 386XkbRF_RulesPtr rules; 387 388 if (!rules_name) 389 return FALSE; 390 391 if (strlen(XkbBaseDirectory) + strlen(rules_name) + 8 > PATH_MAX) { 392 LogMessage(X_ERROR, "XKB: Rules name is too long\n"); 393 return FALSE; 394 } 395 sprintf(buf,"%s/rules/%s", XkbBaseDirectory, rules_name); 396 397 file = fopen(buf, "r"); 398 if (!file) { 399 LogMessage(X_ERROR, "XKB: Couldn't open rules file %s\n", buf); 400 return FALSE; 401 } 402 403 rules = XkbRF_Create(); 404 if (!rules) { 405 LogMessage(X_ERROR, "XKB: Couldn't create rules struct\n"); 406 fclose(file); 407 return FALSE; 408 } 409 410 if (!XkbRF_LoadRules(file, rules)) { 411 LogMessage(X_ERROR, "XKB: Couldn't parse rules file %s\n", rules_name); 412 fclose(file); 413 XkbRF_Free(rules,TRUE); 414 return FALSE; 415 } 416 417 memset(names, 0, sizeof(*names)); 418 complete = XkbRF_GetComponents(rules,defs,names); 419 fclose(file); 420 XkbRF_Free(rules, TRUE); 421 422 if (!complete) 423 LogMessage(X_ERROR, "XKB: Rules returned no components\n"); 424 425 return complete; 426} 427 428static Bool 429XkbRMLVOtoKcCGST(DeviceIntPtr dev, XkbRMLVOSet *rmlvo, XkbComponentNamesPtr kccgst) 430{ 431 XkbRF_VarDefsRec mlvo; 432 433 mlvo.model = rmlvo->model; 434 mlvo.layout = rmlvo->layout; 435 mlvo.variant = rmlvo->variant; 436 mlvo.options = rmlvo->options; 437 438 return XkbDDXNamesFromRules(dev, rmlvo->rules, &mlvo, kccgst); 439} 440 441/** 442 * Compile the given RMLVO keymap and return it. Returns the XkbDescPtr on 443 * success or NULL on failure. If the components compiled are not a superset 444 * or equal to need, the compiliation is treated as failure. 445 */ 446static XkbDescPtr 447XkbCompileKeymapForDevice(DeviceIntPtr dev, XkbRMLVOSet *rmlvo, int need) 448{ 449 XkbDescPtr xkb = NULL; 450 unsigned int provided; 451 XkbComponentNamesRec kccgst = {0}; 452 char name[PATH_MAX]; 453 454 if (XkbRMLVOtoKcCGST(dev, rmlvo, &kccgst)) { 455 provided = XkbDDXLoadKeymapByNames(dev, &kccgst, XkmAllIndicesMask, need, 456 &xkb, name, PATH_MAX); 457 if ((need & provided) != need) { 458 if (xkb) { 459 XkbFreeKeyboard(xkb, 0, TRUE); 460 xkb = NULL; 461 } 462 } 463 } 464 465 XkbFreeComponentNames(&kccgst, FALSE); 466 return xkb; 467} 468 469XkbDescPtr 470XkbCompileKeymap(DeviceIntPtr dev, XkbRMLVOSet *rmlvo) 471{ 472 XkbDescPtr xkb; 473 unsigned int need; 474 475 if (!dev || !rmlvo) { 476 LogMessage(X_ERROR, "XKB: No device or RMLVO specified\n"); 477 return NULL; 478 } 479 480 /* These are the components we really really need */ 481 need = XkmSymbolsMask | XkmCompatMapMask | XkmTypesMask | 482 XkmKeyNamesMask | XkmVirtualModsMask; 483 484 485 xkb = XkbCompileKeymapForDevice(dev, rmlvo, need); 486 487 if (!xkb) { 488 XkbRMLVOSet dflts; 489 490 /* we didn't get what we really needed. And that will likely leave 491 * us with a keyboard that doesn't work. Use the defaults instead */ 492 LogMessage(X_ERROR, "XKB: Failed to load keymap. Loading default " 493 "keymap instead.\n"); 494 495 XkbGetRulesDflts(&dflts); 496 497 xkb = XkbCompileKeymapForDevice(dev, &dflts, 0); 498 499 XkbFreeRMLVOSet(&dflts, FALSE); 500 } 501 502 return xkb; 503} 504