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