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