kbproto unentanglement: XkbNumVirtualMods
[platform/upstream/libxkbcommon.git] / src / xkbcomp / keymap.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 "xkbcomp-priv.h"
28
29 static void
30 ComputeEffectiveMask(struct xkb_keymap *keymap, struct xkb_mods *mods)
31 {
32     xkb_mod_index_t i;
33     xkb_mod_mask_t vmask = mods->mods >> XKB_NUM_CORE_MODS;
34
35     /* The effective mask is only real mods for now. */
36     mods->mask = mods->mods & 0xff;
37
38     for (i = 0; i < XKB_NUM_VIRTUAL_MODS; i++) {
39         if (!(vmask & (1 << i)))
40             continue;
41         mods->mask |= keymap->vmods[i];
42     }
43 }
44
45 static void
46 UpdateActionMods(struct xkb_keymap *keymap, union xkb_action *act,
47                  xkb_mod_mask_t rmodmask)
48 {
49     unsigned int flags;
50     struct xkb_mods *mods;
51
52     switch (act->type) {
53     case XkbSA_SetMods:
54     case XkbSA_LatchMods:
55     case XkbSA_LockMods:
56         flags = act->mods.flags;
57         mods = &act->mods.mods;
58         break;
59
60     case XkbSA_ISOLock:
61         flags = act->iso.flags;
62         mods = &act->iso.mods;
63         break;
64
65     default:
66         return;
67     }
68
69     if (flags & XkbSA_UseModMapMods) {
70         /* XXX: what's that. */
71         mods->mods &= 0xff;
72         mods->mods |= rmodmask;
73     }
74     ComputeEffectiveMask(keymap, mods);
75 }
76
77 /**
78  * Find an interpretation which applies to this particular level, either by
79  * finding an exact match for the symbol and modifier combination, or a
80  * generic XKB_KEY_NoSymbol match.
81  */
82 static struct xkb_sym_interpret *
83 FindInterpForKey(struct xkb_keymap *keymap, struct xkb_key *key,
84                  xkb_group_index_t group, xkb_level_index_t level)
85 {
86     struct xkb_sym_interpret *interp;
87     const xkb_keysym_t *syms;
88     int num_syms;
89
90     num_syms = xkb_key_get_syms_by_level(keymap, key, group, level, &syms);
91     if (num_syms == 0)
92         return NULL;
93
94     /*
95      * There may be multiple matchings interprets; we should always return
96      * the most specific. Here we rely on compat.c to set up the
97      * sym_interpret array from the most specific to the least specific,
98      * such that when we find a match we return immediately.
99      */
100     darray_foreach(interp, keymap->sym_interpret) {
101         uint32_t mods;
102         bool found;
103
104         if ((num_syms > 1 || interp->sym != syms[0]) &&
105             interp->sym != XKB_KEY_NoSymbol)
106             continue;
107
108         if (level == 0 || !(interp->match & XkbSI_LevelOneOnly))
109             mods = key->modmap;
110         else
111             mods = 0;
112
113         switch (interp->match & XkbSI_OpMask) {
114         case XkbSI_NoneOf:
115             found = !(interp->mods & mods);
116             break;
117         case XkbSI_AnyOfOrNone:
118             found = (!mods || (interp->mods & mods));
119             break;
120         case XkbSI_AnyOf:
121             found = !!(interp->mods & mods);
122             break;
123         case XkbSI_AllOf:
124             found = ((interp->mods & mods) == interp->mods);
125             break;
126         case XkbSI_Exactly:
127             found = (interp->mods == mods);
128             break;
129         default:
130             found = false;
131             break;
132         }
133
134         if (found)
135             return interp;
136     }
137
138     return NULL;
139 }
140
141 static bool
142 ApplyInterpsToKey(struct xkb_keymap *keymap, struct xkb_key *key)
143 {
144     xkb_mod_mask_t vmodmask = 0;
145     xkb_group_index_t group;
146     xkb_level_index_t width, level;
147
148     /* If we've been told not to bind interps to this key, then don't. */
149     if (key->explicit & XkbExplicitInterpretMask)
150         return true;
151
152     for (group = 0; group < key->num_groups; group++) {
153         width = XkbKeyGroupWidth(keymap, key, group);
154         for (level = 0; level < width; level++) {
155             struct xkb_sym_interpret *interp;
156
157             interp = FindInterpForKey(keymap, key, group, level);
158
159             /* Infer default key behaviours from the base level. */
160             if (group == 0 && level == 0) {
161                 if (!(key->explicit & XkbExplicitAutoRepeatMask) &&
162                     (!interp || (interp->flags & XkbSI_AutoRepeat)))
163                     key->repeats = true;
164             }
165
166             if (!interp)
167                 continue;
168
169             if ((group == 0 && level == 0) ||
170                 !(interp->match & XkbSI_LevelOneOnly)) {
171                 if (interp->virtual_mod != XKB_MOD_INVALID)
172                     vmodmask |= (1 << interp->virtual_mod);
173             }
174
175             if (interp->act.type != XkbSA_NoAction) {
176                 if (!key->actions) {
177                     key->actions = calloc(key->num_groups * key->width,
178                                           sizeof(*key->actions));
179                     if (!key->actions)
180                         return false;
181                 }
182
183                 *XkbKeyActionEntry(key, group, level) = interp->act;
184             }
185         }
186     }
187
188     if (!(key->explicit & XkbExplicitVModMapMask))
189         key->vmodmap = vmodmask;
190
191     return true;
192 }
193
194 /**
195  * This collects a bunch of disparate functions which was done in the server
196  * at various points that really should've been done within xkbcomp.  Turns out
197  * your actions and types are a lot more useful when any of your modifiers
198  * other than Shift actually do something ...
199  */
200 static bool
201 UpdateDerivedKeymapFields(struct xkb_keymap *keymap)
202 {
203     xkb_mod_index_t vmod;
204     xkb_led_index_t led;
205     unsigned int i, j;
206     struct xkb_key *key;
207
208     /* Find all the interprets for the key and bind them to actions,
209      * which will also update the vmodmap. */
210     xkb_foreach_key(key, keymap)
211         if (!ApplyInterpsToKey(keymap, key))
212             return false;
213
214     /* Update keymap->vmods, the virtual -> real mod mapping. */
215     for (vmod = 0; vmod < XKB_NUM_VIRTUAL_MODS; vmod++)
216         keymap->vmods[vmod] = 0;
217
218     xkb_foreach_key(key, keymap) {
219         if (!key->vmodmap)
220             continue;
221
222         for (vmod = 0; vmod < XKB_NUM_VIRTUAL_MODS; vmod++) {
223             if (!(key->vmodmap & (1 << vmod)))
224                 continue;
225             keymap->vmods[vmod] |= key->modmap;
226         }
227     }
228
229     /* Now update the level masks for all the types to reflect the vmods. */
230     for (i = 0; i < keymap->num_types; i++) {
231         ComputeEffectiveMask(keymap, &keymap->types[i].mods);
232
233         for (j = 0; j < keymap->types[i].num_entries; j++) {
234             ComputeEffectiveMask(keymap, &keymap->types[i].map[j].mods);
235             ComputeEffectiveMask(keymap, &keymap->types[i].map[j].preserve);
236         }
237     }
238
239     /* Update action modifiers. */
240     xkb_foreach_key(key, keymap) {
241         if (!key->actions)
242             continue;
243
244         for (i = 0; i < key->num_groups * key->width; i++)
245             UpdateActionMods(keymap, &key->actions[i], key->modmap);
246     }
247
248     /* Update vmod -> indicator maps. */
249     for (led = 0; led < XKB_NUM_INDICATORS; led++)
250         ComputeEffectiveMask(keymap, &keymap->indicators[led].mods);
251
252     /* Find maximum number of groups out of all keys in the keymap. */
253     xkb_foreach_key(key, keymap)
254         keymap->num_groups = MAX(keymap->num_groups, key->num_groups);
255
256     return true;
257 }
258
259 typedef bool (*compile_file_fn)(XkbFile *file,
260                                 struct xkb_keymap *keymap,
261                                 enum merge_mode merge);
262
263 static const compile_file_fn compile_file_fns[LAST_KEYMAP_FILE_TYPE + 1] = {
264     [FILE_TYPE_KEYCODES] = CompileKeycodes,
265     [FILE_TYPE_TYPES] = CompileKeyTypes,
266     [FILE_TYPE_COMPAT] = CompileCompatMap,
267     [FILE_TYPE_SYMBOLS] = CompileSymbols,
268 };
269
270 bool
271 CompileKeymap(XkbFile *file, struct xkb_keymap *keymap, enum merge_mode merge)
272 {
273     bool ok;
274     const char *main_name;
275     XkbFile *files[LAST_KEYMAP_FILE_TYPE + 1] = { NULL };
276     enum xkb_file_type type;
277     struct xkb_context *ctx = keymap->ctx;
278
279     main_name = file->name ? file->name : "(unnamed)";
280
281     /* Collect section files and check for duplicates. */
282     for (file = (XkbFile *) file->defs; file;
283          file = (XkbFile *) file->common.next) {
284         if (file->file_type < FIRST_KEYMAP_FILE_TYPE ||
285             file->file_type > LAST_KEYMAP_FILE_TYPE) {
286             log_err(ctx, "Cannot define %s in a keymap file\n",
287                     xkb_file_type_to_string(file->file_type));
288             continue;
289         }
290
291         if (files[file->file_type]) {
292             log_err(ctx,
293                     "More than one %s section in keymap file; "
294                     "All sections after the first ignored\n",
295                     xkb_file_type_to_string(file->file_type));
296             continue;
297         }
298
299         if (!file->topName) {
300             free(file->topName);
301             file->topName = strdup(main_name);
302         }
303
304         files[file->file_type] = file;
305     }
306
307     /*
308      * Check that all required section were provided.
309      * Report everything before failing.
310      */
311     ok = true;
312     for (type = FIRST_KEYMAP_FILE_TYPE;
313          type <= LAST_KEYMAP_FILE_TYPE;
314          type++) {
315         if (files[type] == NULL) {
316             log_err(ctx, "Required section %s missing from keymap\n",
317                     xkb_file_type_to_string(type));
318             ok = false;
319         }
320     }
321     if (!ok)
322         return false;
323
324     /* Compile sections. */
325     for (type = FIRST_KEYMAP_FILE_TYPE;
326          type <= LAST_KEYMAP_FILE_TYPE;
327          type++) {
328         log_dbg(ctx, "Compiling %s \"%s\"\n",
329                 xkb_file_type_to_string(type), files[type]->topName);
330
331         ok = compile_file_fns[type](files[type], keymap, merge);
332         if (!ok) {
333             log_err(ctx, "Failed to compile %s\n",
334                     xkb_file_type_to_string(type));
335             return false;
336         }
337     }
338
339     return UpdateDerivedKeymapFields(keymap);
340 }