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