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