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