kbproto unentanglement: action flags
[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 < ACTION_TYPE_LAST; type++)
85         info->actions[type].type = type;
86
87     /* Apply some "factory defaults". */
88
89     /* Increment default button. */
90     info->actions[ACTION_TYPE_PTR_DEFAULT].dflt.affect = XkbSA_AffectDfltBtn;
91     info->actions[ACTION_TYPE_PTR_DEFAULT].dflt.flags = 0;
92     info->actions[ACTION_TYPE_PTR_DEFAULT].dflt.value = 1;
93
94     return info;
95 }
96
97 void
98 FreeActionsInfo(ActionsInfo *info)
99 {
100     free(info);
101 }
102
103 static const LookupEntry fieldStrings[] = {
104     { "clearLocks",       ACTION_FIELD_CLEAR_LOCKS   },
105     { "latchToLock",      ACTION_FIELD_LATCH_TO_LOCK },
106     { "genKeyEvent",      ACTION_FIELD_GEN_KEY_EVENT },
107     { "generateKeyEvent", ACTION_FIELD_GEN_KEY_EVENT },
108     { "report",           ACTION_FIELD_REPORT        },
109     { "default",          ACTION_FIELD_DEFAULT       },
110     { "affect",           ACTION_FIELD_AFFECT        },
111     { "increment",        ACTION_FIELD_INCREMENT     },
112     { "modifiers",        ACTION_FIELD_MODIFIERS     },
113     { "mods",             ACTION_FIELD_MODIFIERS     },
114     { "group",            ACTION_FIELD_GROUP         },
115     { "x",                ACTION_FIELD_X             },
116     { "y",                ACTION_FIELD_Y             },
117     { "accel",            ACTION_FIELD_ACCEL         },
118     { "accelerate",       ACTION_FIELD_ACCEL         },
119     { "repeat",           ACTION_FIELD_ACCEL         },
120     { "button",           ACTION_FIELD_BUTTON        },
121     { "value",            ACTION_FIELD_VALUE         },
122     { "controls",         ACTION_FIELD_CONTROLS      },
123     { "ctrls",            ACTION_FIELD_CONTROLS      },
124     { "type",             ACTION_FIELD_TYPE          },
125     { "count",            ACTION_FIELD_COUNT         },
126     { "screen",           ACTION_FIELD_SCREEN        },
127     { "same",             ACTION_FIELD_SAME          },
128     { "sameServer",       ACTION_FIELD_SAME          },
129     { "data",             ACTION_FIELD_DATA          },
130     { "device",           ACTION_FIELD_DEVICE        },
131     { "dev",              ACTION_FIELD_DEVICE        },
132     { "key",              ACTION_FIELD_KEYCODE       },
133     { "keycode",          ACTION_FIELD_KEYCODE       },
134     { "kc",               ACTION_FIELD_KEYCODE       },
135     { "clearmods",        ACTION_FIELD_MODS_TO_CLEAR },
136     { "clearmodifiers",   ACTION_FIELD_MODS_TO_CLEAR },
137     { NULL,               0                          }
138 };
139
140 static bool
141 stringToAction(const char *str, unsigned *type_rtrn)
142 {
143     return LookupString(actionTypeNames, str, type_rtrn);
144 }
145
146 static bool
147 stringToField(const char *str, enum action_field *field_rtrn)
148 {
149     return LookupString(fieldStrings, str, field_rtrn);
150 }
151
152 static const char *
153 fieldText(enum action_field field)
154 {
155     return LookupValue(fieldStrings, field);
156 }
157
158 /***====================================================================***/
159
160 static inline bool
161 ReportMismatch(struct xkb_keymap *keymap, enum xkb_action_type action,
162                enum action_field field, const char *type)
163 {
164     log_err(keymap->ctx,
165             "Value of %s field must be of type %s; "
166             "Action %s definition ignored\n",
167             fieldText(field), type, ActionTypeText(action));
168     return false;
169 }
170
171 static inline bool
172 ReportIllegal(struct xkb_keymap *keymap, enum xkb_action_type action,
173               enum action_field field)
174 {
175     log_err(keymap->ctx,
176             "Field %s is not defined for an action of type %s; "
177             "Action definition ignored\n",
178             fieldText(field), ActionTypeText(action));
179     return false;
180 }
181
182 static inline bool
183 ReportActionNotArray(struct xkb_keymap *keymap, enum xkb_action_type action,
184                      enum action_field field)
185 {
186     log_err(keymap->ctx,
187             "The %s field in the %s action is not an array; "
188             "Action definition ignored\n",
189             fieldText(field), ActionTypeText(action));
190     return false;
191 }
192
193 static inline bool
194 ReportNotFound(struct xkb_keymap *keymap, enum xkb_action_type action,
195                enum action_field field, const char *what, const char *bad)
196 {
197     log_err(keymap->ctx,
198             "%s named %s not found; "
199             "Ignoring the %s field of an %s action\n",
200             what, bad, fieldText(field), ActionTypeText(action));
201     return false;
202 }
203
204 static bool
205 HandleNoAction(struct xkb_keymap *keymap, union xkb_action *action,
206                enum action_field field, const ExprDef *array_ndx,
207                const ExprDef *value)
208
209 {
210     return true;
211 }
212
213 static bool
214 CheckLatchLockFlags(struct xkb_keymap *keymap, enum xkb_action_type action,
215                     enum action_field field, const ExprDef * value,
216                     enum xkb_action_flags *flags_inout)
217 {
218     enum xkb_action_flags tmp;
219     bool result;
220
221     if (field == ACTION_FIELD_CLEAR_LOCKS)
222         tmp = ACTION_LOCK_CLEAR;
223     else if (field == ACTION_FIELD_LATCH_TO_LOCK)
224         tmp = ACTION_LATCH_TO_LOCK;
225     else
226         return false;           /* WSGO! */
227
228     if (!ExprResolveBoolean(keymap->ctx, value, &result))
229         return ReportMismatch(keymap, action, field, "boolean");
230
231     if (result)
232         *flags_inout |= tmp;
233     else
234         *flags_inout &= ~tmp;
235
236     return true;
237 }
238
239 static bool
240 CheckModifierField(struct xkb_keymap *keymap, enum xkb_action_type action,
241                    const ExprDef *value, enum xkb_action_flags *flags_inout,
242                    xkb_mod_mask_t *mods_rtrn)
243 {
244     if (value->op == EXPR_IDENT) {
245         const char *valStr;
246         valStr = xkb_atom_text(keymap->ctx, value->value.str);
247         if (valStr && (istreq(valStr, "usemodmapmods") ||
248                        istreq(valStr, "modmapmods"))) {
249
250             *mods_rtrn = 0;
251             *flags_inout |= ACTION_MODS_LOOKUP_MODMAP;
252             return true;
253         }
254     }
255
256     if (!ExprResolveVModMask(keymap, value, mods_rtrn))
257         return ReportMismatch(keymap, action,
258                               ACTION_FIELD_MODIFIERS, "modifier mask");
259
260     *flags_inout &= ~ACTION_MODS_LOOKUP_MODMAP;
261     return true;
262 }
263
264 static bool
265 HandleSetLatchMods(struct xkb_keymap *keymap, union xkb_action *action,
266                    enum action_field field, const ExprDef *array_ndx,
267                    const ExprDef *value)
268 {
269     struct xkb_mod_action *act = &action->mods;
270     enum xkb_action_flags rtrn, t1;
271     xkb_mod_mask_t t2;
272
273     if (array_ndx != NULL) {
274         switch (field) {
275         case ACTION_FIELD_CLEAR_LOCKS:
276         case ACTION_FIELD_LATCH_TO_LOCK:
277         case ACTION_FIELD_MODIFIERS:
278             return ReportActionNotArray(keymap, action->type, field);
279         default:
280             break;
281         }
282     }
283
284     switch (field) {
285     case ACTION_FIELD_CLEAR_LOCKS:
286     case ACTION_FIELD_LATCH_TO_LOCK:
287         rtrn = act->flags;
288         if (CheckLatchLockFlags(keymap, action->type, field, value, &rtrn)) {
289             act->flags = rtrn;
290             return true;
291         }
292         return false;
293
294     case ACTION_FIELD_MODIFIERS:
295         t1 = act->flags;
296         if (CheckModifierField(keymap, action->type, value, &t1, &t2)) {
297             act->flags = t1;
298             act->mods.mods = t2;
299             return true;
300         }
301         return false;
302
303     default:
304         break;
305     }
306
307     return ReportIllegal(keymap, action->type, field);
308 }
309
310 static bool
311 HandleLockMods(struct xkb_keymap *keymap, union xkb_action *action,
312                enum action_field field, const ExprDef *array_ndx,
313                const ExprDef *value)
314 {
315     struct xkb_mod_action *act = &action->mods;
316     enum xkb_action_flags t1;
317     xkb_mod_mask_t t2;
318
319     if (array_ndx && field == ACTION_FIELD_MODIFIERS)
320         return ReportActionNotArray(keymap, action->type, field);
321
322     switch (field) {
323     case ACTION_FIELD_MODIFIERS:
324         t1 = act->flags;
325         if (CheckModifierField(keymap, action->type, value, &t1, &t2)) {
326             act->flags = t1;
327             act->mods.mods = t2;
328             return true;
329         }
330         return false;
331
332     default:
333         break;
334     }
335
336     return ReportIllegal(keymap, action->type, field);
337 }
338
339 static bool
340 CheckGroupField(struct xkb_keymap *keymap, unsigned action,
341                 const ExprDef *value, enum xkb_action_flags *flags_inout,
342                 xkb_group_index_t *grp_rtrn)
343 {
344     const ExprDef *spec;
345
346     if (value->op == EXPR_NEGATE || value->op == EXPR_UNARY_PLUS) {
347         *flags_inout &= ~ACTION_ABSOLUTE_SWITCH;
348         spec = value->value.child;
349     }
350     else {
351         *flags_inout |= ACTION_ABSOLUTE_SWITCH;
352         spec = value;
353     }
354
355     if (!ExprResolveGroup(keymap->ctx, spec, grp_rtrn))
356         return ReportMismatch(keymap, action, ACTION_FIELD_GROUP,
357                               "integer (range 1..8)");
358
359     if (value->op == EXPR_NEGATE)
360         *grp_rtrn = -*grp_rtrn;
361     else if (value->op != EXPR_UNARY_PLUS)
362         (*grp_rtrn)--;
363
364     return true;
365 }
366
367 static bool
368 HandleSetLatchGroup(struct xkb_keymap *keymap, union xkb_action *action,
369                     enum action_field field, const ExprDef *array_ndx,
370                     const ExprDef *value)
371 {
372     struct xkb_group_action *act = &action->group;
373     enum xkb_action_flags rtrn, t1;
374     xkb_group_index_t t2;
375
376     if (array_ndx != NULL) {
377         switch (field) {
378         case ACTION_FIELD_CLEAR_LOCKS:
379         case ACTION_FIELD_LATCH_TO_LOCK:
380         case ACTION_FIELD_GROUP:
381             return ReportActionNotArray(keymap, action->type, field);
382
383         default:
384             break;
385         }
386     }
387
388     switch (field) {
389     case ACTION_FIELD_CLEAR_LOCKS:
390     case ACTION_FIELD_LATCH_TO_LOCK:
391         rtrn = act->flags;
392         if (CheckLatchLockFlags(keymap, action->type, field, value, &rtrn)) {
393             act->flags = rtrn;
394             return true;
395         }
396         return false;
397
398     case ACTION_FIELD_GROUP:
399         t1 = act->flags;
400         if (CheckGroupField(keymap, action->type, value, &t1, &t2)) {
401             act->flags = t1;
402             act->group = t2;
403             return true;
404         }
405         return false;
406
407     default:
408         break;
409     }
410
411     return ReportIllegal(keymap, action->type, field);
412 }
413
414 static bool
415 HandleLockGroup(struct xkb_keymap *keymap, union xkb_action *action,
416                 enum action_field field, const ExprDef *array_ndx,
417                 const ExprDef *value)
418 {
419     struct xkb_group_action *act = &action->group;
420     enum xkb_action_flags t1;
421     xkb_group_index_t t2;
422
423     if ((array_ndx != NULL) && (field == ACTION_FIELD_GROUP))
424         return ReportActionNotArray(keymap, action->type, field);
425     if (field == ACTION_FIELD_GROUP) {
426         t1 = act->flags;
427         if (CheckGroupField(keymap, action->type, value, &t1, &t2)) {
428             act->flags = t1;
429             act->group = t2;
430             return true;
431         }
432         return false;
433     }
434     return ReportIllegal(keymap, action->type, field);
435 }
436
437 static bool
438 HandleMovePtr(struct xkb_keymap *keymap, union xkb_action *action,
439               enum action_field field, const ExprDef *array_ndx,
440               const ExprDef *value)
441 {
442     struct xkb_pointer_action *act = &action->ptr;
443     bool absolute;
444
445     if (array_ndx && (field == ACTION_FIELD_X || field == ACTION_FIELD_Y))
446         return ReportActionNotArray(keymap, action->type, field);
447
448     if (field == ACTION_FIELD_X || field == ACTION_FIELD_Y) {
449         int val;
450
451         if (value->op == EXPR_NEGATE || value->op == EXPR_UNARY_PLUS)
452             absolute = false;
453         else
454             absolute = true;
455
456         if (!ExprResolveInteger(keymap->ctx, value, &val))
457             return ReportMismatch(keymap, action->type, field, "integer");
458
459         if (field == ACTION_FIELD_X) {
460             if (absolute)
461                 act->flags |= ACTION_ABSOLUTE_X;
462             act->x = val;
463         }
464         else {
465             if (absolute)
466                 act->flags |= ACTION_ABSOLUTE_Y;
467             act->y = val;
468         }
469
470         return true;
471     }
472     else if (field == ACTION_FIELD_ACCEL) {
473         bool set;
474
475         if (!ExprResolveBoolean(keymap->ctx, value, &set))
476             return ReportMismatch(keymap, action->type, field, "boolean");
477
478         if (set)
479             act->flags &= ~ACTION_NO_ACCEL;
480         else
481             act->flags |= ACTION_NO_ACCEL;
482     }
483
484     return ReportIllegal(keymap, action->type, field);
485 }
486
487 static const LookupEntry lockWhich[] = {
488     { "both", 0 },
489     { "lock", ACTION_LOCK_NO_UNLOCK },
490     { "neither", (ACTION_LOCK_NO_LOCK | ACTION_LOCK_NO_UNLOCK) },
491     { "unlock", ACTION_LOCK_NO_LOCK },
492     { NULL, 0 }
493 };
494
495 static bool
496 HandlePtrBtn(struct xkb_keymap *keymap, union xkb_action *action,
497              enum action_field field, const ExprDef *array_ndx,
498              const ExprDef *value)
499 {
500     struct xkb_pointer_button_action *act = &action->btn;
501
502     if (field == ACTION_FIELD_BUTTON) {
503         int btn;
504
505         if (array_ndx)
506             return ReportActionNotArray(keymap, action->type, field);
507
508         if (!ExprResolveButton(keymap->ctx, value, &btn))
509             return ReportMismatch(keymap, action->type, field,
510                                   "integer (range 1..5)");
511
512         if (btn < 0 || btn > 5) {
513             log_err(keymap->ctx,
514                     "Button must specify default or be in the range 1..5; "
515                     "Illegal button value %d ignored\n", btn);
516             return false;
517         }
518
519         act->button = btn;
520         return true;
521     }
522     else if (action->type == ACTION_TYPE_PTR_LOCK &&
523              field == ACTION_FIELD_AFFECT) {
524         enum xkb_action_flags val;
525
526         if (array_ndx)
527             return ReportActionNotArray(keymap, action->type, field);
528
529         if (!ExprResolveEnum(keymap->ctx, value, &val, lockWhich))
530             return ReportMismatch(keymap, action->type, field,
531                                   "lock or unlock");
532
533         act->flags &= ~(ACTION_LOCK_NO_LOCK | ACTION_LOCK_NO_UNLOCK);
534         act->flags |= val;
535         return true;
536     }
537     else if (field == ACTION_FIELD_COUNT) {
538         int btn;
539
540         if (array_ndx)
541             return ReportActionNotArray(keymap, action->type, field);
542
543         /* XXX: Should this actually be ResolveButton? */
544         if (!ExprResolveButton(keymap->ctx, value, &btn))
545             return ReportMismatch(keymap, action->type, field, "integer");
546
547         if (btn < 0 || btn > 255) {
548             log_err(keymap->ctx,
549                     "The count field must have a value in the range 0..255; "
550                     "Illegal count %d ignored\n", btn);
551             return false;
552         }
553
554         act->count = btn;
555         return true;
556     }
557     return ReportIllegal(keymap, action->type, field);
558 }
559
560 static const LookupEntry ptrDflts[] = {
561     { "dfltbtn", XkbSA_AffectDfltBtn },
562     { "defaultbutton", XkbSA_AffectDfltBtn },
563     { "button", XkbSA_AffectDfltBtn },
564     { NULL, 0 }
565 };
566
567 static bool
568 HandleSetPtrDflt(struct xkb_keymap *keymap, union xkb_action *action,
569                  enum action_field field, const ExprDef *array_ndx,
570                  const ExprDef *value)
571 {
572     struct xkb_pointer_default_action *act = &action->dflt;
573
574     if (field == ACTION_FIELD_AFFECT) {
575         unsigned int val;
576
577         if (array_ndx)
578             return ReportActionNotArray(keymap, action->type, field);
579
580         if (!ExprResolveEnum(keymap->ctx, value, &val, ptrDflts))
581             return ReportMismatch(keymap, action->type, field,
582                                   "pointer component");
583         act->affect = val;
584         return true;
585     }
586     else if (field == ACTION_FIELD_BUTTON || field == ACTION_FIELD_VALUE) {
587         const ExprDef *button;
588         int btn;
589
590         if (array_ndx)
591             return ReportActionNotArray(keymap, action->type, field);
592
593         if (value->op == EXPR_NEGATE || value->op == EXPR_UNARY_PLUS) {
594             act->flags &= ~ACTION_ABSOLUTE_SWITCH;
595             button = value->value.child;
596         }
597         else {
598             act->flags |= ACTION_ABSOLUTE_SWITCH;
599             button = value;
600         }
601
602         if (!ExprResolveButton(keymap->ctx, button, &btn))
603             return ReportMismatch(keymap, action->type, field,
604                                   "integer (range 1..5)");
605
606         if (btn < 0 || btn > 5) {
607             log_err(keymap->ctx,
608                     "New default button value must be in the range 1..5; "
609                     "Illegal default button value %d ignored\n", btn);
610             return false;
611         }
612         if (btn == 0) {
613             log_err(keymap->ctx,
614                     "Cannot set default pointer button to \"default\"; "
615                     "Illegal default button setting ignored\n");
616             return false;
617         }
618
619         act->value = (value->op == EXPR_NEGATE ? -btn: btn);
620         return true;
621     }
622
623     return ReportIllegal(keymap, action->type, field);
624 }
625
626 static bool
627 HandleSwitchScreen(struct xkb_keymap *keymap, union xkb_action *action,
628                    enum action_field field, const ExprDef *array_ndx,
629                    const ExprDef *value)
630 {
631     struct xkb_switch_screen_action *act = &action->screen;
632
633     if (field == ACTION_FIELD_SCREEN) {
634         const ExprDef *scrn;
635         int val;
636
637         if (array_ndx)
638             return ReportActionNotArray(keymap, action->type, field);
639
640         if (value->op == EXPR_NEGATE || value->op == EXPR_UNARY_PLUS) {
641             act->flags &= ~ACTION_ABSOLUTE_SWITCH;
642             scrn = value->value.child;
643         }
644         else {
645             act->flags |= ACTION_ABSOLUTE_SWITCH;
646             scrn = value;
647         }
648
649         if (!ExprResolveInteger(keymap->ctx, scrn, &val))
650             return ReportMismatch(keymap, action->type, field,
651                                   "integer (0..255)");
652
653         if (val < 0 || val > 255) {
654             log_err(keymap->ctx,
655                     "Screen index must be in the range 1..255; "
656                     "Illegal screen value %d ignored\n", val);
657             return false;
658         }
659
660         act->screen = (value->op == EXPR_NEGATE ? -val : val);
661         return true;
662     }
663     else if (field == ACTION_FIELD_SAME) {
664         bool set;
665
666         if (array_ndx)
667             return ReportActionNotArray(keymap, action->type, field);
668
669         if (!ExprResolveBoolean(keymap->ctx, value, &set))
670             return ReportMismatch(keymap, action->type, field, "boolean");
671
672         if (set)
673             act->flags &= ~ACTION_SAME_SCREEN;
674         else
675             act->flags |= ACTION_SAME_SCREEN;
676
677         return true;
678     }
679
680     return ReportIllegal(keymap, action->type, field);
681 }
682
683 static bool
684 HandleSetLockControls(struct xkb_keymap *keymap, union xkb_action *action,
685                       enum action_field field, const ExprDef *array_ndx,
686                       const ExprDef *value)
687 {
688     struct xkb_controls_action *act = &action->ctrls;
689
690     if (field == ACTION_FIELD_CONTROLS) {
691         unsigned int mask;
692
693         if (array_ndx)
694             return ReportActionNotArray(keymap, action->type, field);
695
696         if (!ExprResolveMask(keymap->ctx, value, &mask, ctrlMaskNames))
697             return ReportMismatch(keymap, action->type, field,
698                                   "controls mask");
699
700         act->ctrls = mask;
701         return true;
702     }
703
704     return ReportIllegal(keymap, action->type, field);
705 }
706
707 static bool
708 HandlePrivate(struct xkb_keymap *keymap, union xkb_action *action,
709               enum action_field field, const ExprDef *array_ndx,
710               const ExprDef *value)
711 {
712     struct xkb_private_action *act = &action->priv;
713
714     if (field == ACTION_FIELD_TYPE) {
715         int type;
716
717         if (!ExprResolveInteger(keymap->ctx, value, &type))
718             return ReportMismatch(keymap, ACTION_TYPE_PRIVATE, field, "integer");
719
720         if (type < 0 || type > 255) {
721             log_err(keymap->ctx,
722                     "Private action type must be in the range 0..255; "
723                     "Illegal type %d ignored\n", type);
724             return false;
725         }
726
727         act->type = (uint8_t) type;
728         return true;
729     }
730     else if (field == ACTION_FIELD_DATA) {
731         if (array_ndx == NULL) {
732             xkb_atom_t val;
733             const char *str;
734             int len;
735
736             if (!ExprResolveString(keymap->ctx, value, &val))
737                 return ReportMismatch(keymap, action->type, field, "string");
738
739             str = xkb_atom_text(keymap->ctx, val);
740             len = strlen(str);
741             if (len < 1 || len > 7) {
742                 log_warn(keymap->ctx,
743                          "A private action has 7 data bytes; "
744                          "Extra %d bytes ignored\n", len - 6);
745                 return false;
746             }
747
748             strncpy((char *) act->data, str, sizeof(act->data));
749             return true;
750         }
751         else {
752             int ndx, datum;
753
754             if (!ExprResolveInteger(keymap->ctx, array_ndx, &ndx)) {
755                 log_err(keymap->ctx,
756                         "Array subscript must be integer; "
757                         "Illegal subscript ignored\n");
758                 return false;
759             }
760
761             if (ndx < 0 || ndx >= sizeof(act->data)) {
762                 log_err(keymap->ctx,
763                         "The data for a private action is %zu bytes long; "
764                         "Attempt to use data[%d] ignored\n",
765                         sizeof(act->data), ndx);
766                 return false;
767             }
768
769             if (!ExprResolveInteger(keymap->ctx, value, &datum))
770                 return ReportMismatch(keymap, act->type, field, "integer");
771
772             if (datum < 0 || datum > 255) {
773                 log_err(keymap->ctx,
774                         "All data for a private action must be 0..255; "
775                         "Illegal datum %d ignored\n", datum);
776                 return false;
777             }
778
779             act->data[ndx] = (uint8_t) datum;
780             return true;
781         }
782     }
783
784     return ReportIllegal(keymap, ACTION_TYPE_NONE, field);
785 }
786
787 typedef bool (*actionHandler)(struct xkb_keymap *keymap,
788                               union xkb_action *action,
789                               enum action_field field,
790                               const ExprDef *array_ndx,
791                               const ExprDef *value);
792
793 static const actionHandler handleAction[ACTION_TYPE_LAST] = {
794     [ACTION_TYPE_NONE] = HandleNoAction,
795     [ACTION_TYPE_MOD_SET] = HandleSetLatchMods,
796     [ACTION_TYPE_MOD_LATCH] = HandleSetLatchMods,
797     [ACTION_TYPE_MOD_LOCK] = HandleLockMods,
798     [ACTION_TYPE_GROUP_SET] = HandleSetLatchGroup,
799     [ACTION_TYPE_GROUP_LATCH] = HandleSetLatchGroup,
800     [ACTION_TYPE_GROUP_LOCK] = HandleLockGroup,
801     [ACTION_TYPE_PTR_MOVE] = HandleMovePtr,
802     [ACTION_TYPE_PTR_BUTTON] = HandlePtrBtn,
803     [ACTION_TYPE_PTR_LOCK] = HandlePtrBtn,
804     [ACTION_TYPE_PTR_DEFAULT] = HandleSetPtrDflt,
805     [ACTION_TYPE_TERMINATE] = HandleNoAction,
806     [ACTION_TYPE_SWITCH_VT] = HandleSwitchScreen,
807     [ACTION_TYPE_CTRL_SET] = HandleSetLockControls,
808     [ACTION_TYPE_CTRL_LOCK] = HandleSetLockControls,
809     [ACTION_TYPE_PRIVATE] = HandlePrivate,
810 };
811
812 /***====================================================================***/
813
814 bool
815 HandleActionDef(ExprDef *def, struct xkb_keymap *keymap,
816                 union xkb_action *action, ActionsInfo *info)
817 {
818     ExprDef *arg;
819     const char *str;
820     unsigned hndlrType;
821
822     if (def->op != EXPR_ACTION_DECL) {
823         log_err(keymap->ctx, "Expected an action definition, found %s\n",
824                 expr_op_type_to_string(def->op));
825         return false;
826     }
827
828     str = xkb_atom_text(keymap->ctx, def->value.action.name);
829     if (!stringToAction(str, &hndlrType)) {
830         log_err(keymap->ctx, "Unknown action %s\n", str);
831         return false;
832     }
833
834     /*
835      * Get the default values for this action type, as modified by
836      * statements such as:
837      *     latchMods.clearLocks = True;
838      */
839     *action = info->actions[hndlrType];
840
841     /*
842      * Now change the action properties as specified for this
843      * particular instance, e.g. "modifiers" and "clearLocks" in:
844      *     SetMods(modifiers=Alt,clearLocks);
845      */
846     for (arg = def->value.action.args; arg != NULL;
847          arg = (ExprDef *) arg->common.next) {
848         const ExprDef *value;
849         ExprDef *field, *arrayRtrn;
850         const char *elemRtrn, *fieldRtrn;
851         enum action_field fieldNdx;
852
853         if (arg->op == EXPR_ASSIGN) {
854             field = arg->value.binary.left;
855             value = arg->value.binary.right;
856         }
857         else if (arg->op == EXPR_NOT || arg->op == EXPR_INVERT) {
858             field = arg->value.child;
859             value = &constFalse;
860         }
861         else {
862             field = arg;
863             value = &constTrue;
864         }
865
866         if (!ExprResolveLhs(keymap->ctx, field, &elemRtrn, &fieldRtrn,
867                             &arrayRtrn))
868             return false;
869
870         if (elemRtrn) {
871             log_err(keymap->ctx,
872                     "Cannot change defaults in an action definition; "
873                     "Ignoring attempt to change %s.%s\n",
874                     elemRtrn, fieldRtrn);
875             return false;
876         }
877
878         if (!stringToField(fieldRtrn, &fieldNdx)) {
879             log_err(keymap->ctx, "Unknown field name %s\n", fieldRtrn);
880             return false;
881         }
882
883         if (!handleAction[hndlrType](keymap, action, fieldNdx, arrayRtrn,
884                                      value))
885             return false;
886     }
887
888     return true;
889 }
890
891
892 bool
893 SetActionField(struct xkb_keymap *keymap, const char *elem, const char *field,
894                ExprDef *array_ndx, ExprDef *value, ActionsInfo *info)
895 {
896     unsigned action;
897     enum action_field action_field;
898
899     if (!stringToAction(elem, &action))
900         return false;
901
902     if (!stringToField(field, &action_field)) {
903         log_err(keymap->ctx, "\"%s\" is not a legal field name\n", field);
904         return false;
905     }
906
907     return handleAction[action](keymap, &info->actions[action],
908                                 action_field, array_ndx, value);
909 }