compat: ignore "index" field in indicator statements
[platform/upstream/libxkbcommon.git] / src / xkbcomp / compat.c
1 /************************************************************
2  * Copyright (c) 1994 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 #include "xkbcomp-priv.h"
28 #include "text.h"
29 #include "expr.h"
30 #include "action.h"
31 #include "vmod.h"
32 #include "include.h"
33
34 /*
35  * The xkb_compat section
36  * =====================
37  * This section is the third to be processesed, after xkb_keycodes and
38  * xkb_types.
39  *
40  * Interpret statements
41  * --------------------
42  * Statements of the form:
43  *      interpret Num_Lock+Any { ... }
44  *
45  * The body of the statment may include statements of the following
46  * forms:
47  *
48  * - action statement:
49  *      action = LockMods(modifiers=NumLock);
50  *
51  * - virtual modifier statement:
52  *      virtualModifier = NumLock;
53  *
54  * - repeat statement:
55  *      repeat = True;
56  *
57  * - useModMapMods statement:
58  *      useModMapMods = level1;
59  *
60  * Indicator map statements
61  * ------------------------
62  * Statements of the form:
63  *      indicator "Shift Lock" { ... }
64  *
65  *   This statement specifies the behavior and binding of the indicator
66  *   with the given name ("Shift Lock" above). The name should have been
67  *   declared previously in the xkb_keycodes section (see Indicator name
68  *   statement), and given an index there. If it wasn't, it is created
69  *   with the next free index.
70  *   The body of the statement describes the conditions of the keyboard
71  *   state which will cause the indicator to be lit. It may include the
72  *   following statements:
73  *
74  * - modifiers statment:
75  *      modifiers = ScrollLock;
76  *
77  *   If the given modifiers are in the required state (see below), the
78  *   led is lit.
79  *
80  * - whichModifierState statment:
81  *      whichModState = Latched + Locked;
82  *
83  *   Can be any combination of:
84  *      base, latched, locked, effective
85  *      any (i.e. all of the above)
86  *      none (i.e. none of the above)
87  *      compat (this is legal, but unused)
88  *   This will cause the respective portion of the modifer state (see
89  *   struct xkb_state) to be matched against the modifiers given in the
90  *   "modifiers" statement.
91  *
92  *   Here's a simple example:
93  *      indicator "Num Lock" {
94  *          modifiers = NumLock;
95  *          whichModState = Locked;
96  *      };
97  *   Whenever the NumLock modifier is locked, the Num Lock indicator
98  *   will light up.
99  *
100  * - groups statment:
101  *      groups = All - group1;
102  *
103  *   If the given groups are in the required state (see below), the led
104  *   is lit.
105  *
106  * - whichGroupState statment:
107  *      whichGroupState = Effective;
108  *
109  *   Can be any combination of:
110  *      base, latched, locked, effective
111  *      any (i.e. all of the above)
112  *      none (i.e. none of the above)
113  *   This will cause the respective portion of the group state (see
114  *   struct xkb_state) to be matched against the groups given in the
115  *   "groups" statement.
116  *
117  *   Note: the above conditions are disjunctive, i.e. if any of them are
118  *   satisfied the led is lit.
119  *
120  * Virtual modifier statements
121  * ---------------------------
122  * Statements of the form:
123  *     virtual_modifiers LControl;
124  *
125  * Can appear in the xkb_types, xkb_compat, xkb_symbols sections.
126  * TODO
127  *
128  * Effect on keymap
129  * ----------------
130  * After all of the xkb_compat sections have been compiled, the following
131  * members of struct xkb_keymap are finalized:
132  *      darray(struct xkb_sym_interpret) sym_interpret;
133  *      struct xkb_indicator_map indicators[XkbNumIndicators];
134  *      char *compat_section_name;
135  * TODO: virtual modifiers.
136  */
137
138 enum si_field {
139     SI_FIELD_VIRTUAL_MOD    = (1 << 0),
140     SI_FIELD_ACTION         = (1 << 1),
141     SI_FIELD_AUTO_REPEAT    = (1 << 2),
142     SI_FIELD_LEVEL_ONE_ONLY = (1 << 3),
143 };
144
145 typedef struct _SymInterpInfo {
146     enum si_field defined;
147     unsigned file_id;
148     enum merge_mode merge;
149     struct list entry;
150
151     struct xkb_sym_interpret interp;
152 } SymInterpInfo;
153
154 enum led_field {
155     LED_FIELD_MODS       = (1 << 0),
156     LED_FIELD_GROUPS     = (1 << 1),
157     LED_FIELD_CTRLS      = (1 << 2),
158     LED_FIELD_EXPLICIT   = (1 << 3),
159     LED_FIELD_DRIVES_KBD = (1 << 4),
160 };
161
162 typedef struct _LEDInfo {
163     enum led_field defined;
164     unsigned file_id;
165     enum merge_mode merge;
166     struct list entry;
167
168     xkb_atom_t name;
169     xkb_led_index_t indicator;
170     unsigned char flags;
171     unsigned char which_mods;
172     xkb_mod_mask_t mods;
173     unsigned char which_groups;
174     uint32_t groups;
175     unsigned int ctrls;
176 } LEDInfo;
177
178 typedef struct _GroupCompatInfo {
179     unsigned file_id;
180     enum merge_mode merge;
181     bool defined;
182     xkb_mod_mask_t mods;
183 } GroupCompatInfo;
184
185 typedef struct _CompatInfo {
186     char *name;
187     unsigned file_id;
188     int errorCount;
189     int nInterps;
190     struct list interps;
191     SymInterpInfo dflt;
192     LEDInfo ledDflt;
193     GroupCompatInfo groupCompat[XkbNumKbdGroups];
194     struct list leds;
195     VModInfo vmods;
196     ActionInfo *act;
197     struct xkb_keymap *keymap;
198 } CompatInfo;
199
200 static const char *
201 siText(SymInterpInfo *si, CompatInfo *info)
202 {
203     static char buf[128];
204
205     if (si == &info->dflt)
206         return "default";
207
208     snprintf(buf, sizeof(buf), "%s+%s(%s)",
209              KeysymText(si->interp.sym),
210              SIMatchText(si->interp.match),
211              ModMaskText(si->interp.mods));
212     return buf;
213 }
214
215 static inline bool
216 ReportSINotArray(CompatInfo *info, SymInterpInfo *si, const char *field)
217 {
218     return ReportNotArray(info->keymap, "symbol interpretation", field,
219                           siText(si, info));
220 }
221
222 static inline bool
223 ReportSIBadType(CompatInfo *info, SymInterpInfo *si, const char *field,
224                 const char *wanted)
225 {
226     return ReportBadType(info->keymap->ctx, "symbol interpretation", field,
227                          siText(si, info), wanted);
228 }
229
230 static inline bool
231 ReportIndicatorBadType(CompatInfo *info, LEDInfo *led,
232                        const char *field, const char *wanted)
233 {
234     return ReportBadType(info->keymap->ctx, "indicator map", field,
235                          xkb_atom_text(info->keymap->ctx, led->name),
236                          wanted);
237 }
238
239 static inline bool
240 ReportIndicatorNotArray(CompatInfo *info, LEDInfo *led,
241                         const char *field)
242 {
243     return ReportNotArray(info->keymap, "indicator map", field,
244                           xkb_atom_text(info->keymap->ctx, led->name));
245 }
246
247 static void
248 ClearIndicatorMapInfo(struct xkb_context *ctx, LEDInfo *info)
249 {
250     info->name = xkb_atom_intern(ctx, "default");
251     info->indicator = XKB_LED_INVALID;
252     info->flags = 0;
253     info->which_mods = 0;
254     info->mods = 0;
255     info->which_groups = info->groups = 0;
256     info->ctrls = 0;
257 }
258
259 static void
260 InitCompatInfo(CompatInfo *info, struct xkb_keymap *keymap, unsigned file_id)
261 {
262     info->keymap = keymap;
263     info->name = NULL;
264     info->file_id = file_id;
265     info->errorCount = 0;
266     info->nInterps = 0;
267     list_init(&info->interps);
268     info->act = NULL;
269     info->dflt.file_id = file_id;
270     info->dflt.defined = 0;
271     info->dflt.merge = MERGE_OVERRIDE;
272     info->dflt.interp.flags = 0;
273     info->dflt.interp.virtual_mod = XkbNoModifier;
274     memset(&info->dflt.interp.act, 0, sizeof(info->dflt.interp.act));
275     info->dflt.interp.act.type = XkbSA_NoAction;
276     ClearIndicatorMapInfo(keymap->ctx, &info->ledDflt);
277     info->ledDflt.file_id = file_id;
278     info->ledDflt.defined = 0;
279     info->ledDflt.merge = MERGE_OVERRIDE;
280     memset(&info->groupCompat[0], 0,
281            XkbNumKbdGroups * sizeof(GroupCompatInfo));
282     list_init(&info->leds);
283     InitVModInfo(&info->vmods, keymap);
284 }
285
286 static void
287 ClearCompatInfo(CompatInfo *info)
288 {
289     SymInterpInfo *si, *next_si;
290     LEDInfo *led, *next_led;
291     struct xkb_keymap *keymap = info->keymap;
292
293     free(info->name);
294     info->name = NULL;
295     info->dflt.defined = 0;
296     info->dflt.merge = MERGE_AUGMENT;
297     info->dflt.interp.flags = 0;
298     info->dflt.interp.virtual_mod = XkbNoModifier;
299     memset(&info->dflt.interp.act, 0, sizeof(info->dflt.interp.act));
300     info->dflt.interp.act.type = XkbSA_NoAction;
301     ClearIndicatorMapInfo(keymap->ctx, &info->ledDflt);
302     info->nInterps = 0;
303     list_foreach_safe(si, next_si, &info->interps, entry)
304         free(si);
305     memset(&info->groupCompat[0], 0,
306            XkbNumKbdGroups * sizeof(GroupCompatInfo));
307     list_foreach_safe(led, next_led, &info->leds, entry)
308         free(led);
309     FreeActionInfo(info->act);
310     info->act = NULL;
311     info->keymap = NULL;
312     ClearVModInfo(&info->vmods, keymap);
313 }
314
315 static SymInterpInfo *
316 NextInterp(CompatInfo *info)
317 {
318     SymInterpInfo *si;
319
320     si = calloc(1, sizeof(*si));
321     if (!si)
322         return NULL;
323
324     list_append(&si->entry, &info->interps);
325     info->nInterps++;
326
327     return si;
328 }
329
330 static SymInterpInfo *
331 FindMatchingInterp(CompatInfo *info, SymInterpInfo *new)
332 {
333     SymInterpInfo *old;
334
335     list_foreach(old, &info->interps, entry)
336         if (old->interp.sym == new->interp.sym &&
337             old->interp.mods == new->interp.mods &&
338             old->interp.match == new->interp.match)
339             return old;
340
341     return NULL;
342 }
343
344 static bool
345 UseNewInterpField(enum si_field field, SymInterpInfo *old, SymInterpInfo *new,
346                   int verbosity, enum si_field *collide)
347 {
348     if (!(old->defined & field))
349         return true;
350
351     if (new->defined & field) {
352         if ((old->file_id == new->file_id && verbosity > 0) || verbosity > 9)
353             *collide |= field;
354
355         if (new->merge != MERGE_AUGMENT)
356             return true;
357     }
358
359     return false;
360 }
361
362 static bool
363 AddInterp(CompatInfo *info, SymInterpInfo *new)
364 {
365     enum si_field collide;
366     SymInterpInfo *old;
367     struct list entry;
368     int verbosity = xkb_get_log_verbosity(info->keymap->ctx);
369
370     collide = 0;
371     old = FindMatchingInterp(info, new);
372     if (old != NULL) {
373         if (new->merge == MERGE_REPLACE) {
374             entry = old->entry;
375             if ((old->file_id == new->file_id && verbosity > 0) ||
376                 verbosity > 9)
377                 log_warn(info->keymap->ctx,
378                          "Multiple definitions for \"%s\"; "
379                          "Earlier interpretation ignored\n",
380                          siText(new, info));
381             *old = *new;
382             old->entry = entry;
383             return true;
384         }
385
386         if (UseNewInterpField(SI_FIELD_VIRTUAL_MOD, old, new, verbosity,
387                               &collide)) {
388             old->interp.virtual_mod = new->interp.virtual_mod;
389             old->defined |= SI_FIELD_VIRTUAL_MOD;
390         }
391         if (UseNewInterpField(SI_FIELD_ACTION, old, new, verbosity,
392                               &collide)) {
393             old->interp.act = new->interp.act;
394             old->defined |= SI_FIELD_ACTION;
395         }
396         if (UseNewInterpField(SI_FIELD_AUTO_REPEAT, old, new, verbosity,
397                               &collide)) {
398             old->interp.flags &= ~XkbSI_AutoRepeat;
399             old->interp.flags |= (new->interp.flags & XkbSI_AutoRepeat);
400             old->defined |= SI_FIELD_AUTO_REPEAT;
401         }
402         if (UseNewInterpField(SI_FIELD_LEVEL_ONE_ONLY, old, new, verbosity,
403                               &collide)) {
404             old->interp.match &= ~XkbSI_LevelOneOnly;
405             old->interp.match |= (new->interp.match & XkbSI_LevelOneOnly);
406             old->defined |= SI_FIELD_LEVEL_ONE_ONLY;
407         }
408
409         if (collide) {
410             log_warn(info->keymap->ctx,
411                      "Multiple interpretations of \"%s\"; "
412                      "Using %s definition for duplicate fields\n",
413                      siText(new, info),
414                      (new->merge != MERGE_AUGMENT ? "last" : "first"));
415         }
416
417         return true;
418     }
419
420     old = new;
421     new = NextInterp(info);
422     if (!new)
423         return false;
424     entry = new->entry;
425     *new = *old;
426     new->entry = entry;
427     return true;
428 }
429
430 static bool
431 AddGroupCompat(CompatInfo *info, xkb_group_index_t group, GroupCompatInfo *new)
432 {
433     GroupCompatInfo *gc;
434     int verbosity = xkb_get_log_verbosity(info->keymap->ctx);
435
436     gc = &info->groupCompat[group];
437     if (gc->mods == new->mods)
438         return true;
439
440     if ((gc->file_id == new->file_id && verbosity > 0) || verbosity > 9)
441         log_warn(info->keymap->ctx,
442                  "Compat map for group %u redefined; "
443                  "Using %s definition\n",
444                  group + 1, (new->merge == MERGE_AUGMENT ? "old" : "new"));
445
446     if (new->defined && (new->merge != MERGE_AUGMENT || !gc->defined))
447         *gc = *new;
448
449     return true;
450 }
451
452 /***====================================================================***/
453
454 static bool
455 ResolveStateAndPredicate(ExprDef *expr, unsigned *pred_rtrn,
456                          xkb_mod_mask_t *mods_rtrn, CompatInfo *info)
457 {
458     if (expr == NULL) {
459         *pred_rtrn = XkbSI_AnyOfOrNone;
460         *mods_rtrn = ~0;
461         return true;
462     }
463
464     *pred_rtrn = XkbSI_Exactly;
465     if (expr->op == EXPR_ACTION_DECL) {
466         const char *pred_txt = xkb_atom_text(info->keymap->ctx,
467                                              expr->value.action.name);
468         if (istreq(pred_txt, "noneof"))
469             *pred_rtrn = XkbSI_NoneOf;
470         else if (istreq(pred_txt, "anyofornone"))
471             *pred_rtrn = XkbSI_AnyOfOrNone;
472         else if (istreq(pred_txt, "anyof"))
473             *pred_rtrn = XkbSI_AnyOf;
474         else if (istreq(pred_txt, "allof"))
475             *pred_rtrn = XkbSI_AllOf;
476         else if (istreq(pred_txt, "exactly"))
477             *pred_rtrn = XkbSI_Exactly;
478         else {
479             log_err(info->keymap->ctx,
480                     "Illegal modifier predicate \"%s\"; Ignored\n", pred_txt);
481             return false;
482         }
483         expr = expr->value.action.args;
484     }
485     else if (expr->op == EXPR_IDENT) {
486         const char *pred_txt = xkb_atom_text(info->keymap->ctx,
487                                              expr->value.str);
488         if (pred_txt && istreq(pred_txt, "any")) {
489             *pred_rtrn = XkbSI_AnyOf;
490             *mods_rtrn = 0xff;
491             return true;
492         }
493     }
494
495     return ExprResolveModMask(info->keymap->ctx, expr, mods_rtrn);
496 }
497
498 /***====================================================================***/
499
500 static bool
501 UseNewLEDField(enum led_field field, LEDInfo *old, LEDInfo *new,
502                int verbosity, enum led_field *collide)
503 {
504     if (!(old->defined & field))
505         return true;
506
507     if (new->defined & field) {
508         if ((old->file_id == new->file_id && verbosity > 0) || verbosity > 9)
509             *collide |= field;
510
511         if (new->merge != MERGE_AUGMENT)
512             return true;
513     }
514
515     return false;
516 }
517
518 static bool
519 AddIndicatorMap(CompatInfo *info, LEDInfo *new)
520 {
521     LEDInfo *old;
522     enum led_field collide;
523     struct xkb_context *ctx = info->keymap->ctx;
524     int verbosity = xkb_get_log_verbosity(ctx);
525
526     list_foreach(old, &info->leds, entry) {
527         if (old->name != new->name)
528             continue;
529
530         if (old->mods == new->mods &&
531             old->groups == new->groups &&
532             old->ctrls == new->ctrls &&
533             old->which_mods == new->which_mods &&
534             old->which_groups == new->which_groups) {
535             old->defined |= new->defined;
536             return true;
537         }
538
539         if (new->merge == MERGE_REPLACE) {
540             struct list entry = old->entry;
541             if ((old->file_id == new->file_id && verbosity > 0) ||
542                 verbosity > 9)
543                 log_warn(info->keymap->ctx,
544                          "Map for indicator %s redefined; "
545                          "Earlier definition ignored\n",
546                          xkb_atom_text(ctx, old->name));
547             *old = *new;
548             old->entry = entry;
549             return true;
550         }
551
552         collide = 0;
553         if (UseNewLEDField(LED_FIELD_MODS, old, new, verbosity,
554                            &collide)) {
555             old->which_mods = new->which_mods;
556             old->mods = new->mods;
557             old->defined |= LED_FIELD_MODS;
558         }
559         if (UseNewLEDField(LED_FIELD_GROUPS, old, new, verbosity,
560                            &collide)) {
561             old->which_groups = new->which_groups;
562             old->groups = new->groups;
563             old->defined |= LED_FIELD_GROUPS;
564         }
565         if (UseNewLEDField(LED_FIELD_CTRLS, old, new, verbosity,
566                            &collide)) {
567             old->ctrls = new->ctrls;
568             old->defined |= LED_FIELD_CTRLS;
569         }
570         if (UseNewLEDField(LED_FIELD_EXPLICIT, old, new, verbosity,
571                            &collide)) {
572             old->flags &= ~XkbIM_NoExplicit;
573             old->flags |= (new->flags & XkbIM_NoExplicit);
574             old->defined |= LED_FIELD_EXPLICIT;
575         }
576         if (UseNewLEDField(LED_FIELD_DRIVES_KBD, old, new, verbosity,
577                            &collide)) {
578             old->flags &= ~XkbIM_LEDDrivesKB;
579             old->flags |= (new->flags & XkbIM_LEDDrivesKB);
580             old->defined |= LED_FIELD_DRIVES_KBD;
581         }
582
583         if (collide) {
584             log_warn(info->keymap->ctx,
585                      "Map for indicator %s redefined; "
586                      "Using %s definition for duplicate fields\n",
587                      xkb_atom_text(ctx, old->name),
588                      (new->merge == MERGE_AUGMENT ? "first" : "last"));
589         }
590
591         return true;
592     }
593
594     old = malloc(sizeof(*old));
595     if (!old) {
596         log_wsgo(info->keymap->ctx,
597                  "Couldn't allocate indicator map; "
598                  "Map for indicator %s not compiled\n",
599                  xkb_atom_text(ctx, new->name));
600         return false;
601     }
602
603     *old = *new;
604     list_append(&old->entry, &info->leds);
605
606     return true;
607 }
608
609 static void
610 MergeIncludedCompatMaps(CompatInfo *into, CompatInfo *from,
611                         enum merge_mode merge)
612 {
613     SymInterpInfo *si, *next_si;
614     LEDInfo *led, *next_led;
615     GroupCompatInfo *gcm;
616     xkb_group_index_t i;
617
618     if (from->errorCount > 0) {
619         into->errorCount += from->errorCount;
620         return;
621     }
622
623     if (into->name == NULL) {
624         into->name = from->name;
625         from->name = NULL;
626     }
627
628     list_foreach_safe(si, next_si, &from->interps, entry) {
629         si->merge = (merge == MERGE_DEFAULT ? si->merge : merge);
630         if (!AddInterp(into, si))
631             into->errorCount++;
632     }
633
634     for (i = 0; i < XkbNumKbdGroups; i++) {
635         gcm = &from->groupCompat[i];
636         gcm->merge = (merge == MERGE_DEFAULT ? gcm->merge : merge);
637         if (!AddGroupCompat(into, i, gcm))
638             into->errorCount++;
639     }
640
641     list_foreach_safe(led, next_led, &from->leds, entry) {
642         led->merge = (merge == MERGE_DEFAULT ? led->merge : merge);
643         if (!AddIndicatorMap(into, led))
644             into->errorCount++;
645     }
646 }
647
648 static void
649 HandleCompatMapFile(CompatInfo *info, XkbFile *file, enum merge_mode merge);
650
651 static bool
652 HandleIncludeCompatMap(CompatInfo *info, IncludeStmt *stmt)
653 {
654     enum merge_mode merge = MERGE_DEFAULT;
655     XkbFile *rtrn;
656     CompatInfo included, next_incl;
657
658     InitCompatInfo(&included, info->keymap, info->file_id);
659     if (stmt->stmt) {
660         free(included.name);
661         included.name = stmt->stmt;
662         stmt->stmt = NULL;
663     }
664
665     for (; stmt; stmt = stmt->next_incl) {
666         if (!ProcessIncludeFile(info->keymap->ctx, stmt, FILE_TYPE_COMPAT,
667                                 &rtrn, &merge)) {
668             info->errorCount += 10;
669             ClearCompatInfo(&included);
670             return false;
671         }
672
673         InitCompatInfo(&next_incl, info->keymap, rtrn->id);
674         next_incl.file_id = rtrn->id;
675         next_incl.dflt = info->dflt;
676         next_incl.dflt.file_id = rtrn->id;
677         next_incl.dflt.merge = merge;
678         next_incl.ledDflt.file_id = rtrn->id;
679         next_incl.ledDflt.merge = merge;
680         next_incl.act = info->act;
681
682         HandleCompatMapFile(&next_incl, rtrn, MERGE_OVERRIDE);
683
684         MergeIncludedCompatMaps(&included, &next_incl, merge);
685         if (info->act)
686             next_incl.act = NULL;
687
688         ClearCompatInfo(&next_incl);
689         FreeXkbFile(rtrn);
690     }
691
692     MergeIncludedCompatMaps(info, &included, merge);
693     ClearCompatInfo(&included);
694
695     return (info->errorCount == 0);
696 }
697
698 static const LookupEntry useModMapValues[] = {
699     { "levelone", 1 },
700     { "level1", 1 },
701     { "anylevel", 0 },
702     { "any", 0 },
703     { NULL, 0 }
704 };
705
706 static bool
707 SetInterpField(CompatInfo *info, SymInterpInfo *si, const char *field,
708                ExprDef *arrayNdx, ExprDef *value)
709 {
710     struct xkb_keymap *keymap = info->keymap;
711     xkb_mod_index_t ndx;
712
713     if (istreq(field, "action")) {
714         if (arrayNdx)
715             return ReportSINotArray(info, si, field);
716
717         if (!HandleActionDef(value, keymap, &si->interp.act, info->act))
718             return false;
719
720         si->defined |= SI_FIELD_ACTION;
721     }
722     else if (istreq(field, "virtualmodifier") ||
723              istreq(field, "virtualmod")) {
724         if (arrayNdx)
725             return ReportSINotArray(info, si, field);
726
727         if (!ResolveVirtualModifier(value, keymap, &ndx, &info->vmods))
728             return ReportSIBadType(info, si, field, "virtual modifier");
729
730         si->interp.virtual_mod = ndx;
731         si->defined |= SI_FIELD_VIRTUAL_MOD;
732     }
733     else if (istreq(field, "repeat")) {
734         bool set;
735
736         if (arrayNdx)
737             return ReportSINotArray(info, si, field);
738
739         if (!ExprResolveBoolean(keymap->ctx, value, &set))
740             return ReportSIBadType(info, si, field, "boolean");
741
742         if (set)
743             si->interp.flags |= XkbSI_AutoRepeat;
744         else
745             si->interp.flags &= ~XkbSI_AutoRepeat;
746
747         si->defined |= SI_FIELD_AUTO_REPEAT;
748     }
749     else if (istreq(field, "locking")) {
750         log_dbg(info->keymap->ctx,
751                 "The \"locking\" field in symbol interpretation is unsupported; "
752                 "Ignored\n");
753     }
754     else if (istreq(field, "usemodmap") ||
755              istreq(field, "usemodmapmods")) {
756         unsigned int val;
757
758         if (arrayNdx)
759             return ReportSINotArray(info, si, field);
760
761         if (!ExprResolveEnum(keymap->ctx, value, &val, useModMapValues))
762             return ReportSIBadType(info, si, field, "level specification");
763
764         if (val)
765             si->interp.match |= XkbSI_LevelOneOnly;
766         else
767             si->interp.match &= ~XkbSI_LevelOneOnly;
768
769         si->defined |= SI_FIELD_LEVEL_ONE_ONLY;
770     }
771     else {
772         return ReportBadField(keymap, "symbol interpretation", field,
773                               siText(si, info));
774     }
775
776     return true;
777 }
778
779 static const LookupEntry modComponentNames[] = {
780     {"base", XkbIM_UseBase},
781     {"latched", XkbIM_UseLatched},
782     {"locked", XkbIM_UseLocked},
783     {"effective", XkbIM_UseEffective},
784     {"compat", XkbIM_UseCompat},
785     {"any", XkbIM_UseAnyMods},
786     {"none", 0},
787     {NULL, 0}
788 };
789
790 static const LookupEntry groupComponentNames[] = {
791     {"base", XkbIM_UseBase},
792     {"latched", XkbIM_UseLatched},
793     {"locked", XkbIM_UseLocked},
794     {"effective", XkbIM_UseEffective},
795     {"any", XkbIM_UseAnyGroup},
796     {"none", 0},
797     {NULL, 0}
798 };
799
800 static const LookupEntry groupNames[] = {
801     {"group1", 0x01},
802     {"group2", 0x02},
803     {"group3", 0x04},
804     {"group4", 0x08},
805     {"group5", 0x10},
806     {"group6", 0x20},
807     {"group7", 0x40},
808     {"group8", 0x80},
809     {"none", 0x00},
810     {"all", 0xff},
811     {NULL, 0}
812 };
813
814 static bool
815 SetIndicatorMapField(CompatInfo *info, LEDInfo *led,
816                      const char *field, ExprDef *arrayNdx, ExprDef *value)
817 {
818     bool ok = true;
819     struct xkb_keymap *keymap = info->keymap;
820
821     if (istreq(field, "modifiers") || istreq(field, "mods")) {
822         if (arrayNdx)
823             return ReportIndicatorNotArray(info, led, field);
824
825         if (!ExprResolveVModMask(keymap, value, &led->mods))
826             return ReportIndicatorBadType(info, led, field, "modifier mask");
827
828         led->defined |= LED_FIELD_MODS;
829     }
830     else if (istreq(field, "groups")) {
831         unsigned int mask;
832
833         if (arrayNdx)
834             return ReportIndicatorNotArray(info, led, field);
835
836         if (!ExprResolveMask(keymap->ctx, value, &mask, groupNames))
837             return ReportIndicatorBadType(info, led, field, "group mask");
838
839         led->groups = mask;
840         led->defined |= LED_FIELD_GROUPS;
841     }
842     else if (istreq(field, "controls") || istreq(field, "ctrls")) {
843         unsigned int mask;
844
845         if (arrayNdx)
846             return ReportIndicatorNotArray(info, led, field);
847
848         if (!ExprResolveMask(keymap->ctx, value, &mask, ctrlNames))
849             return ReportIndicatorBadType(info, led, field,
850                                           "controls mask");
851
852         led->ctrls = mask;
853         led->defined |= LED_FIELD_CTRLS;
854     }
855     else if (istreq(field, "allowexplicit")) {
856         bool set;
857
858         if (arrayNdx)
859             return ReportIndicatorNotArray(info, led, field);
860
861         if (!ExprResolveBoolean(keymap->ctx, value, &set))
862             return ReportIndicatorBadType(info, led, field, "boolean");
863
864         if (set)
865             led->flags &= ~XkbIM_NoExplicit;
866         else
867             led->flags |= XkbIM_NoExplicit;
868
869         led->defined |= LED_FIELD_EXPLICIT;
870     }
871     else if (istreq(field, "whichmodstate") ||
872              istreq(field, "whichmodifierstate")) {
873         unsigned int mask;
874
875         if (arrayNdx)
876             return ReportIndicatorNotArray(info, led, field);
877
878         if (!ExprResolveMask(keymap->ctx, value, &mask, modComponentNames))
879             return ReportIndicatorBadType(info, led, field,
880                                           "mask of modifier state components");
881
882         led->which_mods = mask;
883     }
884     else if (istreq(field, "whichgroupstate")) {
885         unsigned mask;
886
887         if (arrayNdx)
888             return ReportIndicatorNotArray(info, led, field);
889
890         if (!ExprResolveMask(keymap->ctx, value, &mask, groupComponentNames))
891             return ReportIndicatorBadType(info, led, field,
892                                           "mask of group state components");
893
894         led->which_groups = mask;
895     }
896     else if (istreq(field, "driveskbd") ||
897              istreq(field, "driveskeyboard") ||
898              istreq(field, "leddriveskbd") ||
899              istreq(field, "leddriveskeyboard") ||
900              istreq(field, "indicatordriveskbd") ||
901              istreq(field, "indicatordriveskeyboard")) {
902         bool set;
903
904         if (arrayNdx)
905             return ReportIndicatorNotArray(info, led, field);
906
907         if (!ExprResolveBoolean(keymap->ctx, value, &set))
908             return ReportIndicatorBadType(info, led, field, "boolean");
909
910         if (set)
911             led->flags |= XkbIM_LEDDrivesKB;
912         else
913             led->flags &= ~XkbIM_LEDDrivesKB;
914
915         led->defined |= LED_FIELD_DRIVES_KBD;
916     }
917     else if (istreq(field, "index")) {
918         /* Users should see this, it might cause unexpected behavior. */
919         log_err(info->keymap->ctx,
920                 "The \"index\" field in indicator statements is unsupported; "
921                 "Ignored\n");
922     }
923     else {
924         log_err(info->keymap->ctx,
925                 "Unknown field %s in map for %s indicator; "
926                 "Definition ignored\n",
927                 field, xkb_atom_text(keymap->ctx, led->name));
928         ok = false;
929     }
930
931     return ok;
932 }
933
934 static bool
935 HandleInterpVar(CompatInfo *info, VarDef *stmt)
936 {
937     const char *elem, *field;
938     ExprDef *ndx;
939     bool ret;
940
941     if (!ExprResolveLhs(info->keymap->ctx, stmt->name, &elem, &field, &ndx))
942         ret = false;
943     else if (elem && istreq(elem, "interpret"))
944         ret = SetInterpField(info, &info->dflt, field, ndx, stmt->value);
945     else if (elem && istreq(elem, "indicator"))
946         ret = SetIndicatorMapField(info, &info->ledDflt, field, ndx,
947                                    stmt->value);
948     else
949         ret = SetActionField(info->keymap, elem, field, ndx, stmt->value,
950                              &info->act);
951     return ret;
952 }
953
954 static bool
955 HandleInterpBody(CompatInfo *info, VarDef *def, SymInterpInfo *si)
956 {
957     bool ok = true;
958     const char *elem, *field;
959     ExprDef *arrayNdx;
960
961     for (; def; def = (VarDef *) def->common.next) {
962         if (def->name && def->name->op == EXPR_FIELD_REF) {
963             ok = HandleInterpVar(info, def);
964             continue;
965         }
966
967         ok = ExprResolveLhs(info->keymap->ctx, def->name, &elem, &field,
968                             &arrayNdx);
969         if (!ok)
970             continue;
971
972         ok = SetInterpField(info, si, field, arrayNdx, def->value);
973     }
974
975     return ok;
976 }
977
978 static bool
979 HandleInterpDef(CompatInfo *info, InterpDef *def, enum merge_mode merge)
980 {
981     unsigned pred, mods;
982     SymInterpInfo si;
983
984     if (!ResolveStateAndPredicate(def->match, &pred, &mods, info)) {
985         log_err(info->keymap->ctx,
986                 "Couldn't determine matching modifiers; "
987                 "Symbol interpretation ignored\n");
988         return false;
989     }
990
991     si = info->dflt;
992
993     si.merge = merge = (def->merge == MERGE_DEFAULT ? merge : def->merge);
994
995     if (!LookupKeysym(def->sym, &si.interp.sym)) {
996         log_err(info->keymap->ctx,
997                 "Could not resolve keysym %s; "
998                 "Symbol interpretation ignored\n",
999                 def->sym);
1000         return false;
1001     }
1002
1003     si.interp.match = pred & XkbSI_OpMask;
1004
1005     si.interp.mods = mods;
1006
1007     if (!HandleInterpBody(info, def->def, &si)) {
1008         info->errorCount++;
1009         return false;
1010     }
1011
1012     if (!AddInterp(info, &si)) {
1013         info->errorCount++;
1014         return false;
1015     }
1016
1017     return true;
1018 }
1019
1020 static bool
1021 HandleGroupCompatDef(CompatInfo *info, GroupCompatDef *def,
1022                      enum merge_mode merge)
1023 {
1024     GroupCompatInfo tmp;
1025
1026     merge = (def->merge == MERGE_DEFAULT ? merge : def->merge);
1027
1028     if (def->group < 1 || def->group > XkbNumKbdGroups) {
1029         log_err(info->keymap->ctx,
1030                 "Keyboard group must be in the range 1..%u; "
1031                 "Compatibility map for illegal group %u ignored\n",
1032                 XkbNumKbdGroups, def->group);
1033         return false;
1034     }
1035
1036     tmp.file_id = info->file_id;
1037     tmp.merge = merge;
1038
1039     if (!ExprResolveVModMask(info->keymap, def->def, &tmp.mods)) {
1040         log_err(info->keymap->ctx,
1041                 "Expected a modifier mask in group compatibility definition; "
1042                 "Ignoring illegal compatibility map for group %u\n",
1043                 def->group);
1044         return false;
1045     }
1046
1047     tmp.defined = true;
1048     return AddGroupCompat(info, def->group - 1, &tmp);
1049 }
1050
1051 static bool
1052 HandleIndicatorMapDef(CompatInfo *info, IndicatorMapDef *def,
1053                       enum merge_mode merge)
1054 {
1055     LEDInfo led;
1056     VarDef *var;
1057     bool ok;
1058
1059     if (def->merge != MERGE_DEFAULT)
1060         merge = def->merge;
1061
1062     led = info->ledDflt;
1063     led.merge = merge;
1064     led.name = def->name;
1065
1066     ok = true;
1067     for (var = def->body; var != NULL; var = (VarDef *) var->common.next) {
1068         const char *elem, *field;
1069         ExprDef *arrayNdx;
1070         if (!ExprResolveLhs(info->keymap->ctx, var->name, &elem, &field,
1071                             &arrayNdx)) {
1072             ok = false;
1073             continue;
1074         }
1075
1076         if (elem) {
1077             log_err(info->keymap->ctx,
1078                     "Cannot set defaults for \"%s\" element in indicator map; "
1079                     "Assignment to %s.%s ignored\n", elem, elem, field);
1080             ok = false;
1081         }
1082         else {
1083             ok = SetIndicatorMapField(info, &led, field, arrayNdx,
1084                                       var->value) && ok;
1085         }
1086     }
1087
1088     if (ok)
1089         return AddIndicatorMap(info, &led);
1090
1091     return false;
1092 }
1093
1094 static void
1095 HandleCompatMapFile(CompatInfo *info, XkbFile *file, enum merge_mode merge)
1096 {
1097     bool ok;
1098     ParseCommon *stmt;
1099
1100     merge = (merge == MERGE_DEFAULT ? MERGE_AUGMENT : merge);
1101
1102     free(info->name);
1103     info->name = strdup_safe(file->name);
1104
1105     for (stmt = file->defs; stmt; stmt = stmt->next) {
1106         switch (stmt->type) {
1107         case STMT_INCLUDE:
1108             ok = HandleIncludeCompatMap(info, (IncludeStmt *) stmt);
1109             break;
1110         case STMT_INTERP:
1111             ok = HandleInterpDef(info, (InterpDef *) stmt, merge);
1112             break;
1113         case STMT_GROUP_COMPAT:
1114             ok = HandleGroupCompatDef(info, (GroupCompatDef *) stmt, merge);
1115             break;
1116         case STMT_INDICATOR_MAP:
1117             ok = HandleIndicatorMapDef(info, (IndicatorMapDef *) stmt, merge);
1118             break;
1119         case STMT_VAR:
1120             ok = HandleInterpVar(info, (VarDef *) stmt);
1121             break;
1122         case STMT_VMOD:
1123             ok = HandleVModDef((VModDef *) stmt, info->keymap, merge,
1124                                &info->vmods);
1125             break;
1126         default:
1127             log_err(info->keymap->ctx,
1128                     "Interpretation files may not include other types; "
1129                     "Ignoring %s\n", StmtTypeToString(stmt->type));
1130             ok = false;
1131             break;
1132         }
1133
1134         if (!ok)
1135             info->errorCount++;
1136
1137         if (info->errorCount > 10) {
1138             log_err(info->keymap->ctx,
1139                     "Abandoning compatibility map \"%s\"\n", file->topName);
1140             break;
1141         }
1142     }
1143 }
1144
1145 static void
1146 CopyInterps(CompatInfo *info, bool needSymbol, unsigned pred)
1147 {
1148     SymInterpInfo *si;
1149
1150     list_foreach(si, &info->interps, entry) {
1151         if (((si->interp.match & XkbSI_OpMask) != pred) ||
1152             (needSymbol && si->interp.sym == XKB_KEY_NoSymbol) ||
1153             (!needSymbol && si->interp.sym != XKB_KEY_NoSymbol))
1154             continue;
1155
1156         darray_append(info->keymap->sym_interpret, si->interp);
1157     }
1158 }
1159
1160 static void
1161 BindIndicators(CompatInfo *info, struct list *unbound_leds)
1162 {
1163     xkb_led_index_t i;
1164     LEDInfo *led, *next_led;
1165     struct xkb_indicator_map *map;
1166     struct xkb_keymap *keymap = info->keymap;
1167
1168     list_foreach(led, unbound_leds, entry) {
1169         if (led->indicator == XKB_LED_INVALID) {
1170             for (i = 0; i < XkbNumIndicators; i++) {
1171                 if (keymap->indicator_names[i] &&
1172                     streq(keymap->indicator_names[i],
1173                           xkb_atom_text(keymap->ctx, led->name))) {
1174                     led->indicator = i + 1;
1175                     break;
1176                 }
1177             }
1178         }
1179     }
1180
1181     list_foreach(led, unbound_leds, entry) {
1182         if (led->indicator == XKB_LED_INVALID) {
1183             for (i = 0; i < XkbNumIndicators; i++) {
1184                 if (keymap->indicator_names[i] == NULL) {
1185                     keymap->indicator_names[i] =
1186                         xkb_atom_text(keymap->ctx, led->name);
1187                     led->indicator = i + 1;
1188                     break;
1189                 }
1190             }
1191
1192             if (led->indicator == XKB_LED_INVALID) {
1193                 log_err(info->keymap->ctx,
1194                         "No unnamed indicators found; "
1195                         "Virtual indicator map \"%s\" not bound\n",
1196                         xkb_atom_text(keymap->ctx, led->name));
1197                 continue;
1198             }
1199         }
1200     }
1201
1202     list_foreach_safe(led, next_led, unbound_leds, entry) {
1203         if (led->indicator == XKB_LED_INVALID) {
1204             free(led);
1205             continue;
1206         }
1207
1208         if (!streq(keymap->indicator_names[led->indicator - 1],
1209                    xkb_atom_text(keymap->ctx, led->name))) {
1210             const char *old = keymap->indicator_names[led->indicator - 1];
1211             log_err(info->keymap->ctx,
1212                     "Multiple names bound to indicator %d; "
1213                     "Using %s, ignoring %s\n",
1214                     led->indicator, old,
1215                     xkb_atom_text(keymap->ctx, led->name));
1216             free(led);
1217             continue;
1218         }
1219
1220         map = &keymap->indicators[led->indicator - 1];
1221         map->flags = led->flags;
1222         map->which_groups = led->which_groups;
1223         map->groups = led->groups;
1224         map->which_mods = led->which_mods;
1225         map->mods.mods = led->mods;
1226         map->ctrls = led->ctrls;
1227         free(led);
1228     }
1229
1230     list_init(unbound_leds);
1231 }
1232
1233 static bool
1234 CopyIndicatorMapDefs(CompatInfo *info)
1235 {
1236     LEDInfo *led, *next_led;
1237     struct list unbound_leds;
1238     struct xkb_indicator_map *im;
1239     struct xkb_keymap *keymap = info->keymap;
1240
1241     list_init(&unbound_leds);
1242
1243     list_foreach_safe(led, next_led, &info->leds, entry) {
1244         if (led->groups != 0 && led->which_groups == 0)
1245             led->which_groups = XkbIM_UseEffective;
1246
1247         if (led->which_mods == 0 && led->mods)
1248             led->which_mods = XkbIM_UseEffective;
1249
1250         if (led->indicator == XKB_LED_INVALID) {
1251             list_append(&led->entry, &unbound_leds);
1252             continue;
1253         }
1254
1255         im = &keymap->indicators[led->indicator - 1];
1256         im->flags = led->flags;
1257         im->which_groups = led->which_groups;
1258         im->groups = led->groups;
1259         im->which_mods = led->which_mods;
1260         im->mods.mods = led->mods;
1261         im->ctrls = led->ctrls;
1262         keymap->indicator_names[led->indicator - 1] =
1263             xkb_atom_text(keymap->ctx, led->name);
1264         free(led);
1265     }
1266     list_init(&info->leds);
1267
1268     BindIndicators(info, &unbound_leds);
1269
1270     return true;
1271 }
1272
1273 bool
1274 CompileCompatMap(XkbFile *file, struct xkb_keymap *keymap,
1275                  enum merge_mode merge)
1276 {
1277     xkb_group_index_t i;
1278     CompatInfo info;
1279     GroupCompatInfo *gcm;
1280
1281     InitCompatInfo(&info, keymap, file->id);
1282     info.dflt.merge = merge;
1283     info.ledDflt.merge = merge;
1284
1285     HandleCompatMapFile(&info, file, merge);
1286
1287     if (info.errorCount != 0)
1288         goto err_info;
1289
1290     if (info.name)
1291         keymap->compat_section_name = strdup(info.name);
1292
1293     darray_init(keymap->sym_interpret);
1294     if (info.nInterps > 0) {
1295         darray_growalloc(keymap->sym_interpret, info.nInterps);
1296         CopyInterps(&info, true, XkbSI_Exactly);
1297         CopyInterps(&info, true, XkbSI_AllOf | XkbSI_NoneOf);
1298         CopyInterps(&info, true, XkbSI_AnyOf);
1299         CopyInterps(&info, true, XkbSI_AnyOfOrNone);
1300         CopyInterps(&info, false, XkbSI_Exactly);
1301         CopyInterps(&info, false, XkbSI_AllOf | XkbSI_NoneOf);
1302         CopyInterps(&info, false, XkbSI_AnyOf);
1303         CopyInterps(&info, false, XkbSI_AnyOfOrNone);
1304     }
1305
1306     for (i = 0; i < XkbNumKbdGroups; i++) {
1307         gcm = &info.groupCompat[i];
1308         if (gcm->file_id != 0 || gcm->mods != 0)
1309             keymap->groups[i].mods = gcm->mods;
1310     }
1311
1312     if (!CopyIndicatorMapDefs(&info))
1313         info.errorCount++;
1314
1315     ClearCompatInfo(&info);
1316     return true;
1317
1318 err_info:
1319     ClearCompatInfo(&info);
1320     return false;
1321 }
1322
1323 static void
1324 ComputeEffectiveMask(struct xkb_keymap *keymap, struct xkb_mods *mods)
1325 {
1326     xkb_mod_index_t i;
1327     xkb_mod_mask_t vmask = mods->mods >> XkbNumModifiers;
1328
1329     /* The effective mask is only real mods for now. */
1330     mods->mask = mods->mods & 0xff;
1331
1332     for (i = 0; i < XkbNumVirtualMods; i++) {
1333         if (!(vmask & (1 << i)))
1334             continue;
1335         mods->mask |= keymap->vmods[i];
1336     }
1337 }
1338
1339 static void
1340 UpdateActionMods(struct xkb_keymap *keymap, union xkb_action *act,
1341                  xkb_mod_mask_t rmodmask)
1342 {
1343     unsigned int flags;
1344     struct xkb_mods *mods;
1345
1346     switch (act->type) {
1347     case XkbSA_SetMods:
1348     case XkbSA_LatchMods:
1349     case XkbSA_LockMods:
1350         flags = act->mods.flags;
1351         mods = &act->mods.mods;
1352         break;
1353
1354     case XkbSA_ISOLock:
1355         flags = act->iso.flags;
1356         mods = &act->iso.mods;
1357         break;
1358
1359     default:
1360         return;
1361     }
1362
1363     if (flags & XkbSA_UseModMapMods) {
1364         /* XXX: what's that. */
1365         mods->mods &= 0xff;
1366         mods->mods |= rmodmask;
1367     }
1368     ComputeEffectiveMask(keymap, mods);
1369 }
1370
1371 /**
1372  * Find an interpretation which applies to this particular level, either by
1373  * finding an exact match for the symbol and modifier combination, or a
1374  * generic XKB_KEY_NoSymbol match.
1375  */
1376 static struct xkb_sym_interpret *
1377 FindInterpForKey(struct xkb_keymap *keymap, struct xkb_key *key,
1378                  xkb_group_index_t group, xkb_level_index_t level)
1379 {
1380     struct xkb_sym_interpret *ret = NULL;
1381     struct xkb_sym_interpret *interp;
1382     const xkb_keysym_t *syms;
1383     int num_syms;
1384
1385     num_syms = xkb_key_get_syms_by_level(keymap, key, group, level, &syms);
1386     if (num_syms == 0)
1387         return NULL;
1388
1389     darray_foreach(interp, keymap->sym_interpret) {
1390         uint32_t mods;
1391         bool found;
1392
1393         if ((num_syms > 1 || interp->sym != syms[0]) &&
1394             interp->sym != XKB_KEY_NoSymbol)
1395             continue;
1396
1397         if (level == 0 || !(interp->match & XkbSI_LevelOneOnly))
1398             mods = key->modmap;
1399         else
1400             mods = 0;
1401
1402         switch (interp->match & XkbSI_OpMask) {
1403         case XkbSI_NoneOf:
1404             found = !(interp->mods & mods);
1405             break;
1406         case XkbSI_AnyOfOrNone:
1407             found = (!mods || (interp->mods & mods));
1408             break;
1409         case XkbSI_AnyOf:
1410             found = !!(interp->mods & mods);
1411             break;
1412         case XkbSI_AllOf:
1413             found = ((interp->mods & mods) == interp->mods);
1414             break;
1415         case XkbSI_Exactly:
1416             found = (interp->mods == mods);
1417             break;
1418         default:
1419             found = false;
1420             break;
1421         }
1422
1423         if (found && interp->sym != XKB_KEY_NoSymbol)
1424             return interp;
1425         else if (found && !ret)
1426             ret = interp;
1427     }
1428
1429     return ret;
1430 }
1431
1432 static bool
1433 ApplyInterpsToKey(struct xkb_keymap *keymap, struct xkb_key *key)
1434 {
1435 #define INTERP_SIZE (8 * 4)
1436     struct xkb_sym_interpret *interps[INTERP_SIZE];
1437     xkb_mod_mask_t vmodmask = 0;
1438     int num_acts = 0;
1439     xkb_group_index_t group;
1440     xkb_level_index_t level;
1441     unsigned int i;
1442
1443     /* If we've been told not to bind interps to this key, then don't. */
1444     if (key->explicit & XkbExplicitInterpretMask)
1445         return true;
1446
1447     for (i = 0; i < INTERP_SIZE; i++)
1448         interps[i] = NULL;
1449
1450     for (group = 0; group < key->num_groups; group++) {
1451         for (level = 0; level < XkbKeyGroupWidth(keymap, key, group);
1452              level++) {
1453             i = (group * key->width) + level;
1454             if (i >= INTERP_SIZE) /* XXX FIXME */
1455                 return false;
1456             interps[i] = FindInterpForKey(keymap, key, group, level);
1457             if (interps[i])
1458                 num_acts++;
1459         }
1460     }
1461
1462     if (num_acts && !key->actions) {
1463         key->actions = calloc(key->num_groups * key->width,
1464                               sizeof(*key->actions));
1465         if (!key->actions)
1466             return false;
1467     }
1468
1469     for (group = 0; group < key->num_groups; group++) {
1470         for (level = 0; level < XkbKeyGroupWidth(keymap, key, group);
1471              level++) {
1472             struct xkb_sym_interpret *interp;
1473
1474             i = (group * key->width) + level;
1475             interp = interps[i];
1476
1477             /* Infer default key behaviours from the base level. */
1478             if (group == 0 && level == 0) {
1479                 if (!(key->explicit & XkbExplicitAutoRepeatMask) &&
1480                     (!interp || (interp->flags & XkbSI_AutoRepeat)))
1481                     key->repeats = true;
1482             }
1483
1484             if (!interp)
1485                 continue;
1486
1487             if ((group == 0 && level == 0) ||
1488                 !(interp->match & XkbSI_LevelOneOnly)) {
1489                 if (interp->virtual_mod != XkbNoModifier)
1490                     vmodmask |= (1 << interp->virtual_mod);
1491             }
1492
1493             key->actions[i] = interp->act;
1494         }
1495     }
1496
1497     if (!(key->explicit & XkbExplicitVModMapMask))
1498         key->vmodmap = vmodmask;
1499
1500     return true;
1501 #undef INTERP_SIZE
1502 }
1503
1504 /**
1505  * This collects a bunch of disparate functions which was done in the server
1506  * at various points that really should've been done within xkbcomp.  Turns out
1507  * your actions and types are a lot more useful when any of your modifiers
1508  * other than Shift actually do something ...
1509  */
1510 bool
1511 UpdateModifiersFromCompat(struct xkb_keymap *keymap)
1512 {
1513     xkb_mod_index_t vmod;
1514     xkb_group_index_t grp;
1515     xkb_led_index_t led;
1516     unsigned int i, j;
1517     struct xkb_key *key;
1518
1519     /* Find all the interprets for the key and bind them to actions,
1520      * which will also update the vmodmap. */
1521     xkb_foreach_key(key, keymap)
1522         if (!ApplyInterpsToKey(keymap, key))
1523             return false;
1524
1525     /* Update keymap->vmods, the virtual -> real mod mapping. */
1526     for (vmod = 0; vmod < XkbNumVirtualMods; vmod++)
1527         keymap->vmods[vmod] = 0;
1528
1529     xkb_foreach_key(key, keymap) {
1530         if (!key->vmodmap)
1531             continue;
1532
1533         for (vmod = 0; vmod < XkbNumVirtualMods; vmod++) {
1534             if (!(key->vmodmap & (1 << vmod)))
1535                 continue;
1536             keymap->vmods[vmod] |= key->modmap;
1537         }
1538     }
1539
1540     /* Now update the level masks for all the types to reflect the vmods. */
1541     for (i = 0; i < keymap->num_types; i++) {
1542         ComputeEffectiveMask(keymap, &keymap->types[i].mods);
1543
1544         for (j = 0; j < keymap->types[i].num_entries; j++) {
1545             ComputeEffectiveMask(keymap, &keymap->types[i].map[j].mods);
1546             ComputeEffectiveMask(keymap, &keymap->types[i].map[j].preserve);
1547         }
1548     }
1549
1550     /* Update action modifiers. */
1551     xkb_foreach_key(key, keymap) {
1552         if (!key->actions)
1553             continue;
1554
1555         for (i = 0; i < key->num_groups * key->width; i++)
1556             UpdateActionMods(keymap, &key->actions[i], key->modmap);
1557     }
1558
1559     /* Update group modifiers. */
1560     for (grp = 0; grp < XkbNumKbdGroups; grp++)
1561         ComputeEffectiveMask(keymap, &keymap->groups[grp]);
1562
1563     /* Update vmod -> indicator maps. */
1564     for (led = 0; led < XkbNumIndicators; led++)
1565         ComputeEffectiveMask(keymap, &keymap->indicators[led].mods);
1566
1567     return true;
1568 }