Convert macros to inline functions
[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 (strcasecmp(entry->name, string) == 0) {
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(unsigned action, unsigned field, const char *type)
170 {
171     ERROR("Value of %s field must be of type %s\n", fieldText(field), type);
172     ACTION("Action %s definition ignored\n", XkbcActionTypeText(action));
173     return false;
174 }
175
176 static inline bool
177 ReportIllegal(unsigned action, unsigned field)
178 {
179     ERROR("Field %s is not defined for an action of type %s\n",
180           fieldText(field), XkbcActionTypeText(action));
181     ACTION("Action definition ignored\n");
182     return false;
183 }
184
185 static inline bool
186 ReportActionNotArray(unsigned action, unsigned field)
187 {
188     ERROR("The %s field in the %s action is not an array\n",
189           fieldText(field), XkbcActionTypeText(action));
190     ACTION("Action definition ignored\n");
191     return false;
192 }
193
194 static inline bool
195 ReportNotFound(unsigned action, unsigned field, const char *what,
196                const char *bad)
197 {
198     ERROR("%s named %s not found\n", what, bad);
199     ACTION("Ignoring the %s field of an %s action\n", fieldText(field),
200            XkbcActionTypeText(action));
201     return false;
202 }
203
204 static bool
205 HandleNoAction(struct xkb_keymap *keymap, struct xkb_any_action *action,
206                unsigned field, ExprDef *array_ndx, ExprDef *value)
207 {
208     return ReportIllegal(action->type, field);
209 }
210
211 static bool
212 CheckLatchLockFlags(struct xkb_keymap *keymap, unsigned action,
213                     unsigned field, ExprDef * value, unsigned *flags_inout)
214 {
215     unsigned tmp;
216     ExprResult result;
217
218     if (field == F_ClearLocks)
219         tmp = XkbSA_ClearLocks;
220     else if (field == F_LatchToLock)
221         tmp = XkbSA_LatchToLock;
222     else
223         return false;           /* WSGO! */
224     if (!ExprResolveBoolean(keymap->ctx, value, &result))
225         return ReportMismatch(action, field, "boolean");
226     if (result.uval)
227         *flags_inout |= tmp;
228     else
229         *flags_inout &= ~tmp;
230     return true;
231 }
232
233 static bool
234 CheckModifierField(struct xkb_keymap *keymap, unsigned action, ExprDef *value,
235                    unsigned *flags_inout, unsigned *mods_rtrn)
236 {
237     ExprResult rtrn;
238
239     if (value->op == ExprIdent) {
240         const char *valStr;
241         valStr = xkb_atom_text(keymap->ctx, value->value.str);
242         if (valStr && ((strcasecmp(valStr, "usemodmapmods") == 0) ||
243                        (strcasecmp(valStr, "modmapmods") == 0))) {
244
245             *mods_rtrn = 0;
246             *flags_inout |= XkbSA_UseModMapMods;
247             return true;
248         }
249     }
250     if (!ExprResolveVModMask(value, &rtrn, keymap))
251         return ReportMismatch(action, F_Modifiers, "modifier mask");
252     *mods_rtrn = rtrn.uval;
253     *flags_inout &= ~XkbSA_UseModMapMods;
254     return true;
255 }
256
257 static bool
258 HandleSetLatchMods(struct xkb_keymap *keymap, struct xkb_any_action *action,
259                    unsigned field, ExprDef *array_ndx, ExprDef *value)
260 {
261     struct xkb_mod_action *act;
262     unsigned rtrn;
263     unsigned t1, t2;
264
265     act = (struct xkb_mod_action *) action;
266     if (array_ndx != NULL) {
267         switch (field) {
268         case F_ClearLocks:
269         case F_LatchToLock:
270         case F_Modifiers:
271             return ReportActionNotArray(action->type, field);
272         }
273     }
274     switch (field) {
275     case F_ClearLocks:
276     case F_LatchToLock:
277         rtrn = act->flags;
278         if (CheckLatchLockFlags(keymap, action->type, field, value, &rtrn)) {
279             act->flags = rtrn;
280             return true;
281         }
282         return false;
283
284     case F_Modifiers:
285         t1 = act->flags;
286         if (CheckModifierField(keymap, action->type, value, &t1, &t2)) {
287             act->flags = t1;
288             act->real_mods = act->mask = (t2 & 0xff);
289             act->vmods = (t2 >> 8) & 0xffff;
290             return true;
291         }
292         return false;
293     }
294     return ReportIllegal(action->type, field);
295 }
296
297 static bool
298 HandleLockMods(struct xkb_keymap *keymap, struct xkb_any_action *action,
299                unsigned field, ExprDef *array_ndx, ExprDef *value)
300 {
301     struct xkb_mod_action *act;
302     unsigned t1, t2;
303
304     act = (struct xkb_mod_action *) action;
305     if ((array_ndx != NULL) && (field == F_Modifiers))
306         return ReportActionNotArray(action->type, field);
307     switch (field) {
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 >> 8) & 0xffff;
314             return true;
315         }
316         return false;
317     }
318     return ReportIllegal(action->type, field);
319 }
320
321 static bool
322 CheckGroupField(struct xkb_keymap *keymap, unsigned action,
323                 ExprDef * value, unsigned *flags_inout, int *grp_rtrn)
324 {
325     ExprDef *spec;
326     ExprResult rtrn;
327
328     if ((value->op == OpNegate) || (value->op == OpUnaryPlus)) {
329         *flags_inout &= ~XkbSA_GroupAbsolute;
330         spec = value->value.child;
331     }
332     else {
333         *flags_inout |= XkbSA_GroupAbsolute;
334         spec = value;
335     }
336
337     if (!ExprResolveGroup(keymap->ctx, spec, &rtrn))
338         return ReportMismatch(action, F_Group, "integer (range 1..8)");
339     if (value->op == OpNegate)
340         *grp_rtrn = -rtrn.ival;
341     else if (value->op == OpUnaryPlus)
342         *grp_rtrn = rtrn.ival;
343     else
344         *grp_rtrn = rtrn.ival - 1;
345     return true;
346 }
347
348 static bool
349 HandleSetLatchGroup(struct xkb_keymap *keymap, struct xkb_any_action *action,
350                     unsigned field, ExprDef *array_ndx, ExprDef *value)
351 {
352     struct xkb_group_action *act;
353     unsigned rtrn;
354     unsigned t1;
355     int t2;
356
357     act = (struct xkb_group_action *) action;
358     if (array_ndx != NULL) {
359         switch (field) {
360         case F_ClearLocks:
361         case F_LatchToLock:
362         case F_Group:
363             return ReportActionNotArray(action->type, field);
364         }
365     }
366     switch (field) {
367     case F_ClearLocks:
368     case F_LatchToLock:
369         rtrn = act->flags;
370         if (CheckLatchLockFlags(keymap, action->type, field, value, &rtrn)) {
371             act->flags = rtrn;
372             return true;
373         }
374         return false;
375
376     case F_Group:
377         t1 = act->flags;
378         if (CheckGroupField(keymap, action->type, value, &t1, &t2)) {
379             act->flags = t1;
380             act->group = t2;
381             return true;
382         }
383         return false;
384     }
385     return ReportIllegal(action->type, field);
386 }
387
388 static bool
389 HandleLockGroup(struct xkb_keymap *keymap, struct xkb_any_action *action,
390                 unsigned field, ExprDef *array_ndx, ExprDef *value)
391 {
392     struct xkb_group_action *act;
393     unsigned t1;
394     int t2;
395
396     act = (struct xkb_group_action *) action;
397     if ((array_ndx != NULL) && (field == F_Group))
398         return ReportActionNotArray(action->type, field);
399     if (field == F_Group) {
400         t1 = act->flags;
401         if (CheckGroupField(keymap, action->type, value, &t1, &t2)) {
402             act->flags = t1;
403             act->group = t2;
404             return true;
405         }
406         return false;
407     }
408     return ReportIllegal(action->type, field);
409 }
410
411 static bool
412 HandleMovePtr(struct xkb_keymap *keymap, struct xkb_any_action *action,
413               unsigned field, ExprDef *array_ndx, ExprDef *value)
414 {
415     ExprResult rtrn;
416     struct xkb_pointer_action *act;
417     bool absolute;
418
419     act = (struct xkb_pointer_action *) action;
420     if ((array_ndx != NULL) && ((field == F_X) || (field == F_Y)))
421         return ReportActionNotArray(action->type, field);
422
423     if ((field == F_X) || (field == F_Y)) {
424         if ((value->op == OpNegate) || (value->op == OpUnaryPlus))
425             absolute = false;
426         else
427             absolute = true;
428         if (!ExprResolveInteger(keymap->ctx, value, &rtrn))
429             return ReportMismatch(action->type, field, "integer");
430         if (field == F_X) {
431             if (absolute)
432                 act->flags |= XkbSA_MoveAbsoluteX;
433             act->x = rtrn.ival;
434         }
435         else {
436             if (absolute)
437                 act->flags |= XkbSA_MoveAbsoluteY;
438             act->y = rtrn.ival;
439         }
440         return true;
441     }
442     else if (field == F_Accel) {
443         if (!ExprResolveBoolean(keymap->ctx, value, &rtrn))
444             return ReportMismatch(action->type, field, "boolean");
445         if (rtrn.uval)
446             act->flags &= ~XkbSA_NoAcceleration;
447         else
448             act->flags |= XkbSA_NoAcceleration;
449     }
450     return ReportIllegal(action->type, field);
451 }
452
453 static const LookupEntry lockWhich[] = {
454     { "both", 0 },
455     { "lock", XkbSA_LockNoUnlock },
456     { "neither", (XkbSA_LockNoLock | XkbSA_LockNoUnlock) },
457     { "unlock", XkbSA_LockNoLock },
458     { NULL, 0 }
459 };
460
461 static bool
462 HandlePtrBtn(struct xkb_keymap *keymap, struct xkb_any_action *action,
463              unsigned field, ExprDef *array_ndx, ExprDef *value)
464 {
465     ExprResult rtrn;
466     struct xkb_pointer_button_action *act;
467
468     act = (struct xkb_pointer_button_action *) action;
469     if (field == F_Button) {
470         if (array_ndx != NULL)
471             return ReportActionNotArray(action->type, field);
472         if (!ExprResolveButton(keymap->ctx, value, &rtrn))
473             return ReportMismatch(action->type, field,
474                                   "integer (range 1..5)");
475         if ((rtrn.ival < 0) || (rtrn.ival > 5)) {
476             ERROR("Button must specify default or be in the range 1..5\n");
477             ACTION("Illegal button value %d ignored\n", rtrn.ival);
478             return false;
479         }
480         act->button = rtrn.ival;
481         return true;
482     }
483     else if ((action->type == XkbSA_LockPtrBtn) && (field == F_Affect)) {
484         if (array_ndx != NULL)
485             return ReportActionNotArray(action->type, field);
486         if (!ExprResolveEnum(keymap->ctx, value, &rtrn, lockWhich))
487             return ReportMismatch(action->type, field, "lock or unlock");
488         act->flags &= ~(XkbSA_LockNoLock | XkbSA_LockNoUnlock);
489         act->flags |= rtrn.ival;
490         return true;
491     }
492     else if (field == F_Count) {
493         if (array_ndx != NULL)
494             return ReportActionNotArray(action->type, field);
495         if (!ExprResolveButton(keymap->ctx, value, &rtrn))
496             return ReportMismatch(action->type, field, "integer");
497         if ((rtrn.ival < 0) || (rtrn.ival > 255)) {
498             ERROR("The count field must have a value in the range 0..255\n");
499             ACTION("Illegal count %d ignored\n", rtrn.ival);
500             return false;
501         }
502         act->count = rtrn.ival;
503         return true;
504     }
505     return ReportIllegal(action->type, field);
506 }
507
508 static const LookupEntry ptrDflts[] = {
509     { "dfltbtn", XkbSA_AffectDfltBtn },
510     { "defaultbutton", XkbSA_AffectDfltBtn },
511     { "button", XkbSA_AffectDfltBtn },
512     { NULL, 0 }
513 };
514
515 static bool
516 HandleSetPtrDflt(struct xkb_keymap *keymap, struct xkb_any_action *action,
517                  unsigned field, ExprDef *array_ndx, ExprDef *value)
518 {
519     ExprResult rtrn;
520     struct xkb_pointer_default_action *act;
521
522     act = (struct xkb_pointer_default_action *) action;
523     if (field == F_Affect) {
524         if (array_ndx != NULL)
525             return ReportActionNotArray(action->type, field);
526         if (!ExprResolveEnum(keymap->ctx, value, &rtrn, ptrDflts))
527             return ReportMismatch(action->type, field, "pointer component");
528         act->affect = rtrn.uval;
529         return true;
530     }
531     else if ((field == F_Button) || (field == F_Value)) {
532         ExprDef *btn;
533         if (array_ndx != NULL)
534             return ReportActionNotArray(action->type, field);
535         if ((value->op == OpNegate) || (value->op == OpUnaryPlus)) {
536             act->flags &= ~XkbSA_DfltBtnAbsolute;
537             btn = value->value.child;
538         }
539         else {
540             act->flags |= XkbSA_DfltBtnAbsolute;
541             btn = value;
542         }
543
544         if (!ExprResolveButton(keymap->ctx, btn, &rtrn))
545             return ReportMismatch(action->type, field,
546                                   "integer (range 1..5)");
547         if ((rtrn.ival < 0) || (rtrn.ival > 5)) {
548             ERROR("New default button value must be in the range 1..5\n");
549             ACTION("Illegal default button value %d ignored\n", rtrn.ival);
550             return false;
551         }
552         if (rtrn.ival == 0) {
553             ERROR("Cannot set default pointer button to \"default\"\n");
554             ACTION("Illegal default button setting ignored\n");
555             return false;
556         }
557         if (value->op == OpNegate)
558             act->value = -rtrn.ival;
559         else
560             act->value = rtrn.ival;
561         return true;
562     }
563     return ReportIllegal(action->type, field);
564 }
565
566 static const LookupEntry isoNames[] = {
567     { "mods", XkbSA_ISONoAffectMods },
568     { "modifiers", XkbSA_ISONoAffectMods },
569     { "group", XkbSA_ISONoAffectGroup },
570     { "groups", XkbSA_ISONoAffectGroup },
571     { "ptr", XkbSA_ISONoAffectPtr },
572     { "pointer", XkbSA_ISONoAffectPtr },
573     { "ctrls", XkbSA_ISONoAffectCtrls },
574     { "controls", XkbSA_ISONoAffectCtrls },
575     { "all", ~((unsigned) 0) },
576     { "none", 0 },
577     { NULL, 0 },
578 };
579
580 static bool
581 HandleISOLock(struct xkb_keymap *keymap, struct xkb_any_action *action,
582               unsigned field, ExprDef *array_ndx, ExprDef *value)
583 {
584     ExprResult rtrn;
585     struct xkb_iso_action *act;
586     unsigned flags, mods;
587     int group;
588
589     act = (struct xkb_iso_action *) action;
590     switch (field) {
591     case F_Modifiers:
592         if (array_ndx != NULL)
593             return ReportActionNotArray(action->type, field);
594         flags = act->flags;
595         if (CheckModifierField(keymap, action->type, value, &flags, &mods)) {
596             act->flags = flags & (~XkbSA_ISODfltIsGroup);
597             act->real_mods = mods & 0xff;
598             act->vmods = (mods >> 8) & 0xff;
599             return true;
600         }
601         return false;
602
603     case F_Group:
604         if (array_ndx != NULL)
605             return ReportActionNotArray(action->type, field);
606         flags = act->flags;
607         if (CheckGroupField(keymap, action->type, value, &flags, &group)) {
608             act->flags = flags | XkbSA_ISODfltIsGroup;
609             act->group = group;
610             return true;
611         }
612         return false;
613
614     case F_Affect:
615         if (array_ndx != NULL)
616             return ReportActionNotArray(action->type, field);
617         if (!ExprResolveMask(keymap->ctx, value, &rtrn, isoNames))
618             return ReportMismatch(action->type, field, "keyboard component");
619         act->affect = (~rtrn.uval) & XkbSA_ISOAffectMask;
620         return true;
621     }
622     return ReportIllegal(action->type, field);
623 }
624
625 static bool
626 HandleSwitchScreen(struct xkb_keymap *keymap, struct xkb_any_action *action,
627                    unsigned field, ExprDef *array_ndx, ExprDef *value)
628 {
629     ExprResult rtrn;
630     struct xkb_switch_screen_action *act;
631
632     act = (struct xkb_switch_screen_action *) action;
633     if (field == F_Screen) {
634         ExprDef *scrn;
635         if (array_ndx != NULL)
636             return ReportActionNotArray(action->type, field);
637         if ((value->op == OpNegate) || (value->op == OpUnaryPlus)) {
638             act->flags &= ~XkbSA_SwitchAbsolute;
639             scrn = value->value.child;
640         }
641         else {
642             act->flags |= XkbSA_SwitchAbsolute;
643             scrn = value;
644         }
645
646         if (!ExprResolveInteger(keymap->ctx, scrn, &rtrn))
647             return ReportMismatch(action->type, field, "integer (0..255)");
648         if ((rtrn.ival < 0) || (rtrn.ival > 255)) {
649             ERROR("Screen index must be in the range 1..255\n");
650             ACTION("Illegal screen value %d ignored\n", rtrn.ival);
651             return false;
652         }
653         if (value->op == OpNegate)
654             act->screen = -rtrn.ival;
655         else
656             act->screen = rtrn.ival;
657         return true;
658     }
659     else if (field == F_Same) {
660         if (array_ndx != NULL)
661             return ReportActionNotArray(action->type, field);
662         if (!ExprResolveBoolean(keymap->ctx, value, &rtrn))
663             return ReportMismatch(action->type, field, "boolean");
664         if (rtrn.uval)
665             act->flags &= ~XkbSA_SwitchApplication;
666         else
667             act->flags |= XkbSA_SwitchApplication;
668         return true;
669     }
670     return ReportIllegal(action->type, field);
671 }
672
673 const LookupEntry ctrlNames[] = {
674     { "repeatkeys", XkbRepeatKeysMask },
675     { "repeat", XkbRepeatKeysMask },
676     { "autorepeat", XkbRepeatKeysMask },
677     { "slowkeys", XkbSlowKeysMask },
678     { "bouncekeys", XkbBounceKeysMask },
679     { "stickykeys", XkbStickyKeysMask },
680     { "mousekeys", XkbMouseKeysMask },
681     { "mousekeysaccel", XkbMouseKeysAccelMask },
682     { "accessxkeys", XkbAccessXKeysMask },
683     { "accessxtimeout", XkbAccessXTimeoutMask },
684     { "accessxfeedback", XkbAccessXFeedbackMask },
685     { "audiblebell", XkbAudibleBellMask },
686     { "ignoregrouplock", XkbIgnoreGroupLockMask },
687     { "all", XkbAllBooleanCtrlsMask },
688     { "overlay1", 0 },
689     { "overlay2", 0 },
690     { "none", 0 },
691     { NULL, 0 }
692 };
693
694 static bool
695 HandleSetLockControls(struct xkb_keymap *keymap,
696                       struct xkb_any_action *action,
697                       unsigned field, ExprDef *array_ndx,
698                       ExprDef *value)
699 {
700     ExprResult rtrn;
701     struct xkb_controls_action *act;
702
703     act = (struct xkb_controls_action *) action;
704     if (field == F_Controls) {
705         if (array_ndx != NULL)
706             return ReportActionNotArray(action->type, field);
707         if (!ExprResolveMask(keymap->ctx, value, &rtrn, ctrlNames))
708             return ReportMismatch(action->type, field, "controls mask");
709         act->ctrls = rtrn.uval;
710         return true;
711     }
712     return ReportIllegal(action->type, field);
713 }
714
715 static const LookupEntry evNames[] = {
716     { "press", XkbSA_MessageOnPress },
717     { "keypress", XkbSA_MessageOnPress },
718     { "release", XkbSA_MessageOnRelease },
719     { "keyrelease", XkbSA_MessageOnRelease },
720     { "all", XkbSA_MessageOnPress | XkbSA_MessageOnRelease },
721     { "none", 0 },
722     { NULL, 0 }
723 };
724
725 static bool
726 HandleActionMessage(struct xkb_keymap *keymap, struct xkb_any_action *action,
727                     unsigned field, ExprDef *array_ndx, ExprDef *value)
728 {
729     ExprResult rtrn;
730     struct xkb_message_action *act;
731
732     act = (struct xkb_message_action *) action;
733     switch (field) {
734     case F_Report:
735         if (array_ndx != NULL)
736             return ReportActionNotArray(action->type, field);
737         if (!ExprResolveMask(keymap->ctx, value, &rtrn, evNames))
738             return ReportMismatch(action->type, field, "key event mask");
739         act->flags &= ~(XkbSA_MessageOnPress | XkbSA_MessageOnRelease);
740         act->flags =
741             rtrn.uval & (XkbSA_MessageOnPress | XkbSA_MessageOnRelease);
742         return true;
743
744     case F_GenKeyEvent:
745         if (array_ndx != NULL)
746             return ReportActionNotArray(action->type, field);
747         if (!ExprResolveBoolean(keymap->ctx, value, &rtrn))
748             return ReportMismatch(action->type, field, "boolean");
749         if (rtrn.uval)
750             act->flags |= XkbSA_MessageGenKeyEvent;
751         else
752             act->flags &= ~XkbSA_MessageGenKeyEvent;
753         return true;
754
755     case F_Data:
756         if (array_ndx == NULL) {
757             if (!ExprResolveString(keymap->ctx, value, &rtrn))
758                 return ReportMismatch(action->type, field, "string");
759             else {
760                 int len = strlen(rtrn.str);
761                 if ((len < 1) || (len > 6)) {
762                     WARN("An action message can hold only 6 bytes\n");
763                     ACTION("Extra %d bytes ignored\n", len - 6);
764                 }
765                 strncpy((char *) act->message, rtrn.str, 6);
766             }
767             return true;
768         }
769         else {
770             unsigned ndx;
771             if (!ExprResolveInteger(keymap->ctx, array_ndx, &rtrn)) {
772                 ERROR("Array subscript must be integer\n");
773                 ACTION("Illegal subscript ignored\n");
774                 return false;
775             }
776             ndx = rtrn.uval;
777             if (ndx > 5) {
778                 ERROR("An action message is at most 6 bytes long\n");
779                 ACTION("Attempt to use data[%d] ignored\n", ndx);
780                 return false;
781             }
782             if (!ExprResolveInteger(keymap->ctx, value, &rtrn))
783                 return ReportMismatch(action->type, field, "integer");
784             if ((rtrn.ival < 0) || (rtrn.ival > 255)) {
785                 ERROR("Message data must be in the range 0..255\n");
786                 ACTION("Illegal datum %d ignored\n", rtrn.ival);
787                 return false;
788             }
789             act->message[ndx] = rtrn.uval;
790         }
791         return true;
792     }
793     return ReportIllegal(action->type, field);
794 }
795
796 static bool
797 HandleRedirectKey(struct xkb_keymap *keymap, struct xkb_any_action *action,
798                   unsigned field, ExprDef *array_ndx, ExprDef *value)
799 {
800     ExprResult rtrn;
801     struct xkb_key *key;
802     struct xkb_redirect_key_action *act;
803     unsigned t1, t2;
804     unsigned long tmp;
805
806     if (array_ndx != NULL)
807         return ReportActionNotArray(action->type, field);
808
809     act = (struct xkb_redirect_key_action *) action;
810     switch (field) {
811     case F_Keycode:
812         if (!ExprResolveKeyName(keymap->ctx, value, &rtrn))
813             return ReportMismatch(action->type, field, "key name");
814
815         tmp = KeyNameToLong(rtrn.name);
816         key = FindNamedKey(keymap, tmp, true, CreateKeyNames(keymap), 0);
817         if (!key)
818             return ReportNotFound(action->type, field, "Key",
819                                   XkbcKeyNameText(rtrn.name));
820         act->new_kc = XkbKeyGetKeycode(keymap, key);
821         return true;
822
823     case F_ModsToClear:
824     case F_Modifiers:
825         t1 = 0;
826         if (CheckModifierField(keymap, action->type, value, &t1, &t2)) {
827             act->mods_mask |= (t2 & 0xff);
828             if (field == F_Modifiers)
829                 act->mods |= (t2 & 0xff);
830             else
831                 act->mods &= ~(t2 & 0xff);
832
833             t2 = (t2 >> 8) & 0xffff;
834             act->vmods_mask |= t2;
835             if (field == F_Modifiers)
836                 act->vmods |= t2;
837             else
838                 act->vmods &= ~t2;
839             return true;
840         }
841         return true;
842     }
843     return ReportIllegal(action->type, field);
844 }
845
846 static bool
847 HandleDeviceBtn(struct xkb_keymap *keymap, struct xkb_any_action *action,
848                 unsigned field, ExprDef *array_ndx, ExprDef *value)
849 {
850     ExprResult rtrn;
851     struct xkb_device_button_action *act;
852
853     act = (struct xkb_device_button_action *) action;
854     if (field == F_Button) {
855         if (array_ndx != NULL)
856             return ReportActionNotArray(action->type, field);
857         if (!ExprResolveInteger(keymap->ctx, value, &rtrn))
858             return ReportMismatch(action->type, field,
859                                   "integer (range 1..255)");
860         if ((rtrn.ival < 0) || (rtrn.ival > 255)) {
861             ERROR("Button must specify default or be in the range 1..255\n");
862             ACTION("Illegal button value %d ignored\n", rtrn.ival);
863             return false;
864         }
865         act->button = rtrn.ival;
866         return true;
867     }
868     else if ((action->type == XkbSA_LockDeviceBtn) && (field == F_Affect)) {
869         if (array_ndx != NULL)
870             return ReportActionNotArray(action->type, field);
871         if (!ExprResolveEnum(keymap->ctx, value, &rtrn, lockWhich))
872             return ReportMismatch(action->type, field, "lock or unlock");
873         act->flags &= ~(XkbSA_LockNoLock | XkbSA_LockNoUnlock);
874         act->flags |= rtrn.ival;
875         return true;
876     }
877     else if (field == F_Count) {
878         if (array_ndx != NULL)
879             return ReportActionNotArray(action->type, field);
880         if (!ExprResolveButton(keymap->ctx, value, &rtrn))
881             return ReportMismatch(action->type, field, "integer");
882         if ((rtrn.ival < 0) || (rtrn.ival > 255)) {
883             ERROR("The count field must have a value in the range 0..255\n");
884             ACTION("Illegal count %d ignored\n", rtrn.ival);
885             return false;
886         }
887         act->count = rtrn.ival;
888         return true;
889     }
890     else if (field == F_Device) {
891         if (array_ndx != NULL)
892             return ReportActionNotArray(action->type, field);
893         if (!ExprResolveInteger(keymap->ctx, value, &rtrn))
894             return ReportMismatch(action->type, field,
895                                   "integer (range 1..255)");
896         if ((rtrn.ival < 0) || (rtrn.ival > 255)) {
897             ERROR("Device must specify default or be in the range 1..255\n");
898             ACTION("Illegal device value %d ignored\n", rtrn.ival);
899             return false;
900         }
901         act->device = rtrn.ival;
902         return true;
903     }
904     return ReportIllegal(action->type, field);
905 }
906
907 static bool
908 HandleDeviceValuator(struct xkb_keymap *keymap, struct xkb_any_action *action,
909                      unsigned field, ExprDef *array_ndx, ExprDef *value)
910 {
911 #if 0
912     ExprResult rtrn;
913     struct xkb_device_valuator_action *act;
914
915     act = (struct xkb_device_valuator_action *) action;
916     /*  XXX - Not yet implemented */
917 #endif
918     return false;
919 }
920
921 static bool
922 HandlePrivate(struct xkb_keymap *keymap, struct xkb_any_action *action,
923               unsigned field, ExprDef *array_ndx, ExprDef *value)
924 {
925     ExprResult rtrn;
926
927     switch (field) {
928     case F_Type:
929         if (!ExprResolveInteger(keymap->ctx, value, &rtrn))
930             return ReportMismatch(PrivateAction, field, "integer");
931         if ((rtrn.ival < 0) || (rtrn.ival > 255)) {
932             ERROR("Private action type must be in the range 0..255\n");
933             ACTION("Illegal type %d ignored\n", rtrn.ival);
934             return false;
935         }
936         action->type = rtrn.uval;
937         return true;
938
939     case F_Data:
940         if (array_ndx == NULL) {
941             if (!ExprResolveString(keymap->ctx, value, &rtrn))
942                 return ReportMismatch(action->type, field, "string");
943             else {
944                 int len = strlen(rtrn.str);
945                 if ((len < 1) || (len > 7)) {
946                     WARN("A private action has 7 data bytes\n");
947                     ACTION("Extra %d bytes ignored\n", len - 6);
948                     return false;
949                 }
950                 strncpy((char *) action->data, rtrn.str, sizeof action->data);
951             }
952             free(rtrn.str);
953             return true;
954         }
955         else {
956             unsigned ndx;
957             if (!ExprResolveInteger(keymap->ctx, array_ndx, &rtrn)) {
958                 ERROR("Array subscript must be integer\n");
959                 ACTION("Illegal subscript ignored\n");
960                 return false;
961             }
962             ndx = rtrn.uval;
963             if (ndx >= sizeof action->data) {
964                 ERROR("The data for a private action is 18 bytes long\n");
965                 ACTION("Attempt to use data[%d] ignored\n", ndx);
966                 return false;
967             }
968             if (!ExprResolveInteger(keymap->ctx, value, &rtrn))
969                 return ReportMismatch(action->type, field, "integer");
970             if ((rtrn.ival < 0) || (rtrn.ival > 255)) {
971                 ERROR("All data for a private action must be 0..255\n");
972                 ACTION("Illegal datum %d ignored\n", rtrn.ival);
973                 return false;
974             }
975             action->data[ndx] = rtrn.uval;
976             return true;
977         }
978     }
979     return ReportIllegal(PrivateAction, field);
980 }
981
982 typedef bool (*actionHandler)(struct xkb_keymap *keymap,
983                               struct xkb_any_action *action, unsigned field,
984                               ExprDef *array_ndx, ExprDef *value);
985
986 static const actionHandler handleAction[XkbSA_NumActions + 1] = {
987     [XkbSA_NoAction] = HandleNoAction,
988     [XkbSA_SetMods] = HandleSetLatchMods,
989     [XkbSA_LatchMods] = HandleSetLatchMods,
990     [XkbSA_LockMods] = HandleLockMods,
991     [XkbSA_SetGroup] = HandleSetLatchGroup,
992     [XkbSA_LatchGroup] = HandleSetLatchGroup,
993     [XkbSA_LockGroup] = HandleLockGroup,
994     [XkbSA_MovePtr] = HandleMovePtr,
995     [XkbSA_PtrBtn] = HandlePtrBtn,
996     [XkbSA_LockPtrBtn] = HandlePtrBtn,
997     [XkbSA_SetPtrDflt] = HandleSetPtrDflt,
998     [XkbSA_ISOLock] = HandleISOLock,
999     [XkbSA_Terminate] = HandleNoAction,
1000     [XkbSA_SwitchScreen] = HandleSwitchScreen,
1001     [XkbSA_SetControls] = HandleSetLockControls,
1002     [XkbSA_LockControls] = HandleSetLockControls,
1003     [XkbSA_ActionMessage] = HandleActionMessage,
1004     [XkbSA_RedirectKey] = HandleRedirectKey,
1005     [XkbSA_DeviceBtn] = HandleDeviceBtn,
1006     [XkbSA_LockDeviceBtn] = HandleDeviceBtn,
1007     [XkbSA_DeviceValuator] = HandleDeviceValuator,
1008     [PrivateAction] = HandlePrivate,
1009 };
1010
1011 /***====================================================================***/
1012
1013 static void
1014 ApplyActionFactoryDefaults(union xkb_action * action)
1015 {
1016     if (action->type == XkbSA_SetPtrDflt) { /* increment default button */
1017         action->dflt.affect = XkbSA_AffectDfltBtn;
1018         action->dflt.flags = 0;
1019         action->dflt.value = 1;
1020     }
1021     else if (action->type == XkbSA_ISOLock) {
1022         action->iso.real_mods = LockMask;
1023     }
1024 }
1025
1026 static void
1027 ActionsInit(struct xkb_context *ctx);
1028
1029 int
1030 HandleActionDef(ExprDef * def,
1031                 struct xkb_keymap *keymap,
1032                 struct xkb_any_action *action, ActionInfo *info)
1033 {
1034     ExprDef *arg;
1035     const char *str;
1036     unsigned tmp, hndlrType;
1037
1038     if (!actionsInitialized)
1039         ActionsInit(keymap->ctx);
1040
1041     if (def->op != ExprActionDecl) {
1042         ERROR("Expected an action definition, found %s\n",
1043               exprOpText(def->op));
1044         return false;
1045     }
1046     str = xkb_atom_text(keymap->ctx, def->value.action.name);
1047     if (!str) {
1048         WSGO("Missing name in action definition!!\n");
1049         return false;
1050     }
1051     if (!stringToAction(str, &tmp)) {
1052         ERROR("Unknown action %s\n", str);
1053         return false;
1054     }
1055     action->type = hndlrType = tmp;
1056     if (action->type != XkbSA_NoAction) {
1057         ApplyActionFactoryDefaults((union xkb_action *) action);
1058         while (info)
1059         {
1060             if ((info->action == XkbSA_NoAction)
1061                 || (info->action == hndlrType)) {
1062                 if (!(*handleAction[hndlrType])(keymap, action,
1063                                                 info->field,
1064                                                 info->array_ndx,
1065                                                 info->value)) {
1066                     return false;
1067                 }
1068             }
1069             info = info->next;
1070         }
1071     }
1072     for (arg = def->value.action.args; arg != NULL;
1073          arg = (ExprDef *) arg->common.next) {
1074         ExprDef *field, *value, *arrayRtrn;
1075         ExprResult elemRtrn, fieldRtrn;
1076         unsigned fieldNdx;
1077
1078         if (arg->op == OpAssign) {
1079             field = arg->value.binary.left;
1080             value = arg->value.binary.right;
1081         }
1082         else {
1083             if ((arg->op == OpNot) || (arg->op == OpInvert)) {
1084                 field = arg->value.child;
1085                 constFalse.value.str = xkb_atom_intern(keymap->ctx, "false");
1086                 value = &constFalse;
1087             }
1088             else {
1089                 field = arg;
1090                 constTrue.value.str = xkb_atom_intern(keymap->ctx, "true");
1091                 value = &constTrue;
1092             }
1093         }
1094         if (!ExprResolveLhs(keymap, field, &elemRtrn, &fieldRtrn, &arrayRtrn))
1095             return false;       /* internal error -- already reported */
1096
1097         if (elemRtrn.str != NULL) {
1098             ERROR("Cannot change defaults in an action definition\n");
1099             ACTION("Ignoring attempt to change %s.%s\n", elemRtrn.str,
1100                    fieldRtrn.str);
1101             free(elemRtrn.str);
1102             free(fieldRtrn.str);
1103             return false;
1104         }
1105         if (!stringToField(fieldRtrn.str, &fieldNdx)) {
1106             ERROR("Unknown field name %s\n", uStringText(fieldRtrn.str));
1107             free(elemRtrn.str);
1108             free(fieldRtrn.str);
1109             return false;
1110         }
1111         free(elemRtrn.str);
1112         free(fieldRtrn.str);
1113         if (!(*handleAction[hndlrType])(keymap, action, fieldNdx, arrayRtrn,
1114                                         value))
1115             return false;
1116     }
1117     return true;
1118 }
1119
1120 /***====================================================================***/
1121
1122 int
1123 SetActionField(struct xkb_keymap *keymap,
1124                char *elem,
1125                char *field,
1126                ExprDef * array_ndx, ExprDef * value, ActionInfo ** info_rtrn)
1127 {
1128     ActionInfo *new, *old;
1129
1130     if (!actionsInitialized)
1131         ActionsInit(keymap->ctx);
1132
1133     new = uTypedAlloc(ActionInfo);
1134     if (new == NULL) {
1135         WSGO("Couldn't allocate space for action default\n");
1136         return false;
1137     }
1138     if (strcasecmp(elem, "action") == 0)
1139         new->action = XkbSA_NoAction;
1140     else {
1141         if (!stringToAction(elem, &new->action)) {
1142             free(new);
1143             return false;
1144         }
1145         if (new->action == XkbSA_NoAction) {
1146             ERROR("\"%s\" is not a valid field in a NoAction action\n",
1147                   field);
1148             free(new);
1149             return false;
1150         }
1151     }
1152     if (!stringToField(field, &new->field)) {
1153         ERROR("\"%s\" is not a legal field name\n", field);
1154         free(new);
1155         return false;
1156     }
1157     new->array_ndx = array_ndx;
1158     new->value = value;
1159     new->next = NULL;
1160     old = *info_rtrn;
1161     while ((old) && (old->next))
1162         old = old->next;
1163     if (old == NULL)
1164         *info_rtrn = new;
1165     else
1166         old->next = new;
1167     return true;
1168 }
1169
1170 /***====================================================================***/
1171
1172 static void
1173 ActionsInit(struct xkb_context *ctx)
1174 {
1175     if (!actionsInitialized) {
1176         memset(&constTrue, 0, sizeof(constTrue));
1177         memset(&constFalse, 0, sizeof(constFalse));
1178         constTrue.common.stmtType = StmtExpr;
1179         constTrue.common.next = NULL;
1180         constTrue.op = ExprIdent;
1181         constTrue.type = TypeBoolean;
1182         constTrue.value.str = xkb_atom_intern(ctx, "true");
1183         constFalse.common.stmtType = StmtExpr;
1184         constFalse.common.next = NULL;
1185         constFalse.op = ExprIdent;
1186         constFalse.type = TypeBoolean;
1187         constFalse.value.str = xkb_atom_intern(ctx, "false");
1188         actionsInitialized = 1;
1189     }
1190 }