initial commit
[profile/ivi/xorg-x11-server.git] / xkb / ddxLoad.c
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 #if defined(CSRG_BASED) || defined(linux) || defined(__GNU__)
49 #include <paths.h>
50 #endif
51
52         /*
53          * If XKM_OUTPUT_DIR specifies a path without a leading slash, it is
54          * relative to the top-level XKB configuration directory.
55          * Making the server write to a subdirectory of that directory
56          * requires some work in the general case (install procedure
57          * has to create links to /var or somesuch on many machines),
58          * so we just compile into /usr/tmp for now.
59          */
60 #ifndef XKM_OUTPUT_DIR
61 #define XKM_OUTPUT_DIR  "compiled/"
62 #endif
63
64 #define PRE_ERROR_MSG "\"The XKEYBOARD keymap compiler (xkbcomp) reports:\""
65 #define ERROR_PREFIX    "\"> \""
66 #define POST_ERROR_MSG1 "\"Errors from xkbcomp are not fatal to the X server\""
67 #define POST_ERROR_MSG2 "\"End of messages from xkbcomp\""
68
69 #if defined(WIN32)
70 #define PATHSEPARATOR "\\"
71 #else
72 #define PATHSEPARATOR "/"
73 #endif
74
75 #ifdef WIN32
76
77 #include <X11/Xwindows.h>
78 const char* 
79 Win32TempDir()
80 {
81     static char buffer[PATH_MAX];
82     if (GetTempPath(sizeof(buffer), buffer))
83     {
84         int len;
85         buffer[sizeof(buffer)-1] = 0;
86         len = strlen(buffer);
87         if (len > 0)
88             if (buffer[len-1] == '\\')
89                 buffer[len-1] = 0;
90         return buffer;
91     }
92     if (getenv("TEMP") != NULL)
93         return getenv("TEMP");
94     else if (getenv("TMP") != NULL)
95         return getenv("TEMP");
96     else
97         return "/tmp";
98 }
99
100 int 
101 Win32System(const char *cmdline)
102 {
103     STARTUPINFO si;
104     PROCESS_INFORMATION pi;
105     DWORD dwExitCode;
106     char *cmd = strdup(cmdline);
107
108     ZeroMemory( &si, sizeof(si) );
109     si.cb = sizeof(si);
110     ZeroMemory( &pi, sizeof(pi) );
111
112     if (!CreateProcess(NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) 
113     {
114         LPVOID buffer;
115         if (!FormatMessage( 
116                     FORMAT_MESSAGE_ALLOCATE_BUFFER | 
117                     FORMAT_MESSAGE_FROM_SYSTEM | 
118                     FORMAT_MESSAGE_IGNORE_INSERTS,
119                     NULL,
120                     GetLastError(),
121                     MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
122                     (LPTSTR) &buffer,
123                     0,
124                     NULL ))
125         {
126             ErrorF("[xkb] Starting '%s' failed!\n", cmdline); 
127         }
128         else
129         {
130             ErrorF("[xkb] Starting '%s' failed: %s", cmdline, (char *)buffer); 
131             LocalFree(buffer);
132         }
133
134         free(cmd);
135         return -1;
136     }
137     /* Wait until child process exits. */
138     WaitForSingleObject( pi.hProcess, INFINITE );
139
140     GetExitCodeProcess( pi.hProcess, &dwExitCode);
141     
142     /* Close process and thread handles. */
143     CloseHandle( pi.hProcess );
144     CloseHandle( pi.hThread );
145     free(cmd);
146
147     return dwExitCode;
148 }
149 #undef System
150 #define System(x) Win32System(x)
151 #endif
152
153 static void
154 OutputDirectory(
155     char* outdir,
156     size_t size)
157 {
158 #ifndef WIN32
159     /* Can we write an xkm and then open it too? */
160     if (access(XKM_OUTPUT_DIR, W_OK | X_OK) == 0 && (strlen(XKM_OUTPUT_DIR) < size))
161     {
162         (void) strcpy (outdir, XKM_OUTPUT_DIR);
163     } else
164 #else
165     if (strlen(Win32TempDir()) + 1 < size)
166     {
167         (void) strcpy(outdir, Win32TempDir());
168         (void) strcat(outdir, "\\");
169     } else 
170 #endif
171     if (strlen("/tmp/") < size)
172     {
173         (void) strcpy (outdir, "/tmp/");
174     }
175 }
176
177 static Bool
178 XkbDDXCompileKeymapByNames(     XkbDescPtr              xkb,
179                                 XkbComponentNamesPtr    names,
180                                 unsigned                want,
181                                 unsigned                need,
182                                 char *                  nameRtrn,
183                                 int                     nameRtrnLen)
184 {
185     FILE *      out;
186     char        *buf = NULL, keymap[PATH_MAX], xkm_output_dir[PATH_MAX];
187
188     const char  *emptystring = "";
189     char *xkbbasedirflag = NULL;
190     const char  *xkbbindir = emptystring;
191     const char  *xkbbindirsep = emptystring;
192
193 #ifdef WIN32
194     /* WIN32 has no popen. The input must be stored in a file which is
195        used as input for xkbcomp. xkbcomp does not read from stdin. */
196     char tmpname[PATH_MAX];
197     const char *xkmfile = tmpname;
198 #else
199     const char *xkmfile = "-";
200 #endif
201
202     snprintf(keymap, sizeof(keymap), "server-%s", display);
203
204     OutputDirectory(xkm_output_dir, sizeof(xkm_output_dir));
205
206 #ifdef WIN32
207     strcpy(tmpname, Win32TempDir());
208     strcat(tmpname, "\\xkb_XXXXXX");
209     (void) mktemp(tmpname);
210 #endif
211
212     if (XkbBaseDirectory != NULL) {
213         xkbbasedirflag = Xprintf("\"-R%s\"", XkbBaseDirectory);
214     }
215
216     if (XkbBinDirectory != NULL) {
217         int ld = strlen(XkbBinDirectory);
218         int lps = strlen(PATHSEPARATOR);
219
220         xkbbindir = XkbBinDirectory;
221
222         if ((ld >= lps) &&
223             (strcmp(xkbbindir + ld - lps, PATHSEPARATOR) != 0)) {
224             xkbbindirsep = PATHSEPARATOR;
225         }
226     }
227
228     buf = Xprintf("\"%s%sxkbcomp\" -w %d %s -xkm \"%s\" "
229                   "-em1 %s -emp %s -eml %s \"%s%s.xkm\"",
230                   xkbbindir, xkbbindirsep,
231                   ( (xkbDebugFlags < 2) ? 1 :
232                     ((xkbDebugFlags > 10) ? 10 : (int)xkbDebugFlags) ),
233                   xkbbasedirflag ? xkbbasedirflag : "", xkmfile,
234                   PRE_ERROR_MSG, ERROR_PREFIX, POST_ERROR_MSG1,
235                   xkm_output_dir, keymap);
236
237     free(xkbbasedirflag);
238
239     if (!buf) {
240         LogMessage(X_ERROR, "XKB: Could not invoke xkbcomp: not enough memory\n");
241         return FALSE;
242     }
243     
244 #ifndef WIN32
245     out= Popen(buf,"w");
246 #else
247     out= fopen(tmpname, "w");
248 #endif
249     
250     if (out!=NULL) {
251 #ifdef DEBUG
252     if (xkbDebugFlags) {
253        ErrorF("[xkb] XkbDDXCompileKeymapByNames compiling keymap:\n");
254        XkbWriteXKBKeymapForNames(stderr,names,xkb,want,need);
255     }
256 #endif
257         XkbWriteXKBKeymapForNames(out,names,xkb,want,need);
258 #ifndef WIN32
259         if (Pclose(out)==0)
260 #else
261         if (fclose(out)==0 && System(buf) >= 0)
262 #endif
263         {
264             if (xkbDebugFlags)
265                 DebugF("[xkb] xkb executes: %s\n",buf);
266             if (nameRtrn) {
267                 strncpy(nameRtrn,keymap,nameRtrnLen);
268                 nameRtrn[nameRtrnLen-1]= '\0';
269             }
270             if (buf != NULL)
271                 free(buf);
272             return TRUE;
273         }
274         else
275             LogMessage(X_ERROR, "Error compiling keymap (%s)\n", keymap);
276 #ifdef WIN32
277         /* remove the temporary file */
278         unlink(tmpname);
279 #endif
280     }
281     else {
282 #ifndef WIN32
283         LogMessage(X_ERROR, "XKB: Could not invoke xkbcomp\n");
284 #else
285         LogMessage(X_ERROR, "Could not open file %s\n", tmpname);
286 #endif
287     }
288     if (nameRtrn)
289         nameRtrn[0]= '\0';
290     if (buf != NULL)
291         free(buf);
292     return FALSE;
293 }
294
295 static FILE *
296 XkbDDXOpenConfigFile(char *mapName,char *fileNameRtrn,int fileNameRtrnLen)
297 {
298 char    buf[PATH_MAX],xkm_output_dir[PATH_MAX];
299 FILE *  file;
300
301     buf[0]= '\0';
302     if (mapName!=NULL) {
303         OutputDirectory(xkm_output_dir, sizeof(xkm_output_dir));
304         if ((XkbBaseDirectory!=NULL)&&(xkm_output_dir[0]!='/')
305 #ifdef WIN32
306                 &&(!isalpha(xkm_output_dir[0]) || xkm_output_dir[1]!=':')
307 #endif
308                 ) {
309             if (strlen(XkbBaseDirectory)+strlen(xkm_output_dir)
310                      +strlen(mapName)+6 <= PATH_MAX)
311             {
312                 sprintf(buf,"%s/%s%s.xkm",XkbBaseDirectory,
313                                         xkm_output_dir,mapName);
314             }
315         }
316         else if (strlen(xkm_output_dir)+strlen(mapName)+5 <= PATH_MAX)
317             sprintf(buf,"%s%s.xkm",xkm_output_dir,mapName);
318         if (buf[0] != '\0')
319             file= fopen(buf,"rb");
320         else file= NULL;
321     }
322     else file= NULL;
323     if ((fileNameRtrn!=NULL)&&(fileNameRtrnLen>0)) {
324         strncpy(fileNameRtrn,buf,fileNameRtrnLen);
325         buf[fileNameRtrnLen-1]= '\0';
326     }
327     return file;
328 }
329
330 unsigned
331 XkbDDXLoadKeymapByNames(        DeviceIntPtr            keybd,
332                                 XkbComponentNamesPtr    names,
333                                 unsigned                want,
334                                 unsigned                need,
335                                 XkbDescPtr *            xkbRtrn,
336                                 char *                  nameRtrn,
337                                 int                     nameRtrnLen)
338 {
339 XkbDescPtr      xkb;
340 FILE    *       file;
341 char            fileName[PATH_MAX];
342 unsigned        missing;
343
344     *xkbRtrn = NULL;
345     if ((keybd==NULL)||(keybd->key==NULL)||(keybd->key->xkbInfo==NULL))
346          xkb= NULL;
347     else xkb= keybd->key->xkbInfo->desc;
348     if ((names->keycodes==NULL)&&(names->types==NULL)&&
349         (names->compat==NULL)&&(names->symbols==NULL)&&
350         (names->geometry==NULL)) {
351         LogMessage(X_ERROR, "XKB: No components provided for device %s\n",
352                    keybd->name ? keybd->name : "(unnamed keyboard)");
353         return 0;
354     }
355     else if (!XkbDDXCompileKeymapByNames(xkb,names,want,need,
356                                          nameRtrn,nameRtrnLen)){
357         LogMessage(X_ERROR, "XKB: Couldn't compile keymap\n");
358         return 0;
359     }
360     file= XkbDDXOpenConfigFile(nameRtrn,fileName,PATH_MAX);
361     if (file==NULL) {
362         LogMessage(X_ERROR, "Couldn't open compiled keymap file %s\n",fileName);
363         return 0;
364     }
365     missing= XkmReadFile(file,need,want,xkbRtrn);
366     if (*xkbRtrn==NULL) {
367         LogMessage(X_ERROR, "Error loading keymap %s\n",fileName);
368         fclose(file);
369         (void) unlink (fileName);
370         return 0;
371     }
372     else {
373         DebugF("Loaded XKB keymap %s, defined=0x%x\n",fileName,(*xkbRtrn)->defined);
374     }
375     fclose(file);
376     (void) unlink (fileName);
377     return (need|want)&(~missing);
378 }
379
380 Bool
381 XkbDDXNamesFromRules(   DeviceIntPtr            keybd,
382                         char *                  rules_name,
383                         XkbRF_VarDefsPtr        defs,
384                         XkbComponentNamesPtr    names)
385 {
386 char            buf[PATH_MAX];
387 FILE *          file;
388 Bool            complete;
389 XkbRF_RulesPtr  rules;
390
391     if (!rules_name)
392         return FALSE;
393
394     if (strlen(XkbBaseDirectory) + strlen(rules_name) + 8 > PATH_MAX) {
395         LogMessage(X_ERROR, "XKB: Rules name is too long\n");
396         return FALSE;
397     }
398     sprintf(buf,"%s/rules/%s", XkbBaseDirectory, rules_name);
399
400     file = fopen(buf, "r");
401     if (!file) {
402         LogMessage(X_ERROR, "XKB: Couldn't open rules file %s\n", buf);
403         return FALSE;
404     }
405
406     rules = XkbRF_Create();
407     if (!rules) {
408         LogMessage(X_ERROR, "XKB: Couldn't create rules struct\n");
409         fclose(file);
410         return FALSE;
411     }
412
413     if (!XkbRF_LoadRules(file, rules)) {
414         LogMessage(X_ERROR, "XKB: Couldn't parse rules file %s\n", rules_name);
415         fclose(file);
416         XkbRF_Free(rules,TRUE);
417         return FALSE;
418     }
419
420     memset(names, 0, sizeof(*names));
421     complete = XkbRF_GetComponents(rules,defs,names);
422     fclose(file);
423     XkbRF_Free(rules, TRUE);
424
425     if (!complete)
426         LogMessage(X_ERROR, "XKB: Rules returned no components\n");
427
428     return complete;
429 }
430
431 XkbDescPtr
432 XkbCompileKeymap(DeviceIntPtr dev, XkbRMLVOSet *rmlvo)
433 {
434     XkbComponentNamesRec kccgst;
435     XkbRF_VarDefsRec mlvo;
436     XkbDescPtr xkb;
437     char name[PATH_MAX];
438
439     if (!dev || !rmlvo) {
440         LogMessage(X_ERROR, "XKB: No device or RMLVO specified\n");
441         return NULL;
442     }
443
444     mlvo.model = rmlvo->model;
445     mlvo.layout = rmlvo->layout;
446     mlvo.variant = rmlvo->variant;
447     mlvo.options = rmlvo->options;
448
449     /* XDNFR already logs for us. */
450     if (!XkbDDXNamesFromRules(dev, rmlvo->rules, &mlvo, &kccgst))
451         return NULL;
452
453     /* XDLKBN too, but it might return 0 as well as allocating. */
454     if (!XkbDDXLoadKeymapByNames(dev, &kccgst, XkmAllIndicesMask, 0, &xkb, name,
455                                  PATH_MAX)) {
456         if (xkb)
457             XkbFreeKeyboard(xkb, 0, TRUE);
458         return NULL;
459     }
460
461     return xkb;
462 }