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