kbproto unentanglement: action 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 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                     unsigned *flags_inout)
217 {
218     unsigned tmp;
219     bool result;
220
221     if (field == ACTION_FIELD_CLEAR_LOCKS)
222         tmp = XkbSA_ClearLocks;
223     else if (field == ACTION_FIELD_LATCH_TO_LOCK)
224         tmp = XkbSA_LatchToLock;
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, unsigned *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 |= XkbSA_UseModMapMods;
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 &= ~XkbSA_UseModMapMods;
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     unsigned rtrn;
271     unsigned t1;
272     xkb_mod_mask_t t2;
273
274     if (array_ndx != NULL) {
275         switch (field) {
276         case ACTION_FIELD_CLEAR_LOCKS:
277         case ACTION_FIELD_LATCH_TO_LOCK:
278         case ACTION_FIELD_MODIFIERS:
279             return ReportActionNotArray(keymap, action->type, field);
280         default:
281             break;
282         }
283     }
284
285     switch (field) {
286     case ACTION_FIELD_CLEAR_LOCKS:
287     case ACTION_FIELD_LATCH_TO_LOCK:
288         rtrn = act->flags;
289         if (CheckLatchLockFlags(keymap, action->type, field, value, &rtrn)) {
290             act->flags = rtrn;
291             return true;
292         }
293         return false;
294
295     case ACTION_FIELD_MODIFIERS:
296         t1 = act->flags;
297         if (CheckModifierField(keymap, action->type, value, &t1, &t2)) {
298             act->flags = t1;
299             act->mods.mods = t2;
300             return true;
301         }
302         return false;
303
304     default:
305         break;
306     }
307
308     return ReportIllegal(keymap, action->type, field);
309 }
310
311 static bool
312 HandleLockMods(struct xkb_keymap *keymap, union xkb_action *action,
313                enum action_field field, const ExprDef *array_ndx,
314                const ExprDef *value)
315 {
316     struct xkb_mod_action *act = &action->mods;
317     unsigned t1;
318     xkb_mod_mask_t t2;
319
320     if (array_ndx && field == ACTION_FIELD_MODIFIERS)
321         return ReportActionNotArray(keymap, action->type, field);
322
323     switch (field) {
324     case ACTION_FIELD_MODIFIERS:
325         t1 = act->flags;
326         if (CheckModifierField(keymap, action->type, value, &t1, &t2)) {
327             act->flags = t1;
328             act->mods.mods = t2;
329             return true;
330         }
331         return false;
332
333     default:
334         break;
335     }
336
337     return ReportIllegal(keymap, action->type, field);
338 }
339
340 static bool
341 CheckGroupField(struct xkb_keymap *keymap, unsigned action,
342                 const ExprDef *value, unsigned *flags_inout,
343                 xkb_group_index_t *grp_rtrn)
344 {
345     const ExprDef *spec;
346
347     if (value->op == EXPR_NEGATE || value->op == EXPR_UNARY_PLUS) {
348         *flags_inout &= ~XkbSA_GroupAbsolute;
349         spec = value->value.child;
350     }
351     else {
352         *flags_inout |= XkbSA_GroupAbsolute;
353         spec = value;
354     }
355
356     if (!ExprResolveGroup(keymap->ctx, spec, grp_rtrn))
357         return ReportMismatch(keymap, action, ACTION_FIELD_GROUP,
358                               "integer (range 1..8)");
359
360     if (value->op == EXPR_NEGATE)
361         *grp_rtrn = -*grp_rtrn;
362     else if (value->op != EXPR_UNARY_PLUS)
363         (*grp_rtrn)--;
364
365     return true;
366 }
367
368 static bool
369 HandleSetLatchGroup(struct xkb_keymap *keymap, union xkb_action *action,
370                     enum action_field field, const ExprDef *array_ndx,
371                     const ExprDef *value)
372 {
373     struct xkb_group_action *act = &action->group;
374     unsigned rtrn;
375     unsigned t1;
376     xkb_group_index_t t2;
377
378     if (array_ndx != NULL) {
379         switch (field) {
380         case ACTION_FIELD_CLEAR_LOCKS:
381         case ACTION_FIELD_LATCH_TO_LOCK:
382         case ACTION_FIELD_GROUP:
383             return ReportActionNotArray(keymap, action->type, field);
384
385         default:
386             break;
387         }
388     }
389
390     switch (field) {
391     case ACTION_FIELD_CLEAR_LOCKS:
392     case ACTION_FIELD_LATCH_TO_LOCK:
393         rtrn = act->flags;
394         if (CheckLatchLockFlags(keymap, action->type, field, value, &rtrn)) {
395             act->flags = rtrn;
396             return true;
397         }
398         return false;
399
400     case ACTION_FIELD_GROUP:
401         t1 = act->flags;
402         if (CheckGroupField(keymap, action->type, value, &t1, &t2)) {
403             act->flags = t1;
404             act->group = t2;
405             return true;
406         }
407         return false;
408
409     default:
410         break;
411     }
412
413     return ReportIllegal(keymap, action->type, field);
414 }
415
416 static bool
417 HandleLockGroup(struct xkb_keymap *keymap, union xkb_action *action,
418                 enum action_field field, const ExprDef *array_ndx,
419                 const ExprDef *value)
420 {
421     struct xkb_group_action *act = &action->group;
422     unsigned t1;
423     xkb_group_index_t t2;
424
425     if ((array_ndx != NULL) && (field == ACTION_FIELD_GROUP))
426         return ReportActionNotArray(keymap, action->type, field);
427     if (field == ACTION_FIELD_GROUP) {
428         t1 = act->flags;
429         if (CheckGroupField(keymap, action->type, value, &t1, &t2)) {
430             act->flags = t1;
431             act->group = t2;
432             return true;
433         }
434         return false;
435     }
436     return ReportIllegal(keymap, action->type, field);
437 }
438
439 static bool
440 HandleMovePtr(struct xkb_keymap *keymap, union xkb_action *action,
441               enum action_field field, const ExprDef *array_ndx,
442               const ExprDef *value)
443 {
444     struct xkb_pointer_action *act = &action->ptr;
445     bool absolute;
446
447     if (array_ndx && (field == ACTION_FIELD_X || field == ACTION_FIELD_Y))
448         return ReportActionNotArray(keymap, action->type, field);
449
450     if (field == ACTION_FIELD_X || field == ACTION_FIELD_Y) {
451         int val;
452
453         if (value->op == EXPR_NEGATE || value->op == EXPR_UNARY_PLUS)
454             absolute = false;
455         else
456             absolute = true;
457
458         if (!ExprResolveInteger(keymap->ctx, value, &val))
459             return ReportMismatch(keymap, action->type, field, "integer");
460
461         if (field == ACTION_FIELD_X) {
462             if (absolute)
463                 act->flags |= XkbSA_MoveAbsoluteX;
464             act->x = val;
465         }
466         else {
467             if (absolute)
468                 act->flags |= XkbSA_MoveAbsoluteY;
469             act->y = val;
470         }
471
472         return true;
473     }
474     else if (field == ACTION_FIELD_ACCEL) {
475         bool set;
476
477         if (!ExprResolveBoolean(keymap->ctx, value, &set))
478             return ReportMismatch(keymap, action->type, field, "boolean");
479
480         if (set)
481             act->flags &= ~XkbSA_NoAcceleration;
482         else
483             act->flags |= XkbSA_NoAcceleration;
484     }
485
486     return ReportIllegal(keymap, action->type, field);
487 }
488
489 static const LookupEntry lockWhich[] = {
490     { "both", 0 },
491     { "lock", XkbSA_LockNoUnlock },
492     { "neither", (XkbSA_LockNoLock | XkbSA_LockNoUnlock) },
493     { "unlock", XkbSA_LockNoLock },
494     { NULL, 0 }
495 };
496
497 static bool
498 HandlePtrBtn(struct xkb_keymap *keymap, union xkb_action *action,
499              enum action_field field, const ExprDef *array_ndx,
500              const ExprDef *value)
501 {
502     struct xkb_pointer_button_action *act = &action->btn;
503
504     if (field == ACTION_FIELD_BUTTON) {
505         int btn;
506
507         if (array_ndx)
508             return ReportActionNotArray(keymap, action->type, field);
509
510         if (!ExprResolveButton(keymap->ctx, value, &btn))
511             return ReportMismatch(keymap, action->type, field,
512                                   "integer (range 1..5)");
513
514         if (btn < 0 || btn > 5) {
515             log_err(keymap->ctx,
516                     "Button must specify default or be in the range 1..5; "
517                     "Illegal button value %d ignored\n", btn);
518             return false;
519         }
520
521         act->button = btn;
522         return true;
523     }
524     else if (action->type == ACTION_TYPE_PTR_LOCK &&
525              field == ACTION_FIELD_AFFECT) {
526         unsigned int val;
527
528         if (array_ndx)
529             return ReportActionNotArray(keymap, action->type, field);
530
531         if (!ExprResolveEnum(keymap->ctx, value, &val, lockWhich))
532             return ReportMismatch(keymap, action->type, field,
533                                   "lock or unlock");
534
535         act->flags &= ~(XkbSA_LockNoLock | XkbSA_LockNoUnlock);
536         act->flags |= val;
537         return true;
538     }
539     else if (field == ACTION_FIELD_COUNT) {
540         int btn;
541
542         if (array_ndx)
543             return ReportActionNotArray(keymap, action->type, field);
544
545         /* XXX: Should this actually be ResolveButton? */
546         if (!ExprResolveButton(keymap->ctx, value, &btn))
547             return ReportMismatch(keymap, action->type, field, "integer");
548
549         if (btn < 0 || btn > 255) {
550             log_err(keymap->ctx,
551                     "The count field must have a value in the range 0..255; "
552                     "Illegal count %d ignored\n", btn);
553             return false;
554         }
555
556         act->count = btn;
557         return true;
558     }
559     return ReportIllegal(keymap, action->type, field);
560 }
561
562 static const LookupEntry ptrDflts[] = {
563     { "dfltbtn", XkbSA_AffectDfltBtn },
564     { "defaultbutton", XkbSA_AffectDfltBtn },
565     { "button", XkbSA_AffectDfltBtn },
566     { NULL, 0 }
567 };
568
569 static bool
570 HandleSetPtrDflt(struct xkb_keymap *keymap, union xkb_action *action,
571                  enum action_field field, const ExprDef *array_ndx,
572                  const ExprDef *value)
573 {
574     struct xkb_pointer_default_action *act = &action->dflt;
575
576     if (field == ACTION_FIELD_AFFECT) {
577         unsigned int val;
578
579         if (array_ndx)
580             return ReportActionNotArray(keymap, action->type, field);
581
582         if (!ExprResolveEnum(keymap->ctx, value, &val, ptrDflts))
583             return ReportMismatch(keymap, action->type, field,
584                                   "pointer component");
585         act->affect = val;
586         return true;
587     }
588     else if (field == ACTION_FIELD_BUTTON || field == ACTION_FIELD_VALUE) {
589         const ExprDef *button;
590         int btn;
591
592         if (array_ndx)
593             return ReportActionNotArray(keymap, action->type, field);
594
595         if (value->op == EXPR_NEGATE || value->op == EXPR_UNARY_PLUS) {
596             act->flags &= ~XkbSA_DfltBtnAbsolute;
597             button = value->value.child;
598         }
599         else {
600             act->flags |= XkbSA_DfltBtnAbsolute;
601             button = value;
602         }
603
604         if (!ExprResolveButton(keymap->ctx, button, &btn))
605             return ReportMismatch(keymap, action->type, field,
606                                   "integer (range 1..5)");
607
608         if (btn < 0 || btn > 5) {
609             log_err(keymap->ctx,
610                     "New default button value must be in the range 1..5; "
611                     "Illegal default button value %d ignored\n", btn);
612             return false;
613         }
614         if (btn == 0) {
615             log_err(keymap->ctx,
616                     "Cannot set default pointer button to \"default\"; "
617                     "Illegal default button setting ignored\n");
618             return false;
619         }
620
621         act->value = (value->op == EXPR_NEGATE ? -btn: btn);
622         return true;
623     }
624
625     return ReportIllegal(keymap, action->type, field);
626 }
627
628 static bool
629 HandleSwitchScreen(struct xkb_keymap *keymap, union xkb_action *action,
630                    enum action_field field, const ExprDef *array_ndx,
631                    const ExprDef *value)
632 {
633     struct xkb_switch_screen_action *act = &action->screen;
634
635     if (field == ACTION_FIELD_SCREEN) {
636         const ExprDef *scrn;
637         int val;
638
639         if (array_ndx)
640             return ReportActionNotArray(keymap, action->type, field);
641
642         if (value->op == EXPR_NEGATE || value->op == EXPR_UNARY_PLUS) {
643             act->flags &= ~XkbSA_SwitchAbsolute;
644             scrn = value->value.child;
645         }
646         else {
647             act->flags |= XkbSA_SwitchAbsolute;
648             scrn = value;
649         }
650
651         if (!ExprResolveInteger(keymap->ctx, scrn, &val))
652             return ReportMismatch(keymap, action->type, field,
653                                   "integer (0..255)");
654
655         if (val < 0 || val > 255) {
656             log_err(keymap->ctx,
657                     "Screen index must be in the range 1..255; "
658                     "Illegal screen value %d ignored\n", val);
659             return false;
660         }
661
662         act->screen = (value->op == EXPR_NEGATE ? -val : val);
663         return true;
664     }
665     else if (field == ACTION_FIELD_SAME) {
666         bool set;
667
668         if (array_ndx)
669             return ReportActionNotArray(keymap, action->type, field);
670
671         if (!ExprResolveBoolean(keymap->ctx, value, &set))
672             return ReportMismatch(keymap, action->type, field, "boolean");
673
674         if (set)
675             act->flags &= ~XkbSA_SwitchApplication;
676         else
677             act->flags |= XkbSA_SwitchApplication;
678
679         return true;
680     }
681
682     return ReportIllegal(keymap, action->type, field);
683 }
684
685 static bool
686 HandleSetLockControls(struct xkb_keymap *keymap, union xkb_action *action,
687                       enum action_field field, const ExprDef *array_ndx,
688                       const ExprDef *value)
689 {
690     struct xkb_controls_action *act = &action->ctrls;
691
692     if (field == ACTION_FIELD_CONTROLS) {
693         unsigned int mask;
694
695         if (array_ndx)
696             return ReportActionNotArray(keymap, action->type, field);
697
698         if (!ExprResolveMask(keymap->ctx, value, &mask, ctrlMaskNames))
699             return ReportMismatch(keymap, action->type, field,
700                                   "controls mask");
701
702         act->ctrls = mask;
703         return true;
704     }
705
706     return ReportIllegal(keymap, action->type, field);
707 }
708
709 static bool
710 HandlePrivate(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_private_action *act = &action->priv;
715
716     if (field == ACTION_FIELD_TYPE) {
717         int type;
718
719         if (!ExprResolveInteger(keymap->ctx, value, &type))
720             return ReportMismatch(keymap, ACTION_TYPE_PRIVATE, field, "integer");
721
722         if (type < 0 || type > 255) {
723             log_err(keymap->ctx,
724                     "Private action type must be in the range 0..255; "
725                     "Illegal type %d ignored\n", type);
726             return false;
727         }
728
729         act->type = (uint8_t) type;
730         return true;
731     }
732     else if (field == ACTION_FIELD_DATA) {
733         if (array_ndx == NULL) {
734             xkb_atom_t val;
735             const char *str;
736             int len;
737
738             if (!ExprResolveString(keymap->ctx, value, &val))
739                 return ReportMismatch(keymap, action->type, field, "string");
740
741             str = xkb_atom_text(keymap->ctx, val);
742             len = strlen(str);
743             if (len < 1 || len > 7) {
744                 log_warn(keymap->ctx,
745                          "A private action has 7 data bytes; "
746                          "Extra %d bytes ignored\n", len - 6);
747                 return false;
748             }
749
750             strncpy((char *) act->data, str, sizeof(act->data));
751             return true;
752         }
753         else {
754             int ndx, datum;
755
756             if (!ExprResolveInteger(keymap->ctx, array_ndx, &ndx)) {
757                 log_err(keymap->ctx,
758                         "Array subscript must be integer; "
759                         "Illegal subscript ignored\n");
760                 return false;
761             }
762
763             if (ndx < 0 || ndx >= sizeof(act->data)) {
764                 log_err(keymap->ctx,
765                         "The data for a private action is %zu bytes long; "
766                         "Attempt to use data[%d] ignored\n",
767                         sizeof(act->data), ndx);
768                 return false;
769             }
770
771             if (!ExprResolveInteger(keymap->ctx, value, &datum))
772                 return ReportMismatch(keymap, act->type, field, "integer");
773
774             if (datum < 0 || datum > 255) {
775                 log_err(keymap->ctx,
776                         "All data for a private action must be 0..255; "
777                         "Illegal datum %d ignored\n", datum);
778                 return false;
779             }
780
781             act->data[ndx] = (uint8_t) datum;
782             return true;
783         }
784     }
785
786     return ReportIllegal(keymap, ACTION_TYPE_NONE, field);
787 }
788
789 typedef bool (*actionHandler)(struct xkb_keymap *keymap,
790                               union xkb_action *action,
791                               enum action_field field,
792                               const ExprDef *array_ndx,
793                               const ExprDef *value);
794
795 static const actionHandler handleAction[ACTION_TYPE_LAST] = {
796     [ACTION_TYPE_NONE] = HandleNoAction,
797     [ACTION_TYPE_MOD_SET] = HandleSetLatchMods,
798     [ACTION_TYPE_MOD_LATCH] = HandleSetLatchMods,
799     [ACTION_TYPE_MOD_LOCK] = HandleLockMods,
800     [ACTION_TYPE_GROUP_SET] = HandleSetLatchGroup,
801     [ACTION_TYPE_GROUP_LATCH] = HandleSetLatchGroup,
802     [ACTION_TYPE_GROUP_LOCK] = HandleLockGroup,
803     [ACTION_TYPE_PTR_MOVE] = HandleMovePtr,
804     [ACTION_TYPE_PTR_BUTTON] = HandlePtrBtn,
805     [ACTION_TYPE_PTR_LOCK] = HandlePtrBtn,
806     [ACTION_TYPE_PTR_DEFAULT] = HandleSetPtrDflt,
807     [ACTION_TYPE_TERMINATE] = HandleNoAction,
808     [ACTION_TYPE_SWITCH_VT] = HandleSwitchScreen,
809     [ACTION_TYPE_CTRL_SET] = HandleSetLockControls,
810     [ACTION_TYPE_CTRL_LOCK] = HandleSetLockControls,
811     [ACTION_TYPE_PRIVATE] = HandlePrivate,
812 };
813
814 /***====================================================================***/
815
816 bool
817 HandleActionDef(ExprDef *def, struct xkb_keymap *keymap,
818                 union xkb_action *action, ActionsInfo *info)
819 {
820     ExprDef *arg;
821     const char *str;
822     unsigned hndlrType;
823
824     if (def->op != EXPR_ACTION_DECL) {
825         log_err(keymap->ctx, "Expected an action definition, found %s\n",
826                 expr_op_type_to_string(def->op));
827         return false;
828     }
829
830     str = xkb_atom_text(keymap->ctx, def->value.action.name);
831     if (!stringToAction(str, &hndlrType)) {
832         log_err(keymap->ctx, "Unknown action %s\n", str);
833         return false;
834     }
835
836     /*
837      * Get the default values for this action type, as modified by
838      * statements such as:
839      *     latchMods.clearLocks = True;
840      */
841     *action = info->actions[hndlrType];
842
843     /*
844      * Now change the action properties as specified for this
845      * particular instance, e.g. "modifiers" and "clearLocks" in:
846      *     SetMods(modifiers=Alt,clearLocks);
847      */
848     for (arg = def->value.action.args; arg != NULL;
849          arg = (ExprDef *) arg->common.next) {
850         const ExprDef *value;
851         ExprDef *field, *arrayRtrn;
852         const char *elemRtrn, *fieldRtrn;
853         enum action_field fieldNdx;
854
855         if (arg->op == EXPR_ASSIGN) {
856             field = arg->value.binary.left;
857             value = arg->value.binary.right;
858         }
859         else if (arg->op == EXPR_NOT || arg->op == EXPR_INVERT) {
860             field = arg->value.child;
861             value = &constFalse;
862         }
863         else {
864             field = arg;
865             value = &constTrue;
866         }
867
868         if (!ExprResolveLhs(keymap->ctx, field, &elemRtrn, &fieldRtrn,
869                             &arrayRtrn))
870             return false;
871
872         if (elemRtrn) {
873             log_err(keymap->ctx,
874                     "Cannot change defaults in an action definition; "
875                     "Ignoring attempt to change %s.%s\n",
876                     elemRtrn, fieldRtrn);
877             return false;
878         }
879
880         if (!stringToField(fieldRtrn, &fieldNdx)) {
881             log_err(keymap->ctx, "Unknown field name %s\n", fieldRtrn);
882             return false;
883         }
884
885         if (!handleAction[hndlrType](keymap, action, fieldNdx, arrayRtrn,
886                                      value))
887             return false;
888     }
889
890     return true;
891 }
892
893
894 bool
895 SetActionField(struct xkb_keymap *keymap, const char *elem, const char *field,
896                ExprDef *array_ndx, ExprDef *value, ActionsInfo *info)
897 {
898     unsigned action;
899     enum action_field action_field;
900
901     if (!stringToAction(elem, &action))
902         return false;
903
904     if (!stringToField(field, &action_field)) {
905         log_err(keymap->ctx, "\"%s\" is not a legal field name\n", field);
906         return false;
907     }
908
909     return handleAction[action](keymap, &info->actions[action],
910                                 action_field, array_ndx, value);
911 }