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