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