action: get rid of xkb_any_action
[platform/upstream/libxkbcommon.git] / src / xkbcomp / action.c
1 /************************************************************
2  * Copyright (c) 1994 by Silicon Graphics Computer Systems, Inc.
3  *
4  * Permission to use, copy, modify, and distribute this
5  * software and its documentation for any purpose and without
6  * fee is hereby granted, provided that the above copyright
7  * notice appear in all copies and that both that copyright
8  * notice and this permission notice appear in supporting
9  * documentation, and that the name of Silicon Graphics not be
10  * used in advertising or publicity pertaining to distribution
11  * of the software without specific prior written permission.
12  * Silicon Graphics makes no representation about the suitability
13  * of this software for any purpose. It is provided "as is"
14  * without any express or implied warranty.
15  *
16  * SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
17  * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
18  * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON
19  * GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
20  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
21  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
22  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION  WITH
23  * THE USE OR PERFORMANCE OF THIS SOFTWARE.
24  *
25  ********************************************************/
26
27 #include "action.h"
28
29 static const ExprDef constTrue = {
30     .common = { .type = STMT_EXPR, .next = NULL },
31     .op = EXPR_VALUE,
32     .value_type = EXPR_TYPE_BOOLEAN,
33     .value = { .ival = 1 },
34 };
35
36 static const ExprDef constFalse = {
37     .common = { .type = STMT_EXPR, .next = NULL },
38     .op = EXPR_VALUE,
39     .value_type = EXPR_TYPE_BOOLEAN,
40     .value = { .ival = 0 },
41 };
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 int *value_rtrn)
130 {
131     const LookupEntry *entry;
132
133     if (!string)
134         return false;
135
136     for (entry = tab; entry->name; entry++) {
137         if (istreq(entry->name, string)) {
138             *value_rtrn = entry->value;
139             return true;
140         }
141     }
142
143     return false;
144 }
145
146 static const char *
147 valueToString(const LookupEntry tab[], unsigned int value)
148 {
149     const LookupEntry *entry;
150
151     for (entry = tab; entry->name; entry++)
152         if (entry->value == 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 inline bool
179 ReportMismatch(struct xkb_keymap *keymap, unsigned action, unsigned field,
180                const char *type)
181 {
182     log_err(keymap->ctx,
183             "Value of %s field must be of type %s; "
184             "Action %s definition ignored\n",
185             fieldText(field), type, ActionTypeText(action));
186     return false;
187 }
188
189 static inline bool
190 ReportIllegal(struct xkb_keymap *keymap, unsigned action, unsigned field)
191 {
192     log_err(keymap->ctx,
193             "Field %s is not defined for an action of type %s; "
194             "Action definition ignored\n",
195             fieldText(field), ActionTypeText(action));
196     return false;
197 }
198
199 static inline bool
200 ReportActionNotArray(struct xkb_keymap *keymap, unsigned action,
201                      unsigned field)
202 {
203     log_err(keymap->ctx,
204             "The %s field in the %s action is not an array; "
205             "Action definition ignored\n",
206             fieldText(field), ActionTypeText(action));
207     return false;
208 }
209
210 static inline bool
211 ReportNotFound(struct xkb_keymap *keymap, unsigned action, unsigned field,
212                const char *what, const char *bad)
213 {
214     log_err(keymap->ctx,
215             "%s named %s not found; "
216             "Ignoring the %s field of an %s action\n",
217             what, bad, fieldText(field), ActionTypeText(action));
218     return false;
219 }
220
221 static bool
222 HandleNoAction(struct xkb_keymap *keymap, union xkb_action *action,
223                unsigned field, const ExprDef *array_ndx, const ExprDef *value)
224
225 {
226     return ReportIllegal(keymap, action->type, field);
227 }
228
229 static bool
230 CheckLatchLockFlags(struct xkb_keymap *keymap, unsigned action,
231                     unsigned field, const ExprDef * value,
232                     unsigned *flags_inout)
233 {
234     unsigned tmp;
235     bool result;
236
237     if (field == F_ClearLocks)
238         tmp = XkbSA_ClearLocks;
239     else if (field == F_LatchToLock)
240         tmp = XkbSA_LatchToLock;
241     else
242         return false;           /* WSGO! */
243
244     if (!ExprResolveBoolean(keymap->ctx, value, &result))
245         return ReportMismatch(keymap, action, field, "boolean");
246
247     if (result)
248         *flags_inout |= tmp;
249     else
250         *flags_inout &= ~tmp;
251
252     return true;
253 }
254
255 static bool
256 CheckModifierField(struct xkb_keymap *keymap, unsigned action,
257                    const ExprDef *value, unsigned *flags_inout,
258                    xkb_mod_mask_t *mods_rtrn)
259 {
260     if (value->op == EXPR_IDENT) {
261         const char *valStr;
262         valStr = xkb_atom_text(keymap->ctx, value->value.str);
263         if (valStr && (istreq(valStr, "usemodmapmods") ||
264                        istreq(valStr, "modmapmods"))) {
265
266             *mods_rtrn = 0;
267             *flags_inout |= XkbSA_UseModMapMods;
268             return true;
269         }
270     }
271
272     if (!ExprResolveVModMask(keymap, value, mods_rtrn))
273         return ReportMismatch(keymap, action, F_Modifiers, "modifier mask");
274
275     *flags_inout &= ~XkbSA_UseModMapMods;
276     return true;
277 }
278
279 static bool
280 HandleSetLatchMods(struct xkb_keymap *keymap, union xkb_action *action,
281                    unsigned field, const ExprDef *array_ndx,
282                    const ExprDef *value)
283 {
284     struct xkb_mod_action *act = &action->mods;
285     unsigned rtrn;
286     unsigned t1;
287     xkb_mod_mask_t t2;
288
289     if (array_ndx != NULL) {
290         switch (field) {
291         case F_ClearLocks:
292         case F_LatchToLock:
293         case F_Modifiers:
294             return ReportActionNotArray(keymap, action->type, field);
295         }
296     }
297     switch (field) {
298     case F_ClearLocks:
299     case F_LatchToLock:
300         rtrn = act->flags;
301         if (CheckLatchLockFlags(keymap, action->type, field, value, &rtrn)) {
302             act->flags = rtrn;
303             return true;
304         }
305         return false;
306
307     case F_Modifiers:
308         t1 = act->flags;
309         if (CheckModifierField(keymap, action->type, value, &t1, &t2)) {
310             act->flags = t1;
311             act->mods.mods = t2;
312             return true;
313         }
314         return false;
315     }
316     return ReportIllegal(keymap, action->type, field);
317 }
318
319 static bool
320 HandleLockMods(struct xkb_keymap *keymap, union xkb_action *action,
321                unsigned field, const ExprDef *array_ndx, const ExprDef *value)
322 {
323     struct xkb_mod_action *act = &action->mods;
324     unsigned t1;
325     xkb_mod_mask_t t2;
326
327     if ((array_ndx != NULL) && (field == F_Modifiers))
328         return ReportActionNotArray(keymap, action->type, field);
329     switch (field) {
330     case F_Modifiers:
331         t1 = act->flags;
332         if (CheckModifierField(keymap, action->type, value, &t1, &t2)) {
333             act->flags = t1;
334             act->mods.mods = t2;
335             return true;
336         }
337         return false;
338     }
339     return ReportIllegal(keymap, action->type, field);
340 }
341
342 static bool
343 CheckGroupField(struct xkb_keymap *keymap, unsigned action,
344                 const ExprDef *value, unsigned *flags_inout,
345                 xkb_group_index_t *grp_rtrn)
346 {
347     const ExprDef *spec;
348
349     if (value->op == EXPR_NEGATE || value->op == EXPR_UNARY_PLUS) {
350         *flags_inout &= ~XkbSA_GroupAbsolute;
351         spec = value->value.child;
352     }
353     else {
354         *flags_inout |= XkbSA_GroupAbsolute;
355         spec = value;
356     }
357
358     if (!ExprResolveGroup(keymap->ctx, spec, grp_rtrn))
359         return ReportMismatch(keymap, action, F_Group,
360                               "integer (range 1..8)");
361
362     if (value->op == EXPR_NEGATE)
363         *grp_rtrn = -*grp_rtrn;
364     else if (value->op != EXPR_UNARY_PLUS)
365         (*grp_rtrn)--;
366
367     return true;
368 }
369
370 static bool
371 HandleSetLatchGroup(struct xkb_keymap *keymap, union xkb_action *action,
372                     unsigned field, const ExprDef *array_ndx,
373                     const ExprDef *value)
374 {
375     struct xkb_group_action *act = &action->group;
376     unsigned rtrn;
377     unsigned t1;
378     xkb_group_index_t t2;
379
380     if (array_ndx != NULL) {
381         switch (field) {
382         case F_ClearLocks:
383         case F_LatchToLock:
384         case F_Group:
385             return ReportActionNotArray(keymap, action->type, field);
386         }
387     }
388     switch (field) {
389     case F_ClearLocks:
390     case F_LatchToLock:
391         rtrn = act->flags;
392         if (CheckLatchLockFlags(keymap, action->type, field, value, &rtrn)) {
393             act->flags = rtrn;
394             return true;
395         }
396         return false;
397
398     case F_Group:
399         t1 = act->flags;
400         if (CheckGroupField(keymap, action->type, value, &t1, &t2)) {
401             act->flags = t1;
402             act->group = t2;
403             return true;
404         }
405         return false;
406     }
407     return ReportIllegal(keymap, action->type, field);
408 }
409
410 static bool
411 HandleLockGroup(struct xkb_keymap *keymap, union xkb_action *action,
412                 unsigned field, const ExprDef *array_ndx,
413                 const ExprDef *value)
414 {
415     struct xkb_group_action *act = &action->group;
416     unsigned t1;
417     xkb_group_index_t t2;
418
419     if ((array_ndx != NULL) && (field == F_Group))
420         return ReportActionNotArray(keymap, action->type, field);
421     if (field == F_Group) {
422         t1 = act->flags;
423         if (CheckGroupField(keymap, action->type, value, &t1, &t2)) {
424             act->flags = t1;
425             act->group = t2;
426             return true;
427         }
428         return false;
429     }
430     return ReportIllegal(keymap, action->type, field);
431 }
432
433 static bool
434 HandleMovePtr(struct xkb_keymap *keymap, union xkb_action *action,
435               unsigned field, const ExprDef *array_ndx, const ExprDef *value)
436 {
437     struct xkb_pointer_action *act = &action->ptr;
438     bool absolute;
439
440     if ((array_ndx != NULL) && ((field == F_X) || (field == F_Y)))
441         return ReportActionNotArray(keymap, action->type, field);
442
443     if (field == F_X || field == F_Y) {
444         int val;
445
446         if (value->op == EXPR_NEGATE || value->op == EXPR_UNARY_PLUS)
447             absolute = false;
448         else
449             absolute = true;
450
451         if (!ExprResolveInteger(keymap->ctx, value, &val))
452             return ReportMismatch(keymap, action->type, field, "integer");
453
454         if (field == F_X) {
455             if (absolute)
456                 act->flags |= XkbSA_MoveAbsoluteX;
457             act->x = val;
458         }
459         else {
460             if (absolute)
461                 act->flags |= XkbSA_MoveAbsoluteY;
462             act->y = val;
463         }
464
465         return true;
466     }
467     else if (field == F_Accel) {
468         bool set;
469
470         if (!ExprResolveBoolean(keymap->ctx, value, &set))
471             return ReportMismatch(keymap, action->type, field, "boolean");
472
473         if (set)
474             act->flags &= ~XkbSA_NoAcceleration;
475         else
476             act->flags |= XkbSA_NoAcceleration;
477     }
478
479     return ReportIllegal(keymap, action->type, field);
480 }
481
482 static const LookupEntry lockWhich[] = {
483     { "both", 0 },
484     { "lock", XkbSA_LockNoUnlock },
485     { "neither", (XkbSA_LockNoLock | XkbSA_LockNoUnlock) },
486     { "unlock", XkbSA_LockNoLock },
487     { NULL, 0 }
488 };
489
490 static bool
491 HandlePtrBtn(struct xkb_keymap *keymap, union xkb_action *action,
492              unsigned field, const ExprDef *array_ndx, const ExprDef *value)
493 {
494     struct xkb_pointer_button_action *act = &action->btn;
495
496     if (field == F_Button) {
497         int btn;
498
499         if (array_ndx)
500             return ReportActionNotArray(keymap, action->type, field);
501
502         if (!ExprResolveButton(keymap->ctx, value, &btn))
503             return ReportMismatch(keymap, action->type, field,
504                                   "integer (range 1..5)");
505
506         if (btn < 0 || btn > 5) {
507             log_err(keymap->ctx,
508                     "Button must specify default or be in the range 1..5; "
509                     "Illegal button value %d ignored\n", btn);
510             return false;
511         }
512
513         act->button = btn;
514         return true;
515     }
516     else if ((action->type == XkbSA_LockPtrBtn) && (field == F_Affect)) {
517         unsigned int val;
518
519         if (array_ndx)
520             return ReportActionNotArray(keymap, action->type, field);
521
522         if (!ExprResolveEnum(keymap->ctx, value, &val, lockWhich))
523             return ReportMismatch(keymap, action->type, field,
524                                   "lock or unlock");
525
526         act->flags &= ~(XkbSA_LockNoLock | XkbSA_LockNoUnlock);
527         act->flags |= val;
528         return true;
529     }
530     else if (field == F_Count) {
531         int btn;
532
533         if (array_ndx)
534             return ReportActionNotArray(keymap, action->type, field);
535
536         /* XXX: Should this actually be ResolveButton? */
537         if (!ExprResolveButton(keymap->ctx, value, &btn))
538             return ReportMismatch(keymap, action->type, field, "integer");
539
540         if (btn < 0 || btn > 255) {
541             log_err(keymap->ctx,
542                     "The count field must have a value in the range 0..255; "
543                     "Illegal count %d ignored\n", btn);
544             return false;
545         }
546
547         act->count = btn;
548         return true;
549     }
550     return ReportIllegal(keymap, action->type, field);
551 }
552
553 static const LookupEntry ptrDflts[] = {
554     { "dfltbtn", XkbSA_AffectDfltBtn },
555     { "defaultbutton", XkbSA_AffectDfltBtn },
556     { "button", XkbSA_AffectDfltBtn },
557     { NULL, 0 }
558 };
559
560 static bool
561 HandleSetPtrDflt(struct xkb_keymap *keymap, union xkb_action *action,
562                  unsigned field, const ExprDef *array_ndx,
563                  const ExprDef *value)
564 {
565     struct xkb_pointer_default_action *act = &action->dflt;
566
567     if (field == F_Affect) {
568         unsigned int val;
569
570         if (array_ndx)
571             return ReportActionNotArray(keymap, action->type, field);
572
573         if (!ExprResolveEnum(keymap->ctx, value, &val, ptrDflts))
574             return ReportMismatch(keymap, action->type, field,
575                                   "pointer component");
576         act->affect = val;
577         return true;
578     }
579     else if ((field == F_Button) || (field == F_Value)) {
580         const ExprDef *button;
581         int btn;
582
583         if (array_ndx)
584             return ReportActionNotArray(keymap, action->type, field);
585
586         if (value->op == EXPR_NEGATE || value->op == EXPR_UNARY_PLUS) {
587             act->flags &= ~XkbSA_DfltBtnAbsolute;
588             button = value->value.child;
589         }
590         else {
591             act->flags |= XkbSA_DfltBtnAbsolute;
592             button = value;
593         }
594
595         if (!ExprResolveButton(keymap->ctx, button, &btn))
596             return ReportMismatch(keymap, action->type, field,
597                                   "integer (range 1..5)");
598
599         if (btn < 0 || btn > 5) {
600             log_err(keymap->ctx,
601                     "New default button value must be in the range 1..5; "
602                     "Illegal default button value %d ignored\n", btn);
603             return false;
604         }
605         if (btn == 0) {
606             log_err(keymap->ctx,
607                     "Cannot set default pointer button to \"default\"; "
608                     "Illegal default button setting ignored\n");
609             return false;
610         }
611
612         act->value = (value->op == EXPR_NEGATE ? -btn: btn);
613         return true;
614     }
615
616     return ReportIllegal(keymap, action->type, field);
617 }
618
619 static const LookupEntry isoNames[] = {
620     { "mods", XkbSA_ISONoAffectMods },
621     { "modifiers", XkbSA_ISONoAffectMods },
622     { "group", XkbSA_ISONoAffectGroup },
623     { "groups", XkbSA_ISONoAffectGroup },
624     { "ptr", XkbSA_ISONoAffectPtr },
625     { "pointer", XkbSA_ISONoAffectPtr },
626     { "ctrls", XkbSA_ISONoAffectCtrls },
627     { "controls", XkbSA_ISONoAffectCtrls },
628     { "all", ~((unsigned) 0) },
629     { "none", 0 },
630     { NULL, 0 },
631 };
632
633 static bool
634 HandleISOLock(struct xkb_keymap *keymap, union xkb_action *action,
635               unsigned field, const ExprDef *array_ndx, const ExprDef *value)
636 {
637     struct xkb_iso_action *act = &action->iso;
638
639     if (field == F_Modifiers) {
640         unsigned flags;
641         xkb_mod_mask_t mods;
642
643         if (array_ndx)
644             return ReportActionNotArray(keymap, action->type, field);
645
646         flags = act->flags;
647         if (!CheckModifierField(keymap, action->type, value, &flags, &mods))
648             return false;
649
650         act->flags = flags & (~XkbSA_ISODfltIsGroup);
651         act->mods.mods = mods;
652         return true;
653     }
654     else if (field == F_Group) {
655         xkb_group_index_t group;
656         unsigned flags;
657
658         if (array_ndx)
659             return ReportActionNotArray(keymap, action->type, field);
660
661         flags = act->flags;
662         if (!CheckGroupField(keymap, action->type, value, &flags, &group))
663             return false;
664
665         act->flags = flags | XkbSA_ISODfltIsGroup;
666         act->group = group;
667         return true;
668     } else if (F_Affect) {
669         xkb_mod_mask_t mask;
670
671         if (array_ndx)
672             return ReportActionNotArray(keymap, action->type, field);
673
674         if (!ExprResolveMask(keymap->ctx, value, &mask, isoNames))
675             return ReportMismatch(keymap, action->type, field,
676                                   "keyboard component");
677
678         act->affect = (~mask) & XkbSA_ISOAffectMask;
679         return true;
680     }
681
682     return ReportIllegal(keymap, action->type, field);
683 }
684
685 static bool
686 HandleSwitchScreen(struct xkb_keymap *keymap, union xkb_action *action,
687                    unsigned field, const ExprDef *array_ndx,
688                    const ExprDef *value)
689 {
690     struct xkb_switch_screen_action *act = &action->screen;
691
692     if (field == F_Screen) {
693         const ExprDef *scrn;
694         int val;
695
696         if (array_ndx)
697             return ReportActionNotArray(keymap, action->type, field);
698
699         if (value->op == EXPR_NEGATE || value->op == EXPR_UNARY_PLUS) {
700             act->flags &= ~XkbSA_SwitchAbsolute;
701             scrn = value->value.child;
702         }
703         else {
704             act->flags |= XkbSA_SwitchAbsolute;
705             scrn = value;
706         }
707
708         if (!ExprResolveInteger(keymap->ctx, scrn, &val))
709             return ReportMismatch(keymap, action->type, field,
710                                   "integer (0..255)");
711
712         if (val < 0 || val > 255) {
713             log_err(keymap->ctx,
714                     "Screen index must be in the range 1..255; "
715                     "Illegal screen value %d ignored\n", val);
716             return false;
717         }
718
719         act->screen = (value->op == EXPR_NEGATE ? -val : val);
720         return true;
721     }
722     else if (field == F_Same) {
723         bool set;
724
725         if (array_ndx)
726             return ReportActionNotArray(keymap, action->type, field);
727
728         if (!ExprResolveBoolean(keymap->ctx, value, &set))
729             return ReportMismatch(keymap, action->type, field, "boolean");
730
731         if (set)
732             act->flags &= ~XkbSA_SwitchApplication;
733         else
734             act->flags |= XkbSA_SwitchApplication;
735
736         return true;
737     }
738
739     return ReportIllegal(keymap, action->type, field);
740 }
741
742 const LookupEntry ctrlNames[] = {
743     { "repeatkeys", XkbRepeatKeysMask },
744     { "repeat", XkbRepeatKeysMask },
745     { "autorepeat", XkbRepeatKeysMask },
746     { "slowkeys", XkbSlowKeysMask },
747     { "bouncekeys", XkbBounceKeysMask },
748     { "stickykeys", XkbStickyKeysMask },
749     { "mousekeys", XkbMouseKeysMask },
750     { "mousekeysaccel", XkbMouseKeysAccelMask },
751     { "accessxkeys", XkbAccessXKeysMask },
752     { "accessxtimeout", XkbAccessXTimeoutMask },
753     { "accessxfeedback", XkbAccessXFeedbackMask },
754     { "audiblebell", XkbAudibleBellMask },
755     { "ignoregrouplock", XkbIgnoreGroupLockMask },
756     { "all", XkbAllBooleanCtrlsMask },
757     { "overlay1", 0 },
758     { "overlay2", 0 },
759     { "none", 0 },
760     { NULL, 0 }
761 };
762
763 static bool
764 HandleSetLockControls(struct xkb_keymap *keymap,
765                       union xkb_action *action,
766                       unsigned field, const ExprDef *array_ndx,
767                       const ExprDef *value)
768 {
769     struct xkb_controls_action *act = &action->ctrls;
770
771     if (field == F_Controls) {
772         unsigned int mask;
773
774         if (array_ndx)
775             return ReportActionNotArray(keymap, action->type, field);
776
777         if (!ExprResolveMask(keymap->ctx, value, &mask, ctrlNames))
778             return ReportMismatch(keymap, action->type, field,
779                                   "controls mask");
780
781         act->ctrls = mask;
782         return true;
783     }
784
785     return ReportIllegal(keymap, action->type, field);
786 }
787
788 static const LookupEntry evNames[] = {
789     { "press", XkbSA_MessageOnPress },
790     { "keypress", XkbSA_MessageOnPress },
791     { "release", XkbSA_MessageOnRelease },
792     { "keyrelease", XkbSA_MessageOnRelease },
793     { "all", XkbSA_MessageOnPress | XkbSA_MessageOnRelease },
794     { "none", 0 },
795     { NULL, 0 }
796 };
797
798 static bool
799 HandleActionMessage(struct xkb_keymap *keymap, union xkb_action *action,
800                     unsigned field, const ExprDef *array_ndx,
801                     const ExprDef *value)
802 {
803     struct xkb_message_action *act = &action->msg;
804
805     if (field == F_Report) {
806         unsigned int mask;
807
808         if (array_ndx)
809             return ReportActionNotArray(keymap, action->type, field);
810
811         if (!ExprResolveMask(keymap->ctx, value, &mask, evNames))
812             return ReportMismatch(keymap, action->type, field,
813                                   "key event mask");
814
815         /* FIXME: Something seems wrong here... */
816         act->flags &= ~(XkbSA_MessageOnPress | XkbSA_MessageOnRelease);
817         act->flags = mask & (XkbSA_MessageOnPress | XkbSA_MessageOnRelease);
818         return true;
819     }
820     else if (field == F_GenKeyEvent) {
821         bool set;
822
823         if (array_ndx)
824             return ReportActionNotArray(keymap, action->type, field);
825
826         if (!ExprResolveBoolean(keymap->ctx, value, &set))
827             return ReportMismatch(keymap, action->type, field, "boolean");
828
829         if (set)
830             act->flags |= XkbSA_MessageGenKeyEvent;
831         else
832             act->flags &= ~XkbSA_MessageGenKeyEvent;
833
834         return true;
835     }
836     else if (field == F_Data && !array_ndx) {
837         const char *str;
838         int len;
839
840         if (!ExprResolveString(keymap->ctx, value, &str))
841             return ReportMismatch(keymap, action->type, field, "string");
842
843         len = strlen(str);
844         if (len < 1 || len > 6) {
845             log_warn(keymap->ctx,
846                      "An action message can hold only 6 bytes; "
847                      "Extra %d bytes ignored\n", len - 6);
848         }
849
850         strncpy((char *) act->message, str, 6);
851         return true;
852     }
853     else if (field == F_Data && array_ndx) {
854         int ndx, datum;
855
856         if (!ExprResolveInteger(keymap->ctx, array_ndx, &ndx)) {
857             log_err(keymap->ctx,
858                     "Array subscript must be integer; "
859                     "Illegal subscript ignored\n");
860             return false;
861         }
862
863         if (ndx < 0 || ndx > 5) {
864             log_err(keymap->ctx,
865                     "An action message is at most 6 bytes long; "
866                     "Attempt to use data[%d] ignored\n", ndx);
867             return false;
868         }
869
870         if (!ExprResolveInteger(keymap->ctx, value, &datum))
871             return ReportMismatch(keymap, action->type, field, "integer");
872
873         if (datum < 0 || datum > 255) {
874             log_err(keymap->ctx,
875                     "Message data must be in the range 0..255; "
876                     "Illegal datum %d ignored\n", datum);
877             return false;
878         }
879
880         act->message[ndx] = (uint8_t) datum;
881         return true;
882     }
883
884     return ReportIllegal(keymap, action->type, field);
885 }
886
887 static bool
888 HandleRedirectKey(struct xkb_keymap *keymap, union xkb_action *action,
889                   unsigned field, const ExprDef *array_ndx,
890                   const ExprDef *value)
891 {
892     struct xkb_key *key;
893     struct xkb_redirect_key_action *act = &action->redirect;
894     unsigned t1;
895     xkb_mod_mask_t t2;
896     unsigned long tmp;
897     char key_name[XkbKeyNameLength];
898
899     if (array_ndx != NULL)
900         return ReportActionNotArray(keymap, action->type, field);
901
902     switch (field) {
903     case F_Keycode:
904         if (!ExprResolveKeyName(keymap->ctx, value, key_name))
905             return ReportMismatch(keymap, action->type, field, "key name");
906
907         tmp = KeyNameToLong(key_name);
908         key = FindNamedKey(keymap, tmp, true, 0);
909         if (!key)
910             return ReportNotFound(keymap, action->type, field, "Key",
911                                   KeyNameText(key_name));
912         act->new_kc = XkbKeyGetKeycode(keymap, key);
913         return true;
914
915     case F_ModsToClear:
916     case F_Modifiers:
917         t1 = 0;
918         if (CheckModifierField(keymap, action->type, value, &t1, &t2)) {
919             act->mods_mask |= (t2 & 0xff);
920             if (field == F_Modifiers)
921                 act->mods |= (t2 & 0xff);
922             else
923                 act->mods &= ~(t2 & 0xff);
924
925             t2 = (t2 >> XkbNumModifiers) & 0xffff;
926             act->vmods_mask |= t2;
927             if (field == F_Modifiers)
928                 act->vmods |= t2;
929             else
930                 act->vmods &= ~t2;
931             return true;
932         }
933         return true;
934     }
935     return ReportIllegal(keymap, action->type, field);
936 }
937
938 static bool
939 HandleDeviceBtn(struct xkb_keymap *keymap, union xkb_action *action,
940                 unsigned field, const ExprDef *array_ndx,
941                 const ExprDef *value)
942 {
943     struct xkb_device_button_action *act = &action->devbtn;
944
945     if (field == F_Button) {
946         int val;
947
948         if (array_ndx)
949             return ReportActionNotArray(keymap, action->type, field);
950
951         if (!ExprResolveInteger(keymap->ctx, value, &val))
952             return ReportMismatch(keymap, action->type, field,
953                                   "integer (range 1..255)");
954
955         if (val < 0 || val > 255) {
956             log_err(keymap->ctx,
957                     "Button must specify default or be in the range 1..255; "
958                     "Illegal button value %d ignored\n", val);
959             return false;
960         }
961
962         act->button = val;
963         return true;
964     }
965     else if (action->type == XkbSA_LockDeviceBtn && field == F_Affect) {
966         unsigned int val;
967
968         if (array_ndx)
969             return ReportActionNotArray(keymap, action->type, field);
970
971         if (!ExprResolveEnum(keymap->ctx, value, &val, lockWhich))
972             return ReportMismatch(keymap, action->type, field,
973                                   "lock or unlock");
974
975         act->flags &= ~(XkbSA_LockNoLock | XkbSA_LockNoUnlock);
976         act->flags |= val;
977         return true;
978     }
979     else if (field == F_Count) {
980         int btn;
981
982         if (array_ndx)
983             return ReportActionNotArray(keymap, action->type, field);
984
985         /* XXX: Should this actually be ResolveButton? */
986         if (!ExprResolveButton(keymap->ctx, value, &btn))
987             return ReportMismatch(keymap, action->type, field, "integer");
988
989         if (btn < 0 || btn > 255) {
990             log_err(keymap->ctx,
991                     "The count field must have a value in the range 0..255; "
992                     "Illegal count %d ignored\n", btn);
993             return false;
994         }
995
996         act->count = btn;
997         return true;
998     }
999     else if (field == F_Device) {
1000         int val;
1001
1002         if (array_ndx)
1003             return ReportActionNotArray(keymap, action->type, field);
1004
1005         if (!ExprResolveInteger(keymap->ctx, value, &val))
1006             return ReportMismatch(keymap, action->type, field,
1007                                   "integer (range 1..255)");
1008
1009         if (val < 0 || val > 255) {
1010             log_err(keymap->ctx,
1011                     "Device must specify default or be in the range 1..255; "
1012                     "Illegal device value %d ignored\n", val);
1013             return false;
1014         }
1015
1016         act->device = val;
1017         return true;
1018     }
1019
1020     return ReportIllegal(keymap, action->type, field);
1021 }
1022
1023 static bool
1024 HandleDeviceValuator(struct xkb_keymap *keymap, union xkb_action *action,
1025                      unsigned field, const ExprDef *array_ndx,
1026                      const ExprDef *value)
1027 {
1028     struct xkb_device_valuator_action *act = &action->devval;
1029     (void) act;
1030
1031     /*  XXX - Not yet implemented */
1032     return false;
1033 }
1034
1035 static bool
1036 HandlePrivate(struct xkb_keymap *keymap, union xkb_action *action,
1037               unsigned field, const ExprDef *array_ndx, const ExprDef *value)
1038 {
1039     struct xkb_private_action *act = &action->priv;
1040
1041     if (field == F_Type) {
1042         int type;
1043
1044         if (!ExprResolveInteger(keymap->ctx, value, &type))
1045             return ReportMismatch(keymap, PrivateAction, field, "integer");
1046
1047         if (type < 0 || type > 255) {
1048             log_err(keymap->ctx,
1049                     "Private action type must be in the range 0..255; "
1050                     "Illegal type %d ignored\n", type);
1051             return false;
1052         }
1053
1054         act->type = (uint8_t) type;
1055         return true;
1056     }
1057     else if (field == F_Data) {
1058         if (array_ndx == NULL) {
1059             const char *str;
1060             int len;
1061
1062             if (!ExprResolveString(keymap->ctx, value, &str))
1063                 return ReportMismatch(keymap, action->type, field, "string");
1064
1065             len = strlen(str);
1066             if (len < 1 || len > 7) {
1067                 log_warn(keymap->ctx,
1068                          "A private action has 7 data bytes; "
1069                          "Extra %d bytes ignored\n", len - 6);
1070                 return false;
1071             }
1072
1073             strncpy((char *) act->data, str, sizeof(act->data));
1074             return true;
1075         }
1076         else {
1077             int ndx, datum;
1078
1079             if (!ExprResolveInteger(keymap->ctx, array_ndx, &ndx)) {
1080                 log_err(keymap->ctx,
1081                         "Array subscript must be integer; "
1082                         "Illegal subscript ignored\n");
1083                 return false;
1084             }
1085
1086             if (ndx < 0 || ndx >= sizeof(act->data)) {
1087                 log_err(keymap->ctx,
1088                         "The data for a private action is %zu bytes long; "
1089                         "Attempt to use data[%d] ignored\n",
1090                         sizeof(act->data), ndx);
1091                 return false;
1092             }
1093
1094             if (!ExprResolveInteger(keymap->ctx, value, &datum))
1095                 return ReportMismatch(keymap, act->type, field, "integer");
1096
1097             if (datum < 0 || datum > 255) {
1098                 log_err(keymap->ctx,
1099                         "All data for a private action must be 0..255; "
1100                         "Illegal datum %d ignored\n", datum);
1101                 return false;
1102             }
1103
1104             act->data[ndx] = (uint8_t) datum;
1105             return true;
1106         }
1107     }
1108
1109     return ReportIllegal(keymap, PrivateAction, field);
1110 }
1111
1112 typedef bool (*actionHandler)(struct xkb_keymap *keymap,
1113                               union xkb_action *action, unsigned field,
1114                               const ExprDef *array_ndx, const ExprDef *value);
1115
1116 static const actionHandler handleAction[XkbSA_NumActions + 1] = {
1117     [XkbSA_NoAction] = HandleNoAction,
1118     [XkbSA_SetMods] = HandleSetLatchMods,
1119     [XkbSA_LatchMods] = HandleSetLatchMods,
1120     [XkbSA_LockMods] = HandleLockMods,
1121     [XkbSA_SetGroup] = HandleSetLatchGroup,
1122     [XkbSA_LatchGroup] = HandleSetLatchGroup,
1123     [XkbSA_LockGroup] = HandleLockGroup,
1124     [XkbSA_MovePtr] = HandleMovePtr,
1125     [XkbSA_PtrBtn] = HandlePtrBtn,
1126     [XkbSA_LockPtrBtn] = HandlePtrBtn,
1127     [XkbSA_SetPtrDflt] = HandleSetPtrDflt,
1128     [XkbSA_ISOLock] = HandleISOLock,
1129     [XkbSA_Terminate] = HandleNoAction,
1130     [XkbSA_SwitchScreen] = HandleSwitchScreen,
1131     [XkbSA_SetControls] = HandleSetLockControls,
1132     [XkbSA_LockControls] = HandleSetLockControls,
1133     [XkbSA_ActionMessage] = HandleActionMessage,
1134     [XkbSA_RedirectKey] = HandleRedirectKey,
1135     [XkbSA_DeviceBtn] = HandleDeviceBtn,
1136     [XkbSA_LockDeviceBtn] = HandleDeviceBtn,
1137     [XkbSA_DeviceValuator] = HandleDeviceValuator,
1138     [PrivateAction] = HandlePrivate,
1139 };
1140
1141 /***====================================================================***/
1142
1143 static void
1144 ApplyActionFactoryDefaults(union xkb_action * action)
1145 {
1146     if (action->type == XkbSA_SetPtrDflt) { /* increment default button */
1147         action->dflt.affect = XkbSA_AffectDfltBtn;
1148         action->dflt.flags = 0;
1149         action->dflt.value = 1;
1150     }
1151     else if (action->type == XkbSA_ISOLock) {
1152         action->iso.mods.mods = (1 << ModNameToIndex(XKB_MOD_NAME_CAPS));
1153     }
1154 }
1155
1156 int
1157 HandleActionDef(ExprDef * def,
1158                 struct xkb_keymap *keymap,
1159                 union xkb_action *action, ActionInfo *info)
1160 {
1161     ExprDef *arg;
1162     const char *str;
1163     unsigned tmp, hndlrType;
1164
1165     if (def->op != EXPR_ACTION_DECL) {
1166         log_err(keymap->ctx, "Expected an action definition, found %s\n",
1167                 exprOpText(def->op));
1168         return false;
1169     }
1170     str = xkb_atom_text(keymap->ctx, def->value.action.name);
1171     if (!str) {
1172         log_wsgo(keymap->ctx, "Missing name in action definition!!\n");
1173         return false;
1174     }
1175     if (!stringToAction(str, &tmp)) {
1176         log_err(keymap->ctx, "Unknown action %s\n", str);
1177         return false;
1178     }
1179     action->type = hndlrType = tmp;
1180     if (action->type != XkbSA_NoAction) {
1181         ApplyActionFactoryDefaults((union xkb_action *) action);
1182         while (info)
1183         {
1184             if ((info->action == XkbSA_NoAction)
1185                 || (info->action == hndlrType)) {
1186                 if (!(*handleAction[hndlrType])(keymap, action,
1187                                                 info->field,
1188                                                 info->array_ndx,
1189                                                 info->value)) {
1190                     return false;
1191                 }
1192             }
1193             info = info->next;
1194         }
1195     }
1196     for (arg = def->value.action.args; arg != NULL;
1197          arg = (ExprDef *) arg->common.next) {
1198         const ExprDef *value;
1199         ExprDef *field, *arrayRtrn;
1200         const char *elemRtrn, *fieldRtrn;
1201         unsigned fieldNdx;
1202
1203         if (arg->op == EXPR_ASSIGN) {
1204             field = arg->value.binary.left;
1205             value = arg->value.binary.right;
1206         }
1207         else {
1208             if (arg->op == EXPR_NOT || arg->op == EXPR_INVERT) {
1209                 field = arg->value.child;
1210                 value = &constFalse;
1211             }
1212             else {
1213                 field = arg;
1214                 value = &constTrue;
1215             }
1216         }
1217         if (!ExprResolveLhs(keymap->ctx, field, &elemRtrn, &fieldRtrn,
1218                             &arrayRtrn))
1219             return false;       /* internal error -- already reported */
1220
1221         if (elemRtrn != NULL) {
1222             log_err(keymap->ctx,
1223                     "Cannot change defaults in an action definition; "
1224                     "Ignoring attempt to change %s.%s\n",
1225                     elemRtrn, fieldRtrn);
1226             return false;
1227         }
1228         if (!stringToField(fieldRtrn, &fieldNdx)) {
1229             log_err(keymap->ctx, "Unknown field name %s\n", fieldRtrn);
1230             return false;
1231         }
1232         if (!handleAction[hndlrType](keymap, action, fieldNdx, arrayRtrn,
1233                                      value))
1234             return false;
1235     }
1236     return true;
1237 }
1238
1239 /***====================================================================***/
1240
1241 int
1242 SetActionField(struct xkb_keymap *keymap, const char *elem, const char *field,
1243                ExprDef *array_ndx, ExprDef *value, ActionInfo **info_rtrn)
1244 {
1245     ActionInfo *new, *old;
1246
1247     new = malloc(sizeof(*new));
1248     if (!new) {
1249         log_wsgo(keymap->ctx, "Couldn't allocate space for action default\n");
1250         return false;
1251     }
1252
1253     if (istreq(elem, "action"))
1254         new->action = XkbSA_NoAction;
1255     else {
1256         if (!stringToAction(elem, &new->action)) {
1257             free(new);
1258             return false;
1259         }
1260         if (new->action == XkbSA_NoAction) {
1261             log_err(keymap->ctx,
1262                     "\"%s\" is not a valid field in a NoAction action\n",
1263                     field);
1264             free(new);
1265             return false;
1266         }
1267     }
1268     if (!stringToField(field, &new->field)) {
1269         log_err(keymap->ctx, "\"%s\" is not a legal field name\n", field);
1270         free(new);
1271         return false;
1272     }
1273     new->array_ndx = array_ndx;
1274     new->value = value;
1275     new->next = NULL;
1276     old = *info_rtrn;
1277     while ((old) && (old->next))
1278         old = old->next;
1279     if (old == NULL)
1280         *info_rtrn = new;
1281     else
1282         old->next = new;
1283     return true;
1284 }