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