Commit | Line | Data |
---|---|---|
a09e091a JB |
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 | /* | |
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 | static void | |
72 | OutputDirectory(char *outdir, size_t size) | |
73 | { | |
74 | #ifndef WIN32 | |
75 | /* Can we write an xkm and then open it too? */ | |
76 | if (access(XKM_OUTPUT_DIR, W_OK | X_OK) == 0 && | |
77 | (strlen(XKM_OUTPUT_DIR) < size)) { | |
78 | (void) strcpy(outdir, XKM_OUTPUT_DIR); | |
79 | } | |
80 | else | |
81 | #else | |
82 | if (strlen(Win32TempDir()) + 1 < size) { | |
83 | (void) strcpy(outdir, Win32TempDir()); | |
84 | (void) strcat(outdir, "\\"); | |
85 | } | |
86 | else | |
87 | #endif | |
88 | if (strlen("/tmp/") < size) { | |
89 | (void) strcpy(outdir, "/tmp/"); | |
90 | } | |
91 | } | |
92 | ||
93 | static Bool | |
94 | XkbDDXCompileKeymapByNames(XkbDescPtr xkb, | |
95 | XkbComponentNamesPtr names, | |
96 | unsigned want, | |
97 | unsigned need, char *nameRtrn, int nameRtrnLen) | |
98 | { | |
99 | FILE *out; | |
100 | char *buf = NULL, keymap[PATH_MAX], xkm_output_dir[PATH_MAX]; | |
101 | ||
102 | const char *emptystring = ""; | |
103 | char *xkbbasedirflag = NULL; | |
104 | const char *xkbbindir = emptystring; | |
105 | const char *xkbbindirsep = emptystring; | |
106 | ||
107 | #ifdef WIN32 | |
108 | /* WIN32 has no popen. The input must be stored in a file which is | |
109 | used as input for xkbcomp. xkbcomp does not read from stdin. */ | |
110 | char tmpname[PATH_MAX]; | |
111 | const char *xkmfile = tmpname; | |
112 | #else | |
113 | const char *xkmfile = "-"; | |
114 | #endif | |
115 | ||
116 | snprintf(keymap, sizeof(keymap), "server-%s", display); | |
117 | ||
118 | OutputDirectory(xkm_output_dir, sizeof(xkm_output_dir)); | |
119 | ||
120 | #ifdef WIN32 | |
121 | strcpy(tmpname, Win32TempDir()); | |
122 | strcat(tmpname, "\\xkb_XXXXXX"); | |
123 | (void) mktemp(tmpname); | |
124 | #endif | |
125 | ||
126 | if (XkbBaseDirectory != NULL) { | |
127 | if (asprintf(&xkbbasedirflag, "\"-R%s\"", XkbBaseDirectory) == -1) | |
128 | xkbbasedirflag = NULL; | |
129 | } | |
130 | ||
131 | if (XkbBinDirectory != NULL) { | |
132 | int ld = strlen(XkbBinDirectory); | |
133 | int lps = strlen(PATHSEPARATOR); | |
134 | ||
135 | xkbbindir = XkbBinDirectory; | |
136 | ||
137 | if ((ld >= lps) && (strcmp(xkbbindir + ld - lps, PATHSEPARATOR) != 0)) { | |
138 | xkbbindirsep = PATHSEPARATOR; | |
139 | } | |
140 | } | |
141 | ||
142 | if (asprintf(&buf, | |
143 | "\"%s%sxkbcomp\" -w %d %s -xkm \"%s\" " | |
144 | "-em1 %s -emp %s -eml %s \"%s%s.xkm\"", | |
145 | xkbbindir, xkbbindirsep, | |
146 | ((xkbDebugFlags < 2) ? 1 : | |
147 | ((xkbDebugFlags > 10) ? 10 : (int) xkbDebugFlags)), | |
148 | xkbbasedirflag ? xkbbasedirflag : "", xkmfile, | |
149 | PRE_ERROR_MSG, ERROR_PREFIX, POST_ERROR_MSG1, | |
150 | xkm_output_dir, keymap) == -1) | |
151 | buf = NULL; | |
152 | ||
153 | free(xkbbasedirflag); | |
154 | ||
155 | if (!buf) { | |
156 | LogMessage(X_ERROR, | |
157 | "XKB: Could not invoke xkbcomp: not enough memory\n"); | |
158 | return FALSE; | |
159 | } | |
160 | ||
161 | #ifndef WIN32 | |
162 | out = Popen(buf, "w"); | |
163 | #else | |
164 | out = fopen(tmpname, "w"); | |
165 | #endif | |
166 | ||
167 | if (out != NULL) { | |
168 | #ifdef DEBUG | |
169 | if (xkbDebugFlags) { | |
170 | ErrorF("[xkb] XkbDDXCompileKeymapByNames compiling keymap:\n"); | |
171 | XkbWriteXKBKeymapForNames(stderr, names, xkb, want, need); | |
172 | } | |
173 | #endif | |
174 | XkbWriteXKBKeymapForNames(out, names, xkb, want, need); | |
175 | #ifndef WIN32 | |
176 | if (Pclose(out) == 0) | |
177 | #else | |
178 | if (fclose(out) == 0 && System(buf) >= 0) | |
179 | #endif | |
180 | { | |
181 | if (xkbDebugFlags) | |
182 | DebugF("[xkb] xkb executes: %s\n", buf); | |
183 | if (nameRtrn) { | |
184 | strlcpy(nameRtrn, keymap, nameRtrnLen); | |
185 | } | |
186 | free(buf); | |
187 | #ifdef WIN32 | |
188 | unlink(tmpname); | |
189 | #endif | |
190 | return TRUE; | |
191 | } | |
192 | else | |
193 | LogMessage(X_ERROR, "Error compiling keymap (%s)\n", keymap); | |
194 | #ifdef WIN32 | |
195 | /* remove the temporary file */ | |
196 | unlink(tmpname); | |
197 | #endif | |
198 | } | |
199 | else { | |
200 | #ifndef WIN32 | |
201 | LogMessage(X_ERROR, "XKB: Could not invoke xkbcomp\n"); | |
202 | #else | |
203 | LogMessage(X_ERROR, "Could not open file %s\n", tmpname); | |
204 | #endif | |
205 | } | |
206 | if (nameRtrn) | |
207 | nameRtrn[0] = '\0'; | |
208 | free(buf); | |
209 | return FALSE; | |
210 | } | |
211 | ||
212 | static FILE * | |
213 | XkbDDXOpenConfigFile(char *mapName, char *fileNameRtrn, int fileNameRtrnLen) | |
214 | { | |
215 | char buf[PATH_MAX], xkm_output_dir[PATH_MAX]; | |
216 | FILE *file; | |
217 | ||
218 | buf[0] = '\0'; | |
219 | if (mapName != NULL) { | |
220 | OutputDirectory(xkm_output_dir, sizeof(xkm_output_dir)); | |
221 | if ((XkbBaseDirectory != NULL) && (xkm_output_dir[0] != '/') | |
222 | #ifdef WIN32 | |
223 | && (!isalpha(xkm_output_dir[0]) || xkm_output_dir[1] != ':') | |
224 | #endif | |
225 | ) { | |
226 | if (snprintf(buf, PATH_MAX, "%s/%s%s.xkm", XkbBaseDirectory, | |
227 | xkm_output_dir, mapName) >= PATH_MAX) | |
228 | buf[0] = '\0'; | |
229 | } | |
230 | else { | |
231 | if (snprintf(buf, PATH_MAX, "%s%s.xkm", xkm_output_dir, mapName) | |
232 | >= PATH_MAX) | |
233 | buf[0] = '\0'; | |
234 | } | |
235 | if (buf[0] != '\0') | |
236 | file = fopen(buf, "rb"); | |
237 | else | |
238 | file = NULL; | |
239 | } | |
240 | else | |
241 | file = NULL; | |
242 | if ((fileNameRtrn != NULL) && (fileNameRtrnLen > 0)) { | |
243 | strlcpy(fileNameRtrn, buf, fileNameRtrnLen); | |
244 | } | |
245 | return file; | |
246 | } | |
247 | ||
248 | unsigned | |
249 | XkbDDXLoadKeymapByNames(DeviceIntPtr keybd, | |
250 | XkbComponentNamesPtr names, | |
251 | unsigned want, | |
252 | unsigned need, | |
253 | XkbDescPtr *xkbRtrn, char *nameRtrn, int nameRtrnLen) | |
254 | { | |
255 | XkbDescPtr xkb; | |
256 | FILE *file; | |
257 | char fileName[PATH_MAX]; | |
258 | unsigned missing; | |
259 | ||
260 | *xkbRtrn = NULL; | |
261 | if ((keybd == NULL) || (keybd->key == NULL) || | |
262 | (keybd->key->xkbInfo == NULL)) | |
263 | xkb = NULL; | |
264 | else | |
265 | xkb = keybd->key->xkbInfo->desc; | |
266 | if ((names->keycodes == NULL) && (names->types == NULL) && | |
267 | (names->compat == NULL) && (names->symbols == NULL) && | |
268 | (names->geometry == NULL)) { | |
269 | LogMessage(X_ERROR, "XKB: No components provided for device %s\n", | |
270 | keybd->name ? keybd->name : "(unnamed keyboard)"); | |
271 | return 0; | |
272 | } | |
273 | else if (!XkbDDXCompileKeymapByNames(xkb, names, want, need, | |
274 | nameRtrn, nameRtrnLen)) { | |
275 | LogMessage(X_ERROR, "XKB: Couldn't compile keymap\n"); | |
276 | return 0; | |
277 | } | |
278 | file = XkbDDXOpenConfigFile(nameRtrn, fileName, PATH_MAX); | |
279 | if (file == NULL) { | |
280 | LogMessage(X_ERROR, "Couldn't open compiled keymap file %s\n", | |
281 | fileName); | |
282 | return 0; | |
283 | } | |
284 | missing = XkmReadFile(file, need, want, xkbRtrn); | |
285 | if (*xkbRtrn == NULL) { | |
286 | LogMessage(X_ERROR, "Error loading keymap %s\n", fileName); | |
287 | fclose(file); | |
288 | (void) unlink(fileName); | |
289 | return 0; | |
290 | } | |
291 | else { | |
292 | DebugF("Loaded XKB keymap %s, defined=0x%x\n", fileName, | |
293 | (*xkbRtrn)->defined); | |
294 | } | |
295 | fclose(file); | |
296 | (void) unlink(fileName); | |
297 | return (need | want) & (~missing); | |
298 | } | |
299 | ||
300 | Bool | |
301 | XkbDDXNamesFromRules(DeviceIntPtr keybd, | |
302 | char *rules_name, | |
303 | XkbRF_VarDefsPtr defs, XkbComponentNamesPtr names) | |
304 | { | |
305 | char buf[PATH_MAX]; | |
306 | FILE *file; | |
307 | Bool complete; | |
308 | XkbRF_RulesPtr rules; | |
309 | ||
310 | if (!rules_name) | |
311 | return FALSE; | |
312 | ||
313 | if (snprintf(buf, PATH_MAX, "%s/rules/%s", XkbBaseDirectory, rules_name) | |
314 | >= PATH_MAX) { | |
315 | LogMessage(X_ERROR, "XKB: Rules name is too long\n"); | |
316 | return FALSE; | |
317 | } | |
318 | ||
319 | file = fopen(buf, "r"); | |
320 | if (!file) { | |
321 | LogMessage(X_ERROR, "XKB: Couldn't open rules file %s\n", buf); | |
322 | return FALSE; | |
323 | } | |
324 | ||
325 | rules = XkbRF_Create(); | |
326 | if (!rules) { | |
327 | LogMessage(X_ERROR, "XKB: Couldn't create rules struct\n"); | |
328 | fclose(file); | |
329 | return FALSE; | |
330 | } | |
331 | ||
332 | if (!XkbRF_LoadRules(file, rules)) { | |
333 | LogMessage(X_ERROR, "XKB: Couldn't parse rules file %s\n", rules_name); | |
334 | fclose(file); | |
335 | XkbRF_Free(rules, TRUE); | |
336 | return FALSE; | |
337 | } | |
338 | ||
339 | memset(names, 0, sizeof(*names)); | |
340 | complete = XkbRF_GetComponents(rules, defs, names); | |
341 | fclose(file); | |
342 | XkbRF_Free(rules, TRUE); | |
343 | ||
344 | if (!complete) | |
345 | LogMessage(X_ERROR, "XKB: Rules returned no components\n"); | |
346 | ||
347 | return complete; | |
348 | } | |
349 | ||
350 | static Bool | |
351 | XkbRMLVOtoKcCGST(DeviceIntPtr dev, XkbRMLVOSet * rmlvo, | |
352 | XkbComponentNamesPtr kccgst) | |
353 | { | |
354 | XkbRF_VarDefsRec mlvo; | |
355 | ||
356 | mlvo.model = rmlvo->model; | |
357 | mlvo.layout = rmlvo->layout; | |
358 | mlvo.variant = rmlvo->variant; | |
359 | mlvo.options = rmlvo->options; | |
360 | ||
361 | return XkbDDXNamesFromRules(dev, rmlvo->rules, &mlvo, kccgst); | |
362 | } | |
363 | ||
364 | /** | |
365 | * Compile the given RMLVO keymap and return it. Returns the XkbDescPtr on | |
366 | * success or NULL on failure. If the components compiled are not a superset | |
367 | * or equal to need, the compiliation is treated as failure. | |
368 | */ | |
369 | static XkbDescPtr | |
370 | XkbCompileKeymapForDevice(DeviceIntPtr dev, XkbRMLVOSet * rmlvo, int need) | |
371 | { | |
372 | XkbDescPtr xkb = NULL; | |
373 | unsigned int provided; | |
374 | XkbComponentNamesRec kccgst = { 0 }; | |
375 | char name[PATH_MAX]; | |
376 | ||
377 | if (XkbRMLVOtoKcCGST(dev, rmlvo, &kccgst)) { | |
378 | provided = | |
379 | XkbDDXLoadKeymapByNames(dev, &kccgst, XkmAllIndicesMask, need, &xkb, | |
380 | name, PATH_MAX); | |
381 | if ((need & provided) != need) { | |
382 | if (xkb) { | |
383 | XkbFreeKeyboard(xkb, 0, TRUE); | |
384 | xkb = NULL; | |
385 | } | |
386 | } | |
387 | } | |
388 | ||
389 | XkbFreeComponentNames(&kccgst, FALSE); | |
390 | return xkb; | |
391 | } | |
392 | ||
393 | XkbDescPtr | |
394 | XkbCompileKeymap(DeviceIntPtr dev, XkbRMLVOSet * rmlvo) | |
395 | { | |
396 | XkbDescPtr xkb; | |
397 | unsigned int need; | |
398 | ||
399 | if (!dev || !rmlvo) { | |
400 | LogMessage(X_ERROR, "XKB: No device or RMLVO specified\n"); | |
401 | return NULL; | |
402 | } | |
403 | ||
404 | /* These are the components we really really need */ | |
405 | need = XkmSymbolsMask | XkmCompatMapMask | XkmTypesMask | | |
406 | XkmKeyNamesMask | XkmVirtualModsMask; | |
407 | ||
408 | xkb = XkbCompileKeymapForDevice(dev, rmlvo, need); | |
409 | ||
410 | if (!xkb) { | |
411 | XkbRMLVOSet dflts; | |
412 | ||
413 | /* we didn't get what we really needed. And that will likely leave | |
414 | * us with a keyboard that doesn't work. Use the defaults instead */ | |
415 | LogMessage(X_ERROR, "XKB: Failed to load keymap. Loading default " | |
416 | "keymap instead.\n"); | |
417 | ||
418 | XkbGetRulesDflts(&dflts); | |
419 | ||
420 | xkb = XkbCompileKeymapForDevice(dev, &dflts, 0); | |
421 | ||
422 | XkbFreeRMLVOSet(&dflts, FALSE); | |
423 | } | |
424 | ||
425 | return xkb; | |
426 | } |