Use CARD32 instead of Atom, move geom headers in
[profile/ivi/libxkbcommon.git] / src / xkbcomp / xkbcomp.c
1 /*
2 Copyright 2009  Dan Nicholson
3
4 Permission is hereby granted, free of charge, to any person obtaining a
5 copy of this software and associated documentation files (the "Software"),
6 to deal in the Software without restriction, including without limitation
7 the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 and/or sell copies of the Software, and to permit persons to whom the
9 Software is furnished to do so, subject to the following conditions:
10
11 The above copyright notice and this permission notice shall be included in
12 all copies or substantial portions of the Software.
13
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
18 ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
21 Except as contained in this notice, the names of the authors or their
22 institutions shall not be used in advertising or otherwise to promote the
23 sale, use or other dealings in this Software without prior written
24 authorization from the authors.
25 */
26
27 #include <limits.h>
28 #include "xkbcomp.h"
29 #include "xkballoc.h"
30 #include "xkbrules.h"
31 #include <X11/extensions/XKM.h>
32 #include "xkbpath.h"
33 #include "parseutils.h"
34 #include "utils.h"
35
36 /* Global debugging flags */
37 unsigned int debugFlags = 0;
38 unsigned int warningLevel = 0;
39
40 #define ISEMPTY(str) (!(str) || (strlen(str) == 0))
41
42 static XkbFile *
43 XkbKeymapFileFromComponents(const XkbComponentNamesPtr ktcsg)
44 {
45     XkbFile *keycodes, *types, *compat, *symbols, *geometry;
46     IncludeStmt *inc;
47
48     if (!ktcsg) {
49         ERROR("no components to generate keymap file from\n");
50         return NULL;
51     }
52
53     inc = IncludeCreate(ktcsg->keycodes, MergeDefault);
54     keycodes = CreateXKBFile(XkmKeyNamesIndex, NULL, (ParseCommon *)inc, 0);
55
56     inc = IncludeCreate(ktcsg->types, MergeDefault);
57     types = CreateXKBFile(XkmTypesIndex, NULL, (ParseCommon *)inc, 0);
58     AppendStmt(&keycodes->common, &types->common);
59
60     inc = IncludeCreate(ktcsg->compat, MergeDefault);
61     compat = CreateXKBFile(XkmCompatMapIndex, NULL, (ParseCommon *)inc, 0);
62     AppendStmt(&keycodes->common, &compat->common);
63
64     inc = IncludeCreate(ktcsg->symbols, MergeDefault);
65     symbols = CreateXKBFile(XkmSymbolsIndex, NULL, (ParseCommon *)inc, 0);
66     AppendStmt(&keycodes->common, &symbols->common);
67
68     inc = IncludeCreate(ktcsg->geometry, MergeDefault);
69     geometry = CreateXKBFile(XkmGeometryIndex, NULL, (ParseCommon *)inc, 0);
70     AppendStmt(&keycodes->common, &geometry->common);
71
72     return CreateXKBFile(XkmKeymapFile, ktcsg->keymap ? ktcsg->keymap : "",
73                          &keycodes->common, 0);
74 }
75
76 static XkbComponentNamesPtr
77 XkbComponentsFromRules(const char *rules, const XkbRF_VarDefsPtr defs)
78 {
79     FILE *rulesFile;
80     char *rulesPath;
81     XkbRF_RulesPtr loaded;
82     XkbComponentNamesPtr names = NULL;
83
84     rulesFile = XkbFindFileInPath((char *)rules, XkmRulesFile, &rulesPath);
85     if (!rulesFile) {
86         ERROR("could not find \"%s\" rules in XKB path\n", rules);
87         goto out;
88     }
89
90     if (!(loaded = _XkbTypedCalloc(1, XkbRF_RulesRec))) {
91         ERROR("failed to allocate XKB rules\n");
92         goto unwind_file;
93     }
94
95     if (!XkbcRF_LoadRules(rulesFile, loaded)) {
96         ERROR("failed to load XKB rules \"%s\"\n", rulesPath);
97         goto unwind_rules;
98     }
99
100     if (!(names = _XkbTypedCalloc(1, XkbComponentNamesRec))) {
101         ERROR("failed to allocate XKB components\n");
102         goto unwind_rules;
103     }
104
105     if (!XkbcRF_GetComponents(loaded, defs, names)) {
106         _XkbFree(names->keymap);
107         _XkbFree(names->keycodes);
108         _XkbFree(names->types);
109         _XkbFree(names->compat);
110         _XkbFree(names->symbols);
111         _XkbFree(names->geometry);
112         _XkbFree(names);
113         names = NULL;
114         ERROR("no components returned from XKB rules \"%s\"\n", rulesPath);
115     }
116
117 unwind_rules:
118     XkbcRF_Free(loaded, True);
119 unwind_file:
120     fclose(rulesFile);
121     free(rulesPath);
122 out:
123     return names;
124 }
125
126 XkbcDescPtr
127 XkbcCompileKeymapFromRules(const XkbRMLVOSet *rmlvo)
128 {
129     XkbRF_VarDefsRec defs;
130     XkbComponentNamesPtr names;
131     XkbcDescPtr xkb;
132
133     if (!rmlvo || ISEMPTY(rmlvo->rules) || ISEMPTY(rmlvo->layout)) {
134         ERROR("rules and layout required to generate XKB keymap\n");
135         return NULL;
136     }
137
138     defs.model = rmlvo->model;
139     defs.layout = rmlvo->layout;
140     defs.variant = rmlvo->variant;
141     defs.options = rmlvo->options;
142
143     names = XkbComponentsFromRules(rmlvo->rules, &defs);
144     if (!names) {
145         ERROR("failed to generate XKB components from rules \"%s\"\n",
146               rmlvo->rules);
147         return NULL;
148     }
149
150     xkb = XkbcCompileKeymapFromComponents(names);
151
152     _XkbFree(names->keymap);
153     _XkbFree(names->keycodes);
154     _XkbFree(names->types);
155     _XkbFree(names->compat);
156     _XkbFree(names->symbols);
157     _XkbFree(names->geometry);
158     _XkbFree(names);
159
160     return xkb;
161 }
162
163 static XkbFile *
164 XkbChooseMap(XkbFile *file, const char *name)
165 {
166     XkbFile *map = file;
167
168     /* map specified? */
169     if (name) {
170         while (map) {
171             if (map->name && strcmp(map->name, name) == 0)
172                 break;
173             map = (XkbFile *) map->common.next;
174         }
175
176         if (!map)
177             ERROR("no map named \"%s\" in input file\n", name);
178     }
179     else if (file->common.next) {
180         /* look for map with XkbLC_Default flag. */
181         for (; map; map = (XkbFile *) map->common.next) {
182             if (map->flags & XkbLC_Default)
183                 break;
184         }
185
186         if (!map) {
187             map = file;
188             WARN("no map specified, but components have several\n");
189             WARN("using the first defined map, \"%s\"\n",
190                  map->name ? map->name : "");
191         }
192     }
193
194     return map;
195 }
196
197 XkbcDescPtr
198 XkbcCompileKeymapFromComponents(const XkbComponentNamesPtr ktcsg)
199 {
200     XkbFile *file, *mapToUse;
201     XkbcDescPtr xkb;
202
203     uSetErrorFile(NULL);
204
205     if (!ktcsg || ISEMPTY(ktcsg->keycodes)) {
206         ERROR("keycodes required to generate XKB keymap\n");
207         goto fail;
208     }
209
210     if (!(file = XkbKeymapFileFromComponents(ktcsg))) {
211         ERROR("failed to generate parsed XKB file from components\n");
212         goto fail;
213     }
214
215     /* Find map to use */
216     if (!(mapToUse = XkbChooseMap(file, NULL)))
217         goto unwind_file;
218
219     /* Compile the keyboard */
220     if (!(xkb = XkbcAllocKeyboard())) {
221         ERROR("could not allocate keyboard description\n");
222         goto unwind_file;
223     }
224
225     if (!CompileKeymap(mapToUse, xkb, MergeReplace)) {
226         ERROR("failed to compile keymap\n");
227         goto unwind_xkb;
228     }
229
230     return xkb;
231 unwind_xkb:
232     XkbcFreeKeyboard(xkb, XkbAllComponentsMask, True);
233 unwind_file:
234     /* XXX: here's where we would free the XkbFile */
235 fail:
236     return NULL;
237 }
238
239 XkbcDescPtr
240 XkbcCompileKeymapFromFile(FILE *inputFile, const char *mapName)
241 {
242     XkbFile *file, *mapToUse;
243     XkbcDescPtr xkb;
244
245     if (!inputFile) {
246         ERROR("no file specified to generate XKB keymap\n");
247         goto fail;
248     }
249
250     setScanState("input", 1);
251     if (!XKBParseFile(inputFile, &file) || !file) {
252         ERROR("failed to parse input xkb file\n");
253         goto fail;
254     }
255
256     /* Find map to use */
257     if (!(mapToUse = XkbChooseMap(file, mapName)))
258         goto unwind_file;
259
260     switch (mapToUse->type) {
261     case XkmSemanticsFile:
262     case XkmLayoutFile:
263     case XkmKeymapFile:
264         break;
265     default:
266         ERROR("file type %d not handled\n", mapToUse->type);
267         goto unwind_file;
268     }
269
270     /* Compile the keyboard */
271     if (!(xkb = XkbcAllocKeyboard())) {
272         ERROR("could not allocate keyboard description\n");
273         goto unwind_file;
274     }
275
276     if (!CompileKeymap(mapToUse, xkb, MergeReplace)) {
277         ERROR("failed to compile keymap\n");
278         goto unwind_xkb;
279     }
280
281     return xkb;
282 unwind_xkb:
283     XkbcFreeKeyboard(xkb, XkbAllComponentsMask, True);
284 unwind_file:
285     /* XXX: here's where we would free the XkbFile */
286 fail:
287     return NULL;
288 }