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