Fix virtual modifiers mask extraction
[platform/upstream/libxkbcommon.git] / src / xkbcomp / action.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 "action.h"
28
29 static const ExprDef constTrue = {
30     .common = { .type = STMT_EXPR, .next = NULL },
31     .op = EXPR_VALUE,
32     .value_type = EXPR_TYPE_BOOLEAN,
33     .value = { .ival = 1 },
34 };
35
36 static const ExprDef constFalse = {
37     .common = { .type = STMT_EXPR, .next = NULL },
38     .op = EXPR_VALUE,
39     .value_type = EXPR_TYPE_BOOLEAN,
40     .value = { .ival = 0 },
41 };
42
43 /***====================================================================***/
44
45 static const LookupEntry actionStrings[] = {
46     { "noaction",          XkbSA_NoAction       },
47     { "setmods",           XkbSA_SetMods        },
48     { "latchmods",         XkbSA_LatchMods      },
49     { "lockmods",          XkbSA_LockMods       },
50     { "setgroup",          XkbSA_SetGroup       },
51     { "latchgroup",        XkbSA_LatchGroup     },
52     { "lockgroup",         XkbSA_LockGroup      },
53     { "moveptr",           XkbSA_MovePtr        },
54     { "movepointer",       XkbSA_MovePtr        },
55     { "ptrbtn",            XkbSA_PtrBtn         },
56     { "pointerbutton",     XkbSA_PtrBtn         },
57     { "lockptrbtn",        XkbSA_LockPtrBtn     },
58     { "lockpointerbutton", XkbSA_LockPtrBtn     },
59     { "lockptrbutton",     XkbSA_LockPtrBtn     },
60     { "lockpointerbtn",    XkbSA_LockPtrBtn     },
61     { "setptrdflt",        XkbSA_SetPtrDflt     },
62     { "setpointerdefault", XkbSA_SetPtrDflt     },
63     { "isolock",           XkbSA_ISOLock        },
64     { "terminate",         XkbSA_Terminate      },
65     { "terminateserver",   XkbSA_Terminate      },
66     { "switchscreen",      XkbSA_SwitchScreen   },
67     { "setcontrols",       XkbSA_SetControls    },
68     { "lockcontrols",      XkbSA_LockControls   },
69     { "actionmessage",     XkbSA_ActionMessage  },
70     { "messageaction",     XkbSA_ActionMessage  },
71     { "message",           XkbSA_ActionMessage  },
72     { "redirect",          XkbSA_RedirectKey    },
73     { "redirectkey",       XkbSA_RedirectKey    },
74     { "devbtn",            XkbSA_DeviceBtn      },
75     { "devicebtn",         XkbSA_DeviceBtn      },
76     { "devbutton",         XkbSA_DeviceBtn      },
77     { "devicebutton",      XkbSA_DeviceBtn      },
78     { "lockdevbtn",        XkbSA_DeviceBtn      },
79     { "lockdevicebtn",     XkbSA_LockDeviceBtn  },
80     { "lockdevbutton",     XkbSA_LockDeviceBtn  },
81     { "lockdevicebutton",  XkbSA_LockDeviceBtn  },
82     { "devval",            XkbSA_DeviceValuator },
83     { "deviceval",         XkbSA_DeviceValuator },
84     { "devvaluator",       XkbSA_DeviceValuator },
85     { "devicevaluator",    XkbSA_DeviceValuator },
86     { "private",           PrivateAction        },
87     { NULL,                0                    }
88 };
89
90 static const LookupEntry fieldStrings[] = {
91     { "clearLocks",       F_ClearLocks  },
92     { "latchToLock",      F_LatchToLock },
93     { "genKeyEvent",      F_GenKeyEvent },
94     { "generateKeyEvent", F_GenKeyEvent },
95     { "report",           F_Report      },
96     { "default",          F_Default     },
97     { "affect",           F_Affect      },
98     { "increment",        F_Increment   },
99     { "modifiers",        F_Modifiers   },
100     { "mods",             F_Modifiers   },
101     { "group",            F_Group       },
102     { "x",                F_X           },
103     { "y",                F_Y           },
104     { "accel",            F_Accel       },
105     { "accelerate",       F_Accel       },
106     { "repeat",           F_Accel       },
107     { "button",           F_Button      },
108     { "value",            F_Value       },
109     { "controls",         F_Controls    },
110     { "ctrls",            F_Controls    },
111     { "type",             F_Type        },
112     { "count",            F_Count       },
113     { "screen",           F_Screen      },
114     { "same",             F_Same        },
115     { "sameServer",       F_Same        },
116     { "data",             F_Data        },
117     { "device",           F_Device      },
118     { "dev",              F_Device      },
119     { "key",              F_Keycode     },
120     { "keycode",          F_Keycode     },
121     { "kc",               F_Keycode     },
122     { "clearmods",        F_ModsToClear },
123     { "clearmodifiers",   F_ModsToClear },
124     { NULL,               0             }
125 };
126
127 static bool
128 stringToValue(const LookupEntry tab[], const char *string,
129               unsigned int *value_rtrn)
130 {
131     const LookupEntry *entry;
132
133     if (!string)
134         return false;
135
136     for (entry = tab; entry->name; entry++) {
137         if (istreq(entry->name, string)) {
138             *value_rtrn = entry->value;
139             return true;
140         }
141     }
142
143     return false;
144 }
145
146 static const char *
147 valueToString(const LookupEntry tab[], unsigned int value)
148 {
149     const LookupEntry *entry;
150
151     for (entry = tab; entry->name; entry++)
152         if (entry->value == value)
153             return entry->name;
154
155     return "unknown";
156 }
157
158 static bool
159 stringToAction(const char *str, unsigned *type_rtrn)
160 {
161     return stringToValue(actionStrings, str, type_rtrn);
162 }
163
164 static bool
165 stringToField(const char *str, unsigned *field_rtrn)
166 {
167     return stringToValue(fieldStrings, str, field_rtrn);
168 }
169
170 static const char *
171 fieldText(unsigned field)
172 {
173     return valueToString(fieldStrings, field);
174 }
175
176 /***====================================================================***/
177
178 static inline bool
179 ReportMismatch(struct xkb_keymap *keymap, unsigned action, unsigned field,
180                const char *type)
181 {
182     log_err(keymap->ctx,
183             "Value of %s field must be of type %s; "
184             "Action %s definition ignored\n",
185             fieldText(field), type, ActionTypeText(action));
186     return false;
187 }
188
189 static inline bool
190 ReportIllegal(struct xkb_keymap *keymap, unsigned action, unsigned field)
191 {
192     log_err(keymap->ctx,
193             "Field %s is not defined for an action of type %s; "
194             "Action definition ignored\n",
195             fieldText(field), ActionTypeText(action));
196     return false;
197 }
198
199 static inline bool
200 ReportActionNotArray(struct xkb_keymap *keymap, unsigned action,
201                      unsigned field)
202 {
203     log_err(keymap->ctx,
204             "The %s field in the %s action is not an array; "
205             "Action definition ignored\n",
206             fieldText(field), ActionTypeText(action));
207     return false;
208 }
209
210 static inline bool
211 ReportNotFound(struct xkb_keymap *keymap, unsigned action, unsigned field,
212                const char *what, const char *bad)
213 {
214     log_err(keymap->ctx,
215             "%s named %s not found; "
216             "Ignoring the %s field of an %s action\n",
217             what, bad, fieldText(field), ActionTypeText(action));
218     return false;
219 }
220
221 static bool
222 HandleNoAction(struct xkb_keymap *keymap, struct xkb_any_action *action,
223                unsigned field, const ExprDef *array_ndx, const ExprDef *value)
224
225 {
226     return ReportIllegal(keymap, action->type, field);
227 }
228
229 static bool
230 CheckLatchLockFlags(struct xkb_keymap *keymap, unsigned action,
231                     unsigned field, const ExprDef * value,
232                     unsigned *flags_inout)
233 {
234     unsigned tmp;
235     bool result;
236
237     if (field == F_ClearLocks)
238         tmp = XkbSA_ClearLocks;
239     else if (field == F_LatchToLock)
240         tmp = XkbSA_LatchToLock;
241     else
242         return false;           /* WSGO! */
243
244     if (!ExprResolveBoolean(keymap->ctx, value, &result))
245         return ReportMismatch(keymap, action, field, "boolean");
246
247     if (result)
248         *flags_inout |= tmp;
249     else
250         *flags_inout &= ~tmp;
251
252     return true;
253 }
254
255 static bool
256 CheckModifierField(struct xkb_keymap *keymap, unsigned action,
257                    const ExprDef *value, unsigned *flags_inout,
258                    xkb_mod_mask_t *mods_rtrn)
259 {
260     if (value->op == EXPR_IDENT) {
261         const char *valStr;
262         valStr = xkb_atom_text(keymap->ctx, value->value.str);
263         if (valStr && (istreq(valStr, "usemodmapmods") ||
264                        istreq(valStr, "modmapmods"))) {
265
266             *mods_rtrn = 0;
267             *flags_inout |= XkbSA_UseModMapMods;
268             return true;
269         }
270     }
271
272     if (!ExprResolveVModMask(keymap, value, mods_rtrn))
273         return ReportMismatch(keymap, action, F_Modifiers, "modifier mask");
274
275     *flags_inout &= ~XkbSA_UseModMapMods;
276     return true;
277 }
278
279 static bool
280 HandleSetLatchMods(struct xkb_keymap *keymap, struct xkb_any_action *action,
281                    unsigned field, const ExprDef *array_ndx,
282                    const ExprDef *value)
283 {
284     struct xkb_mod_action *act;
285     unsigned rtrn;
286     unsigned t1;
287     xkb_mod_mask_t t2;
288
289     act = (struct xkb_mod_action *) action;
290     if (array_ndx != NULL) {
291         switch (field) {
292         case F_ClearLocks:
293         case F_LatchToLock:
294         case F_Modifiers:
295             return ReportActionNotArray(keymap, action->type, field);
296         }
297     }
298     switch (field) {
299     case F_ClearLocks:
300     case F_LatchToLock:
301         rtrn = act->flags;
302         if (CheckLatchLockFlags(keymap, action->type, field, value, &rtrn)) {
303             act->flags = rtrn;
304             return true;
305         }
306         return false;
307
308     case F_Modifiers:
309         t1 = act->flags;
310         if (CheckModifierField(keymap, action->type, value, &t1, &t2)) {
311             act->flags = t1;
312             act->real_mods = act->mask = (t2 & 0xff);
313             act->vmods = (t2 >> XkbNumModifiers) & 0xffff;
314             return true;
315         }
316         return false;
317     }
318     return ReportIllegal(keymap, action->type, field);
319 }
320
321 static bool
322 HandleLockMods(struct xkb_keymap *keymap, struct xkb_any_action *action,
323                unsigned field, const ExprDef *array_ndx, const ExprDef *value)
324 {
325     struct xkb_mod_action *act;
326     unsigned t1;
327     xkb_mod_mask_t t2;
328
329     act = (struct xkb_mod_action *) action;
330     if ((array_ndx != NULL) && (field == F_Modifiers))
331         return ReportActionNotArray(keymap, action->type, field);
332     switch (field) {
333     case F_Modifiers:
334         t1 = act->flags;
335         if (CheckModifierField(keymap, action->type, value, &t1, &t2)) {
336             act->flags = t1;
337             act->real_mods = act->mask = (t2 & 0xff);
338             act->vmods = (t2 >> XkbNumModifiers) & 0xffff;
339             return true;
340         }
341         return false;
342     }
343     return ReportIllegal(keymap, action->type, field);
344 }
345
346 static bool
347 CheckGroupField(struct xkb_keymap *keymap, unsigned action,
348                 const ExprDef *value, unsigned *flags_inout,
349                 xkb_group_index_t *grp_rtrn)
350 {
351     const ExprDef *spec;
352
353     if (value->op == EXPR_NEGATE || value->op == EXPR_UNARY_PLUS) {
354         *flags_inout &= ~XkbSA_GroupAbsolute;
355         spec = value->value.child;
356     }
357     else {
358         *flags_inout |= XkbSA_GroupAbsolute;
359         spec = value;
360     }
361
362     if (!ExprResolveGroup(keymap->ctx, spec, grp_rtrn))
363         return ReportMismatch(keymap, action, F_Group,
364                               "integer (range 1..8)");
365
366     if (value->op == EXPR_NEGATE)
367         *grp_rtrn = -*grp_rtrn;
368     else if (value->op != EXPR_UNARY_PLUS)
369         (*grp_rtrn)--;
370
371     return true;
372 }
373
374 static bool
375 HandleSetLatchGroup(struct xkb_keymap *keymap, struct xkb_any_action *action,
376                     unsigned field, const ExprDef *array_ndx,
377                     const ExprDef *value)
378 {
379     struct xkb_group_action *act;
380     unsigned rtrn;
381     unsigned t1;
382     xkb_group_index_t t2;
383
384     act = (struct xkb_group_action *) action;
385     if (array_ndx != NULL) {
386         switch (field) {
387         case F_ClearLocks:
388         case F_LatchToLock:
389         case F_Group:
390             return ReportActionNotArray(keymap, action->type, field);
391         }
392     }
393     switch (field) {
394     case F_ClearLocks:
395     case F_LatchToLock:
396         rtrn = act->flags;
397         if (CheckLatchLockFlags(keymap, action->type, field, value, &rtrn)) {
398             act->flags = rtrn;
399             return true;
400         }
401         return false;
402
403     case F_Group:
404         t1 = act->flags;
405         if (CheckGroupField(keymap, action->type, value, &t1, &t2)) {
406             act->flags = t1;
407             act->group = t2;
408             return true;
409         }
410         return false;
411     }
412     return ReportIllegal(keymap, action->type, field);
413 }
414
415 static bool
416 HandleLockGroup(struct xkb_keymap *keymap, struct xkb_any_action *action,
417                 unsigned field, const ExprDef *array_ndx,
418                 const ExprDef *value)
419 {
420     struct xkb_group_action *act;
421     unsigned t1;
422     xkb_group_index_t t2;
423
424     act = (struct xkb_group_action *) action;
425     if ((array_ndx != NULL) && (field == F_Group))
426         return ReportActionNotArray(keymap, action->type, field);
427     if (field == F_Group) {
428         t1 = act->flags;
429         if (CheckGroupField(keymap, action->type, value, &t1, &t2)) {
430             act->flags = t1;
431             act->group = t2;
432             return true;
433         }
434         return false;
435     }
436     return ReportIllegal(keymap, action->type, field);
437 }
438
439 static bool
440 HandleMovePtr(struct xkb_keymap *keymap, struct xkb_any_action *action,
441               unsigned field, const ExprDef *array_ndx, const ExprDef *value)
442 {
443     struct xkb_pointer_action *act;
444     bool absolute;
445
446     act = (struct xkb_pointer_action *) action;
447     if ((array_ndx != NULL) && ((field == F_X) || (field == F_Y)))
448         return ReportActionNotArray(keymap, action->type, field);
449
450     if (field == F_X || field == F_Y) {
451         int val;
452
453         if (value->op == EXPR_NEGATE || value->op == EXPR_UNARY_PLUS)
454             absolute = false;
455         else
456             absolute = true;
457
458         if (!ExprResolveInteger(keymap->ctx, value, &val))
459             return ReportMismatch(keymap, action->type, field, "integer");
460
461         if (field == F_X) {
462             if (absolute)
463                 act->flags |= XkbSA_MoveAbsoluteX;
464             act->x = val;
465         }
466         else {
467             if (absolute)
468                 act->flags |= XkbSA_MoveAbsoluteY;
469             act->y = val;
470         }
471
472         return true;
473     }
474     else if (field == F_Accel) {
475         bool set;
476
477         if (!ExprResolveBoolean(keymap->ctx, value, &set))
478             return ReportMismatch(keymap, action->type, field, "boolean");
479
480         if (set)
481             act->flags &= ~XkbSA_NoAcceleration;
482         else
483             act->flags |= XkbSA_NoAcceleration;
484     }
485
486     return ReportIllegal(keymap, action->type, field);
487 }
488
489 static const LookupEntry lockWhich[] = {
490     { "both", 0 },
491     { "lock", XkbSA_LockNoUnlock },
492     { "neither", (XkbSA_LockNoLock | XkbSA_LockNoUnlock) },
493     { "unlock", XkbSA_LockNoLock },
494     { NULL, 0 }
495 };
496
497 static bool
498 HandlePtrBtn(struct xkb_keymap *keymap, struct xkb_any_action *action,
499              unsigned field, const ExprDef *array_ndx, const ExprDef *value)
500 {
501     struct xkb_pointer_button_action *act;
502
503     act = (struct xkb_pointer_button_action *) action;
504     if (field == F_Button) {
505         int btn;
506
507         if (array_ndx)
508             return ReportActionNotArray(keymap, action->type, field);
509
510         if (!ExprResolveButton(keymap->ctx, value, &btn))
511             return ReportMismatch(keymap, action->type, field,
512                                   "integer (range 1..5)");
513
514         if (btn < 0 || btn > 5) {
515             log_err(keymap->ctx,
516                     "Button must specify default or be in the range 1..5; "
517                     "Illegal button value %d ignored\n", btn);
518             return false;
519         }
520
521         act->button = btn;
522         return true;
523     }
524     else if ((action->type == XkbSA_LockPtrBtn) && (field == F_Affect)) {
525         unsigned int val;
526
527         if (array_ndx)
528             return ReportActionNotArray(keymap, action->type, field);
529
530         if (!ExprResolveEnum(keymap->ctx, value, &val, lockWhich))
531             return ReportMismatch(keymap, action->type, field,
532                                   "lock or unlock");
533
534         act->flags &= ~(XkbSA_LockNoLock | XkbSA_LockNoUnlock);
535         act->flags |= val;
536         return true;
537     }
538     else if (field == F_Count) {
539         int btn;
540
541         if (array_ndx)
542             return ReportActionNotArray(keymap, action->type, field);
543
544         /* XXX: Should this actually be ResolveButton? */
545         if (!ExprResolveButton(keymap->ctx, value, &btn))
546             return ReportMismatch(keymap, action->type, field, "integer");
547
548         if (btn < 0 || btn > 255) {
549             log_err(keymap->ctx,
550                     "The count field must have a value in the range 0..255; "
551                     "Illegal count %d ignored\n", btn);
552             return false;
553         }
554
555         act->count = btn;
556         return true;
557     }
558     return ReportIllegal(keymap, action->type, field);
559 }
560
561 static const LookupEntry ptrDflts[] = {
562     { "dfltbtn", XkbSA_AffectDfltBtn },
563     { "defaultbutton", XkbSA_AffectDfltBtn },
564     { "button", XkbSA_AffectDfltBtn },
565     { NULL, 0 }
566 };
567
568 static bool
569 HandleSetPtrDflt(struct xkb_keymap *keymap, struct xkb_any_action *action,
570                  unsigned field, const ExprDef *array_ndx,
571                  const ExprDef *value)
572 {
573     struct xkb_pointer_default_action *act;
574
575     act = (struct xkb_pointer_default_action *) action;
576     if (field == F_Affect) {
577         unsigned int val;
578
579         if (array_ndx)
580             return ReportActionNotArray(keymap, action->type, field);
581
582         if (!ExprResolveEnum(keymap->ctx, value, &val, ptrDflts))
583             return ReportMismatch(keymap, action->type, field,
584                                   "pointer component");
585         act->affect = val;
586         return true;
587     }
588     else if ((field == F_Button) || (field == F_Value)) {
589         const ExprDef *button;
590         int btn;
591
592         if (array_ndx)
593             return ReportActionNotArray(keymap, action->type, field);
594
595         if (value->op == EXPR_NEGATE || value->op == EXPR_UNARY_PLUS) {
596             act->flags &= ~XkbSA_DfltBtnAbsolute;
597             button = value->value.child;
598         }
599         else {
600             act->flags |= XkbSA_DfltBtnAbsolute;
601             button = value;
602         }
603
604         if (!ExprResolveButton(keymap->ctx, button, &btn))
605             return ReportMismatch(keymap, action->type, field,
606                                   "integer (range 1..5)");
607
608         if (btn < 0 || btn > 5) {
609             log_err(keymap->ctx,
610                     "New default button value must be in the range 1..5; "
611                     "Illegal default button value %d ignored\n", btn);
612             return false;
613         }
614         if (btn == 0) {
615             log_err(keymap->ctx,
616                     "Cannot set default pointer button to \"default\"; "
617                     "Illegal default button setting ignored\n");
618             return false;
619         }
620
621         act->value = (value->op == EXPR_NEGATE ? -btn: btn);
622         return true;
623     }
624
625     return ReportIllegal(keymap, action->type, field);
626 }
627
628 static const LookupEntry isoNames[] = {
629     { "mods", XkbSA_ISONoAffectMods },
630     { "modifiers", XkbSA_ISONoAffectMods },
631     { "group", XkbSA_ISONoAffectGroup },
632     { "groups", XkbSA_ISONoAffectGroup },
633     { "ptr", XkbSA_ISONoAffectPtr },
634     { "pointer", XkbSA_ISONoAffectPtr },
635     { "ctrls", XkbSA_ISONoAffectCtrls },
636     { "controls", XkbSA_ISONoAffectCtrls },
637     { "all", ~((unsigned) 0) },
638     { "none", 0 },
639     { NULL, 0 },
640 };
641
642 static bool
643 HandleISOLock(struct xkb_keymap *keymap, struct xkb_any_action *action,
644               unsigned field, const ExprDef *array_ndx, const ExprDef *value)
645 {
646     struct xkb_iso_action *act;
647
648     act = (struct xkb_iso_action *) action;
649     if (field == F_Modifiers) {
650         unsigned flags;
651         xkb_mod_mask_t mods;
652
653         if (array_ndx)
654             return ReportActionNotArray(keymap, action->type, field);
655
656         flags = act->flags;
657         if (!CheckModifierField(keymap, action->type, value, &flags, &mods))
658             return false;
659
660         act->flags = flags & (~XkbSA_ISODfltIsGroup);
661         act->real_mods = mods & 0xff;
662         act->vmods = (mods >> XkbNumModifiers) & 0xffff;
663         return true;
664     }
665     else if (field == F_Group) {
666         xkb_group_index_t group;
667         unsigned flags;
668
669         if (array_ndx)
670             return ReportActionNotArray(keymap, action->type, field);
671
672         flags = act->flags;
673         if (!CheckGroupField(keymap, action->type, value, &flags, &group))
674             return false;
675
676         act->flags = flags | XkbSA_ISODfltIsGroup;
677         act->group = group;
678         return true;
679     } else if (F_Affect) {
680         xkb_mod_mask_t mask;
681
682         if (array_ndx)
683             return ReportActionNotArray(keymap, action->type, field);
684
685         if (!ExprResolveMask(keymap->ctx, value, &mask, isoNames))
686             return ReportMismatch(keymap, action->type, field,
687                                   "keyboard component");
688
689         act->affect = (~mask) & XkbSA_ISOAffectMask;
690         return true;
691     }
692
693     return ReportIllegal(keymap, action->type, field);
694 }
695
696 static bool
697 HandleSwitchScreen(struct xkb_keymap *keymap, struct xkb_any_action *action,
698                    unsigned field, const ExprDef *array_ndx,
699                    const ExprDef *value)
700 {
701     struct xkb_switch_screen_action *act;
702
703     act = (struct xkb_switch_screen_action *) action;
704     if (field == F_Screen) {
705         const ExprDef *scrn;
706         int val;
707
708         if (array_ndx)
709             return ReportActionNotArray(keymap, action->type, field);
710
711         if (value->op == EXPR_NEGATE || value->op == EXPR_UNARY_PLUS) {
712             act->flags &= ~XkbSA_SwitchAbsolute;
713             scrn = value->value.child;
714         }
715         else {
716             act->flags |= XkbSA_SwitchAbsolute;
717             scrn = value;
718         }
719
720         if (!ExprResolveInteger(keymap->ctx, scrn, &val))
721             return ReportMismatch(keymap, action->type, field,
722                                   "integer (0..255)");
723
724         if (val < 0 || val > 255) {
725             log_err(keymap->ctx,
726                     "Screen index must be in the range 1..255; "
727                     "Illegal screen value %d ignored\n", val);
728             return false;
729         }
730
731         act->screen = (value->op == EXPR_NEGATE ? -val : val);
732         return true;
733     }
734     else if (field == F_Same) {
735         bool set;
736
737         if (array_ndx)
738             return ReportActionNotArray(keymap, action->type, field);
739
740         if (!ExprResolveBoolean(keymap->ctx, value, &set))
741             return ReportMismatch(keymap, action->type, field, "boolean");
742
743         if (set)
744             act->flags &= ~XkbSA_SwitchApplication;
745         else
746             act->flags |= XkbSA_SwitchApplication;
747
748         return true;
749     }
750
751     return ReportIllegal(keymap, action->type, field);
752 }
753
754 const LookupEntry ctrlNames[] = {
755     { "repeatkeys", XkbRepeatKeysMask },
756     { "repeat", XkbRepeatKeysMask },
757     { "autorepeat", XkbRepeatKeysMask },
758     { "slowkeys", XkbSlowKeysMask },
759     { "bouncekeys", XkbBounceKeysMask },
760     { "stickykeys", XkbStickyKeysMask },
761     { "mousekeys", XkbMouseKeysMask },
762     { "mousekeysaccel", XkbMouseKeysAccelMask },
763     { "accessxkeys", XkbAccessXKeysMask },
764     { "accessxtimeout", XkbAccessXTimeoutMask },
765     { "accessxfeedback", XkbAccessXFeedbackMask },
766     { "audiblebell", XkbAudibleBellMask },
767     { "ignoregrouplock", XkbIgnoreGroupLockMask },
768     { "all", XkbAllBooleanCtrlsMask },
769     { "overlay1", 0 },
770     { "overlay2", 0 },
771     { "none", 0 },
772     { NULL, 0 }
773 };
774
775 static bool
776 HandleSetLockControls(struct xkb_keymap *keymap,
777                       struct xkb_any_action *action,
778                       unsigned field, const ExprDef *array_ndx,
779                       const ExprDef *value)
780 {
781     struct xkb_controls_action *act;
782
783     act = (struct xkb_controls_action *) action;
784     if (field == F_Controls) {
785         unsigned int mask;
786
787         if (array_ndx)
788             return ReportActionNotArray(keymap, action->type, field);
789
790         if (!ExprResolveMask(keymap->ctx, value, &mask, ctrlNames))
791             return ReportMismatch(keymap, action->type, field,
792                                   "controls mask");
793
794         act->ctrls = mask;
795         return true;
796     }
797
798     return ReportIllegal(keymap, action->type, field);
799 }
800
801 static const LookupEntry evNames[] = {
802     { "press", XkbSA_MessageOnPress },
803     { "keypress", XkbSA_MessageOnPress },
804     { "release", XkbSA_MessageOnRelease },
805     { "keyrelease", XkbSA_MessageOnRelease },
806     { "all", XkbSA_MessageOnPress | XkbSA_MessageOnRelease },
807     { "none", 0 },
808     { NULL, 0 }
809 };
810
811 static bool
812 HandleActionMessage(struct xkb_keymap *keymap, struct xkb_any_action *action,
813                     unsigned field, const ExprDef *array_ndx,
814                     const ExprDef *value)
815 {
816     struct xkb_message_action *act;
817
818     act = (struct xkb_message_action *) action;
819     if (field == F_Report) {
820         unsigned int mask;
821
822         if (array_ndx)
823             return ReportActionNotArray(keymap, action->type, field);
824
825         if (!ExprResolveMask(keymap->ctx, value, &mask, evNames))
826             return ReportMismatch(keymap, action->type, field,
827                                   "key event mask");
828
829         /* FIXME: Something seems wrong here... */
830         act->flags &= ~(XkbSA_MessageOnPress | XkbSA_MessageOnRelease);
831         act->flags = mask & (XkbSA_MessageOnPress | XkbSA_MessageOnRelease);
832         return true;
833     }
834     else if (field == F_GenKeyEvent) {
835         bool set;
836
837         if (array_ndx)
838             return ReportActionNotArray(keymap, action->type, field);
839
840         if (!ExprResolveBoolean(keymap->ctx, value, &set))
841             return ReportMismatch(keymap, action->type, field, "boolean");
842
843         if (set)
844             act->flags |= XkbSA_MessageGenKeyEvent;
845         else
846             act->flags &= ~XkbSA_MessageGenKeyEvent;
847
848         return true;
849     }
850     else if (field == F_Data && !array_ndx) {
851         const char *str;
852         int len;
853
854         if (!ExprResolveString(keymap->ctx, value, &str))
855             return ReportMismatch(keymap, action->type, field, "string");
856
857         len = strlen(str);
858         if (len < 1 || len > 6) {
859             log_warn(keymap->ctx,
860                      "An action message can hold only 6 bytes; "
861                      "Extra %d bytes ignored\n", len - 6);
862         }
863
864         strncpy((char *) act->message, str, 6);
865         return true;
866     }
867     else if (field == F_Data && array_ndx) {
868         int ndx, datum;
869
870         if (!ExprResolveInteger(keymap->ctx, array_ndx, &ndx)) {
871             log_err(keymap->ctx,
872                     "Array subscript must be integer; "
873                     "Illegal subscript ignored\n");
874             return false;
875         }
876
877         if (ndx < 0 || ndx > 5) {
878             log_err(keymap->ctx,
879                     "An action message is at most 6 bytes long; "
880                     "Attempt to use data[%d] ignored\n", ndx);
881             return false;
882         }
883
884         if (!ExprResolveInteger(keymap->ctx, value, &datum))
885             return ReportMismatch(keymap, action->type, field, "integer");
886
887         if (datum < 0 || datum > 255) {
888             log_err(keymap->ctx,
889                     "Message data must be in the range 0..255; "
890                     "Illegal datum %d ignored\n", datum);
891             return false;
892         }
893
894         act->message[ndx] = (uint8_t) datum;
895         return true;
896     }
897
898     return ReportIllegal(keymap, action->type, field);
899 }
900
901 static bool
902 HandleRedirectKey(struct xkb_keymap *keymap, struct xkb_any_action *action,
903                   unsigned field, const ExprDef *array_ndx,
904                   const ExprDef *value)
905 {
906     struct xkb_key *key;
907     struct xkb_redirect_key_action *act;
908     unsigned t1;
909     xkb_mod_mask_t t2;
910     unsigned long tmp;
911     char key_name[XkbKeyNameLength];
912
913     if (array_ndx != NULL)
914         return ReportActionNotArray(keymap, action->type, field);
915
916     act = (struct xkb_redirect_key_action *) action;
917     switch (field) {
918     case F_Keycode:
919         if (!ExprResolveKeyName(keymap->ctx, value, key_name))
920             return ReportMismatch(keymap, action->type, field, "key name");
921
922         tmp = KeyNameToLong(key_name);
923         key = FindNamedKey(keymap, tmp, true, CreateKeyNames(keymap), 0);
924         if (!key)
925             return ReportNotFound(keymap, action->type, field, "Key",
926                                   KeyNameText(key_name));
927         act->new_kc = XkbKeyGetKeycode(keymap, key);
928         return true;
929
930     case F_ModsToClear:
931     case F_Modifiers:
932         t1 = 0;
933         if (CheckModifierField(keymap, action->type, value, &t1, &t2)) {
934             act->mods_mask |= (t2 & 0xff);
935             if (field == F_Modifiers)
936                 act->mods |= (t2 & 0xff);
937             else
938                 act->mods &= ~(t2 & 0xff);
939
940             t2 = (t2 >> XkbNumModifiers) & 0xffff;
941             act->vmods_mask |= t2;
942             if (field == F_Modifiers)
943                 act->vmods |= t2;
944             else
945                 act->vmods &= ~t2;
946             return true;
947         }
948         return true;
949     }
950     return ReportIllegal(keymap, action->type, field);
951 }
952
953 static bool
954 HandleDeviceBtn(struct xkb_keymap *keymap, struct xkb_any_action *action,
955                 unsigned field, const ExprDef *array_ndx,
956                 const ExprDef *value)
957 {
958     struct xkb_device_button_action *act;
959
960     act = (struct xkb_device_button_action *) action;
961     if (field == F_Button) {
962         int val;
963
964         if (array_ndx)
965             return ReportActionNotArray(keymap, action->type, field);
966
967         if (!ExprResolveInteger(keymap->ctx, value, &val))
968             return ReportMismatch(keymap, action->type, field,
969                                   "integer (range 1..255)");
970
971         if (val < 0 || val > 255) {
972             log_err(keymap->ctx,
973                     "Button must specify default or be in the range 1..255; "
974                     "Illegal button value %d ignored\n", val);
975             return false;
976         }
977
978         act->button = val;
979         return true;
980     }
981     else if (action->type == XkbSA_LockDeviceBtn && field == F_Affect) {
982         unsigned int val;
983
984         if (array_ndx)
985             return ReportActionNotArray(keymap, action->type, field);
986
987         if (!ExprResolveEnum(keymap->ctx, value, &val, lockWhich))
988             return ReportMismatch(keymap, action->type, field,
989                                   "lock or unlock");
990
991         act->flags &= ~(XkbSA_LockNoLock | XkbSA_LockNoUnlock);
992         act->flags |= val;
993         return true;
994     }
995     else if (field == F_Count) {
996         int btn;
997
998         if (array_ndx)
999             return ReportActionNotArray(keymap, action->type, field);
1000
1001         /* XXX: Should this actually be ResolveButton? */
1002         if (!ExprResolveButton(keymap->ctx, value, &btn))
1003             return ReportMismatch(keymap, action->type, field, "integer");
1004
1005         if (btn < 0 || btn > 255) {
1006             log_err(keymap->ctx,
1007                     "The count field must have a value in the range 0..255; "
1008                     "Illegal count %d ignored\n", btn);
1009             return false;
1010         }
1011
1012         act->count = btn;
1013         return true;
1014     }
1015     else if (field == F_Device) {
1016         int val;
1017
1018         if (array_ndx)
1019             return ReportActionNotArray(keymap, action->type, field);
1020
1021         if (!ExprResolveInteger(keymap->ctx, value, &val))
1022             return ReportMismatch(keymap, action->type, field,
1023                                   "integer (range 1..255)");
1024
1025         if (val < 0 || val > 255) {
1026             log_err(keymap->ctx,
1027                     "Device must specify default or be in the range 1..255; "
1028                     "Illegal device value %d ignored\n", val);
1029             return false;
1030         }
1031
1032         act->device = val;
1033         return true;
1034     }
1035
1036     return ReportIllegal(keymap, action->type, field);
1037 }
1038
1039 static bool
1040 HandleDeviceValuator(struct xkb_keymap *keymap, struct xkb_any_action *action,
1041                      unsigned field, const ExprDef *array_ndx,
1042                      const ExprDef *value)
1043 {
1044 #if 0
1045     ExprResult rtrn;
1046     struct xkb_device_valuator_action *act;
1047
1048     act = (struct xkb_device_valuator_action *) action;
1049     /*  XXX - Not yet implemented */
1050 #endif
1051     return false;
1052 }
1053
1054 static bool
1055 HandlePrivate(struct xkb_keymap *keymap, struct xkb_any_action *action,
1056               unsigned field, const ExprDef *array_ndx, const ExprDef *value)
1057 {
1058     if (field == F_Type) {
1059         int type;
1060
1061         if (!ExprResolveInteger(keymap->ctx, value, &type))
1062             return ReportMismatch(keymap, PrivateAction, field, "integer");
1063
1064         if (type < 0 || type > 255) {
1065             log_err(keymap->ctx,
1066                     "Private action type must be in the range 0..255; "
1067                     "Illegal type %d ignored\n", type);
1068             return false;
1069         }
1070
1071         action->type = (uint8_t) type;
1072         return true;
1073     }
1074     else if (field == F_Data) {
1075         if (array_ndx == NULL) {
1076             const char *str;
1077             int len;
1078
1079             if (!ExprResolveString(keymap->ctx, value, &str))
1080                 return ReportMismatch(keymap, action->type, field, "string");
1081
1082             len = strlen(str);
1083             if (len < 1 || len > 7) {
1084                 log_warn(keymap->ctx,
1085                          "A private action has 7 data bytes; "
1086                          "Extra %d bytes ignored\n", len - 6);
1087                 return false;
1088             }
1089
1090             strncpy((char *) action->data, str, sizeof(action->data));
1091             return true;
1092         }
1093         else {
1094             int ndx, datum;
1095
1096             if (!ExprResolveInteger(keymap->ctx, array_ndx, &ndx)) {
1097                 log_err(keymap->ctx,
1098                         "Array subscript must be integer; "
1099                         "Illegal subscript ignored\n");
1100                 return false;
1101             }
1102
1103             if (ndx < 0 || ndx >= sizeof(action->data)) {
1104                 log_err(keymap->ctx,
1105                         "The data for a private action is %zu bytes long; "
1106                         "Attempt to use data[%d] ignored\n",
1107                         sizeof(action->data), ndx);
1108                 return false;
1109             }
1110
1111             if (!ExprResolveInteger(keymap->ctx, value, &datum))
1112                 return ReportMismatch(keymap, action->type, field, "integer");
1113
1114             if (datum < 0 || datum > 255) {
1115                 log_err(keymap->ctx,
1116                         "All data for a private action must be 0..255; "
1117                         "Illegal datum %d ignored\n", datum);
1118                 return false;
1119             }
1120
1121             action->data[ndx] = (uint8_t) datum;
1122             return true;
1123         }
1124     }
1125
1126     return ReportIllegal(keymap, PrivateAction, field);
1127 }
1128
1129 typedef bool (*actionHandler)(struct xkb_keymap *keymap,
1130                               struct xkb_any_action *action, unsigned field,
1131                               const ExprDef *array_ndx, const ExprDef *value);
1132
1133 static const actionHandler handleAction[XkbSA_NumActions + 1] = {
1134     [XkbSA_NoAction] = HandleNoAction,
1135     [XkbSA_SetMods] = HandleSetLatchMods,
1136     [XkbSA_LatchMods] = HandleSetLatchMods,
1137     [XkbSA_LockMods] = HandleLockMods,
1138     [XkbSA_SetGroup] = HandleSetLatchGroup,
1139     [XkbSA_LatchGroup] = HandleSetLatchGroup,
1140     [XkbSA_LockGroup] = HandleLockGroup,
1141     [XkbSA_MovePtr] = HandleMovePtr,
1142     [XkbSA_PtrBtn] = HandlePtrBtn,
1143     [XkbSA_LockPtrBtn] = HandlePtrBtn,
1144     [XkbSA_SetPtrDflt] = HandleSetPtrDflt,
1145     [XkbSA_ISOLock] = HandleISOLock,
1146     [XkbSA_Terminate] = HandleNoAction,
1147     [XkbSA_SwitchScreen] = HandleSwitchScreen,
1148     [XkbSA_SetControls] = HandleSetLockControls,
1149     [XkbSA_LockControls] = HandleSetLockControls,
1150     [XkbSA_ActionMessage] = HandleActionMessage,
1151     [XkbSA_RedirectKey] = HandleRedirectKey,
1152     [XkbSA_DeviceBtn] = HandleDeviceBtn,
1153     [XkbSA_LockDeviceBtn] = HandleDeviceBtn,
1154     [XkbSA_DeviceValuator] = HandleDeviceValuator,
1155     [PrivateAction] = HandlePrivate,
1156 };
1157
1158 /***====================================================================***/
1159
1160 static void
1161 ApplyActionFactoryDefaults(union xkb_action * action)
1162 {
1163     if (action->type == XkbSA_SetPtrDflt) { /* increment default button */
1164         action->dflt.affect = XkbSA_AffectDfltBtn;
1165         action->dflt.flags = 0;
1166         action->dflt.value = 1;
1167     }
1168     else if (action->type == XkbSA_ISOLock) {
1169         action->iso.real_mods = ModNameToIndex(XKB_MOD_NAME_CAPS);
1170     }
1171 }
1172
1173 int
1174 HandleActionDef(ExprDef * def,
1175                 struct xkb_keymap *keymap,
1176                 struct xkb_any_action *action, ActionInfo *info)
1177 {
1178     ExprDef *arg;
1179     const char *str;
1180     unsigned tmp, hndlrType;
1181
1182     if (def->op != EXPR_ACTION_DECL) {
1183         log_err(keymap->ctx, "Expected an action definition, found %s\n",
1184                 exprOpText(def->op));
1185         return false;
1186     }
1187     str = xkb_atom_text(keymap->ctx, def->value.action.name);
1188     if (!str) {
1189         log_wsgo(keymap->ctx, "Missing name in action definition!!\n");
1190         return false;
1191     }
1192     if (!stringToAction(str, &tmp)) {
1193         log_err(keymap->ctx, "Unknown action %s\n", str);
1194         return false;
1195     }
1196     action->type = hndlrType = tmp;
1197     if (action->type != XkbSA_NoAction) {
1198         ApplyActionFactoryDefaults((union xkb_action *) action);
1199         while (info)
1200         {
1201             if ((info->action == XkbSA_NoAction)
1202                 || (info->action == hndlrType)) {
1203                 if (!(*handleAction[hndlrType])(keymap, action,
1204                                                 info->field,
1205                                                 info->array_ndx,
1206                                                 info->value)) {
1207                     return false;
1208                 }
1209             }
1210             info = info->next;
1211         }
1212     }
1213     for (arg = def->value.action.args; arg != NULL;
1214          arg = (ExprDef *) arg->common.next) {
1215         const ExprDef *value;
1216         ExprDef *field, *arrayRtrn;
1217         const char *elemRtrn, *fieldRtrn;
1218         unsigned fieldNdx;
1219
1220         if (arg->op == EXPR_ASSIGN) {
1221             field = arg->value.binary.left;
1222             value = arg->value.binary.right;
1223         }
1224         else {
1225             if (arg->op == EXPR_NOT || arg->op == EXPR_INVERT) {
1226                 field = arg->value.child;
1227                 value = &constFalse;
1228             }
1229             else {
1230                 field = arg;
1231                 value = &constTrue;
1232             }
1233         }
1234         if (!ExprResolveLhs(keymap->ctx, field, &elemRtrn, &fieldRtrn,
1235                             &arrayRtrn))
1236             return false;       /* internal error -- already reported */
1237
1238         if (elemRtrn != NULL) {
1239             log_err(keymap->ctx,
1240                     "Cannot change defaults in an action definition; "
1241                     "Ignoring attempt to change %s.%s\n",
1242                     elemRtrn, fieldRtrn);
1243             return false;
1244         }
1245         if (!stringToField(fieldRtrn, &fieldNdx)) {
1246             log_err(keymap->ctx, "Unknown field name %s\n", fieldRtrn);
1247             return false;
1248         }
1249         if (!handleAction[hndlrType](keymap, action, fieldNdx, arrayRtrn,
1250                                      value))
1251             return false;
1252     }
1253     return true;
1254 }
1255
1256 /***====================================================================***/
1257
1258 int
1259 SetActionField(struct xkb_keymap *keymap, const char *elem, const char *field,
1260                ExprDef *array_ndx, ExprDef *value, ActionInfo **info_rtrn)
1261 {
1262     ActionInfo *new, *old;
1263
1264     new = malloc(sizeof(*new));
1265     if (!new) {
1266         log_wsgo(keymap->ctx, "Couldn't allocate space for action default\n");
1267         return false;
1268     }
1269
1270     if (istreq(elem, "action"))
1271         new->action = XkbSA_NoAction;
1272     else {
1273         if (!stringToAction(elem, &new->action)) {
1274             free(new);
1275             return false;
1276         }
1277         if (new->action == XkbSA_NoAction) {
1278             log_err(keymap->ctx,
1279                     "\"%s\" is not a valid field in a NoAction action\n",
1280                     field);
1281             free(new);
1282             return false;
1283         }
1284     }
1285     if (!stringToField(field, &new->field)) {
1286         log_err(keymap->ctx, "\"%s\" is not a legal field name\n", field);
1287         free(new);
1288         return false;
1289     }
1290     new->array_ndx = array_ndx;
1291     new->value = value;
1292     new->next = NULL;
1293     old = *info_rtrn;
1294     while ((old) && (old->next))
1295         old = old->next;
1296     if (old == NULL)
1297         *info_rtrn = new;
1298     else
1299         old->next = new;
1300     return true;
1301 }
1302
1303 /***====================================================================***/
1304
1305 union xkb_action *
1306 ResizeKeyActions(struct xkb_keymap *keymap, struct xkb_key *key,
1307                  uint32_t needed)
1308 {
1309     size_t old_ndx, old_num_acts, new_ndx;
1310
1311     if (needed == 0) {
1312         key->acts_index = 0;
1313         return NULL;
1314     }
1315
1316     if (XkbKeyHasActions(key) && key->width >= needed)
1317         return XkbKeyActionsPtr(keymap, key);
1318
1319     /*
1320      * The key may already be in the array, but without enough space.
1321      * This should not happen often, so in order to avoid moving and
1322      * copying stuff from acts and key_acts, we just allocate new
1323      * space for the key at the end, and leave the old space alone.
1324      */
1325
1326     old_ndx = key->acts_index;
1327     old_num_acts = XkbKeyNumActions(key);
1328     new_ndx = darray_size(keymap->acts);
1329
1330     darray_resize0(keymap->acts, new_ndx + needed);
1331     key->acts_index = new_ndx;
1332
1333     /*
1334      * The key was already in the array, copy the old actions to the
1335      * new space.
1336      */
1337     if (old_ndx != 0)
1338         memcpy(darray_mem(keymap->acts, new_ndx),
1339                darray_mem(keymap->acts, old_ndx),
1340                old_num_acts * sizeof(union xkb_action));
1341
1342     return XkbKeyActionsPtr(keymap, key);
1343 }