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