action: keep array of default actions, instead of list of changes
[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_DeviceBtn      },
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         const char *str;
928         int len;
929
930         if (!ExprResolveString(keymap->ctx, value, &str))
931             return ReportMismatch(keymap, action->type, field, "string");
932
933         len = strlen(str);
934         if (len < 1 || len > 6) {
935             log_warn(keymap->ctx,
936                      "An action message can hold only 6 bytes; "
937                      "Extra %d bytes ignored\n", len - 6);
938         }
939
940         strncpy((char *) act->message, str, 6);
941         return true;
942     }
943     else if (field == ACTION_FIELD_DATA && array_ndx) {
944         int ndx, datum;
945
946         if (!ExprResolveInteger(keymap->ctx, array_ndx, &ndx)) {
947             log_err(keymap->ctx,
948                     "Array subscript must be integer; "
949                     "Illegal subscript ignored\n");
950             return false;
951         }
952
953         if (ndx < 0 || ndx > 5) {
954             log_err(keymap->ctx,
955                     "An action message is at most 6 bytes long; "
956                     "Attempt to use data[%d] ignored\n", ndx);
957             return false;
958         }
959
960         if (!ExprResolveInteger(keymap->ctx, value, &datum))
961             return ReportMismatch(keymap, action->type, field, "integer");
962
963         if (datum < 0 || datum > 255) {
964             log_err(keymap->ctx,
965                     "Message data must be in the range 0..255; "
966                     "Illegal datum %d ignored\n", datum);
967             return false;
968         }
969
970         act->message[ndx] = (uint8_t) datum;
971         return true;
972     }
973
974     return ReportIllegal(keymap, action->type, field);
975 }
976
977 static bool
978 HandleRedirectKey(struct xkb_keymap *keymap, union xkb_action *action,
979                   enum action_field field, const ExprDef *array_ndx,
980                   const ExprDef *value)
981 {
982     struct xkb_key *key;
983     struct xkb_redirect_key_action *act = &action->redirect;
984     unsigned t1;
985     xkb_mod_mask_t t2;
986     unsigned long tmp;
987     char key_name[XkbKeyNameLength];
988
989     if (array_ndx != NULL)
990         return ReportActionNotArray(keymap, action->type, field);
991
992     switch (field) {
993     case ACTION_FIELD_KEYCODE:
994         if (!ExprResolveKeyName(keymap->ctx, value, key_name))
995             return ReportMismatch(keymap, action->type, field, "key name");
996
997         tmp = KeyNameToLong(key_name);
998         key = FindNamedKey(keymap, tmp, true, 0);
999         if (!key)
1000             return ReportNotFound(keymap, action->type, field, "Key",
1001                                   KeyNameText(key_name));
1002         act->new_kc = XkbKeyGetKeycode(keymap, key);
1003         return true;
1004
1005     case ACTION_FIELD_MODS_TO_CLEAR:
1006     case ACTION_FIELD_MODIFIERS:
1007         t1 = 0;
1008         if (CheckModifierField(keymap, action->type, value, &t1, &t2)) {
1009             act->mods_mask |= (t2 & 0xff);
1010             if (field == ACTION_FIELD_MODIFIERS)
1011                 act->mods |= (t2 & 0xff);
1012             else
1013                 act->mods &= ~(t2 & 0xff);
1014
1015             t2 = (t2 >> XkbNumModifiers) & 0xffff;
1016             act->vmods_mask |= t2;
1017             if (field == ACTION_FIELD_MODIFIERS)
1018                 act->vmods |= t2;
1019             else
1020                 act->vmods &= ~t2;
1021             return true;
1022         }
1023         return true;
1024
1025     default:
1026         break;
1027     }
1028
1029     return ReportIllegal(keymap, action->type, field);
1030 }
1031
1032 static bool
1033 HandleDeviceBtn(struct xkb_keymap *keymap, union xkb_action *action,
1034                 enum action_field field, const ExprDef *array_ndx,
1035                 const ExprDef *value)
1036 {
1037     struct xkb_device_button_action *act = &action->devbtn;
1038
1039     if (field == ACTION_FIELD_BUTTON) {
1040         int val;
1041
1042         if (array_ndx)
1043             return ReportActionNotArray(keymap, action->type, field);
1044
1045         if (!ExprResolveInteger(keymap->ctx, value, &val))
1046             return ReportMismatch(keymap, action->type, field,
1047                                   "integer (range 1..255)");
1048
1049         if (val < 0 || val > 255) {
1050             log_err(keymap->ctx,
1051                     "Button must specify default or be in the range 1..255; "
1052                     "Illegal button value %d ignored\n", val);
1053             return false;
1054         }
1055
1056         act->button = val;
1057         return true;
1058     }
1059     else if (action->type == XkbSA_LockDeviceBtn &&
1060              field == ACTION_FIELD_AFFECT) {
1061         unsigned int val;
1062
1063         if (array_ndx)
1064             return ReportActionNotArray(keymap, action->type, field);
1065
1066         if (!ExprResolveEnum(keymap->ctx, value, &val, lockWhich))
1067             return ReportMismatch(keymap, action->type, field,
1068                                   "lock or unlock");
1069
1070         act->flags &= ~(XkbSA_LockNoLock | XkbSA_LockNoUnlock);
1071         act->flags |= val;
1072         return true;
1073     }
1074     else if (field == ACTION_FIELD_COUNT) {
1075         int btn;
1076
1077         if (array_ndx)
1078             return ReportActionNotArray(keymap, action->type, field);
1079
1080         /* XXX: Should this actually be ResolveButton? */
1081         if (!ExprResolveButton(keymap->ctx, value, &btn))
1082             return ReportMismatch(keymap, action->type, field, "integer");
1083
1084         if (btn < 0 || btn > 255) {
1085             log_err(keymap->ctx,
1086                     "The count field must have a value in the range 0..255; "
1087                     "Illegal count %d ignored\n", btn);
1088             return false;
1089         }
1090
1091         act->count = btn;
1092         return true;
1093     }
1094     else if (field == ACTION_FIELD_DEVICE) {
1095         int val;
1096
1097         if (array_ndx)
1098             return ReportActionNotArray(keymap, action->type, field);
1099
1100         if (!ExprResolveInteger(keymap->ctx, value, &val))
1101             return ReportMismatch(keymap, action->type, field,
1102                                   "integer (range 1..255)");
1103
1104         if (val < 0 || val > 255) {
1105             log_err(keymap->ctx,
1106                     "Device must specify default or be in the range 1..255; "
1107                     "Illegal device value %d ignored\n", val);
1108             return false;
1109         }
1110
1111         act->device = val;
1112         return true;
1113     }
1114
1115     return ReportIllegal(keymap, action->type, field);
1116 }
1117
1118 static bool
1119 HandleDeviceValuator(struct xkb_keymap *keymap, union xkb_action *action,
1120                      enum action_field field, const ExprDef *array_ndx,
1121                      const ExprDef *value)
1122 {
1123     struct xkb_device_valuator_action *act = &action->devval;
1124     (void) act;
1125
1126     /*  XXX - Not yet implemented */
1127     return false;
1128 }
1129
1130 static bool
1131 HandlePrivate(struct xkb_keymap *keymap, union xkb_action *action,
1132               enum action_field field, const ExprDef *array_ndx,
1133               const ExprDef *value)
1134 {
1135     struct xkb_private_action *act = &action->priv;
1136
1137     if (field == ACTION_FIELD_TYPE) {
1138         int type;
1139
1140         if (!ExprResolveInteger(keymap->ctx, value, &type))
1141             return ReportMismatch(keymap, PrivateAction, field, "integer");
1142
1143         if (type < 0 || type > 255) {
1144             log_err(keymap->ctx,
1145                     "Private action type must be in the range 0..255; "
1146                     "Illegal type %d ignored\n", type);
1147             return false;
1148         }
1149
1150         act->type = (uint8_t) type;
1151         return true;
1152     }
1153     else if (field == ACTION_FIELD_DATA) {
1154         if (array_ndx == NULL) {
1155             const char *str;
1156             int len;
1157
1158             if (!ExprResolveString(keymap->ctx, value, &str))
1159                 return ReportMismatch(keymap, action->type, field, "string");
1160
1161             len = strlen(str);
1162             if (len < 1 || len > 7) {
1163                 log_warn(keymap->ctx,
1164                          "A private action has 7 data bytes; "
1165                          "Extra %d bytes ignored\n", len - 6);
1166                 return false;
1167             }
1168
1169             strncpy((char *) act->data, str, sizeof(act->data));
1170             return true;
1171         }
1172         else {
1173             int ndx, datum;
1174
1175             if (!ExprResolveInteger(keymap->ctx, array_ndx, &ndx)) {
1176                 log_err(keymap->ctx,
1177                         "Array subscript must be integer; "
1178                         "Illegal subscript ignored\n");
1179                 return false;
1180             }
1181
1182             if (ndx < 0 || ndx >= sizeof(act->data)) {
1183                 log_err(keymap->ctx,
1184                         "The data for a private action is %zu bytes long; "
1185                         "Attempt to use data[%d] ignored\n",
1186                         sizeof(act->data), ndx);
1187                 return false;
1188             }
1189
1190             if (!ExprResolveInteger(keymap->ctx, value, &datum))
1191                 return ReportMismatch(keymap, act->type, field, "integer");
1192
1193             if (datum < 0 || datum > 255) {
1194                 log_err(keymap->ctx,
1195                         "All data for a private action must be 0..255; "
1196                         "Illegal datum %d ignored\n", datum);
1197                 return false;
1198             }
1199
1200             act->data[ndx] = (uint8_t) datum;
1201             return true;
1202         }
1203     }
1204
1205     return ReportIllegal(keymap, PrivateAction, field);
1206 }
1207
1208 typedef bool (*actionHandler)(struct xkb_keymap *keymap,
1209                               union xkb_action *action,
1210                               enum action_field field,
1211                               const ExprDef *array_ndx,
1212                               const ExprDef *value);
1213
1214 static const actionHandler handleAction[XkbSA_NumActions + 1] = {
1215     [XkbSA_NoAction] = HandleNoAction,
1216     [XkbSA_SetMods] = HandleSetLatchMods,
1217     [XkbSA_LatchMods] = HandleSetLatchMods,
1218     [XkbSA_LockMods] = HandleLockMods,
1219     [XkbSA_SetGroup] = HandleSetLatchGroup,
1220     [XkbSA_LatchGroup] = HandleSetLatchGroup,
1221     [XkbSA_LockGroup] = HandleLockGroup,
1222     [XkbSA_MovePtr] = HandleMovePtr,
1223     [XkbSA_PtrBtn] = HandlePtrBtn,
1224     [XkbSA_LockPtrBtn] = HandlePtrBtn,
1225     [XkbSA_SetPtrDflt] = HandleSetPtrDflt,
1226     [XkbSA_ISOLock] = HandleISOLock,
1227     [XkbSA_Terminate] = HandleNoAction,
1228     [XkbSA_SwitchScreen] = HandleSwitchScreen,
1229     [XkbSA_SetControls] = HandleSetLockControls,
1230     [XkbSA_LockControls] = HandleSetLockControls,
1231     [XkbSA_ActionMessage] = HandleActionMessage,
1232     [XkbSA_RedirectKey] = HandleRedirectKey,
1233     [XkbSA_DeviceBtn] = HandleDeviceBtn,
1234     [XkbSA_LockDeviceBtn] = HandleDeviceBtn,
1235     [XkbSA_DeviceValuator] = HandleDeviceValuator,
1236     [PrivateAction] = HandlePrivate,
1237 };
1238
1239 /***====================================================================***/
1240
1241 bool
1242 HandleActionDef(ExprDef *def, struct xkb_keymap *keymap,
1243                 union xkb_action *action, ActionsInfo *info)
1244 {
1245     ExprDef *arg;
1246     const char *str;
1247     unsigned hndlrType;
1248
1249     if (def->op != EXPR_ACTION_DECL) {
1250         log_err(keymap->ctx, "Expected an action definition, found %s\n",
1251                 expr_op_type_to_string(def->op));
1252         return false;
1253     }
1254
1255     str = xkb_atom_text(keymap->ctx, def->value.action.name);
1256     if (!stringToAction(str, &hndlrType)) {
1257         log_err(keymap->ctx, "Unknown action %s\n", str);
1258         return false;
1259     }
1260
1261     /*
1262      * Get the default values for this action type, as modified by
1263      * statements such as:
1264      *     latchMods.clearLocks = True;
1265      */
1266     *action = info->actions[hndlrType];
1267
1268     /*
1269      * Now change the action properties as specified for this
1270      * particular instance, e.g. "modifiers" and "clearLocks" in:
1271      *     SetMods(modifiers=Alt,clearLocks);
1272      */
1273     for (arg = def->value.action.args; arg != NULL;
1274          arg = (ExprDef *) arg->common.next) {
1275         const ExprDef *value;
1276         ExprDef *field, *arrayRtrn;
1277         const char *elemRtrn, *fieldRtrn;
1278         enum action_field fieldNdx;
1279
1280         if (arg->op == EXPR_ASSIGN) {
1281             field = arg->value.binary.left;
1282             value = arg->value.binary.right;
1283         }
1284         else if (arg->op == EXPR_NOT || arg->op == EXPR_INVERT) {
1285             field = arg->value.child;
1286             value = &constFalse;
1287         }
1288         else {
1289             field = arg;
1290             value = &constTrue;
1291         }
1292
1293         if (!ExprResolveLhs(keymap->ctx, field, &elemRtrn, &fieldRtrn,
1294                             &arrayRtrn))
1295             return false;
1296
1297         if (elemRtrn) {
1298             log_err(keymap->ctx,
1299                     "Cannot change defaults in an action definition; "
1300                     "Ignoring attempt to change %s.%s\n",
1301                     elemRtrn, fieldRtrn);
1302             return false;
1303         }
1304
1305         if (!stringToField(fieldRtrn, &fieldNdx)) {
1306             log_err(keymap->ctx, "Unknown field name %s\n", fieldRtrn);
1307             return false;
1308         }
1309
1310         if (!handleAction[hndlrType](keymap, action, fieldNdx, arrayRtrn,
1311                                      value))
1312             return false;
1313     }
1314
1315     return true;
1316 }
1317
1318
1319 bool
1320 SetActionField(struct xkb_keymap *keymap, const char *elem, const char *field,
1321                ExprDef *array_ndx, ExprDef *value, ActionsInfo *info)
1322 {
1323     unsigned action;
1324     enum action_field action_field;
1325
1326     if (!stringToAction(elem, &action))
1327         return false;
1328
1329     if (action == XkbSA_NoAction) {
1330         log_err(keymap->ctx,
1331                 "\"%s\" is not a valid field in a NoAction action\n",
1332                 field);
1333         return false;
1334     }
1335
1336     if (!stringToField(field, &action_field)) {
1337         log_err(keymap->ctx, "\"%s\" is not a legal field name\n", field);
1338         return false;
1339     }
1340
1341     return handleAction[action](keymap, &info->actions[action],
1342                                 action_field, array_ndx, value);
1343 }