action.c revision c82dfdfb
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.h"
28#include "tokens.h"
29#include "expr.h"
30
31#include "keycodes.h"
32#include "vmod.h"
33#include "misc.h"
34#include "action.h"
35#include "misc.h"
36
37static Bool actionsInitialized;
38static ExprDef constTrue;
39static ExprDef constFalse;
40
41/***====================================================================***/
42
43static Bool
44stringToAction(const char *str, unsigned *type_rtrn)
45{
46    if (str == NULL)
47        return False;
48
49    if (uStrCaseCmp(str, "noaction") == 0)
50        *type_rtrn = XkbSA_NoAction;
51    else if (uStrCaseCmp(str, "setmods") == 0)
52        *type_rtrn = XkbSA_SetMods;
53    else if (uStrCaseCmp(str, "latchmods") == 0)
54        *type_rtrn = XkbSA_LatchMods;
55    else if (uStrCaseCmp(str, "lockmods") == 0)
56        *type_rtrn = XkbSA_LockMods;
57    else if (uStrCaseCmp(str, "setgroup") == 0)
58        *type_rtrn = XkbSA_SetGroup;
59    else if (uStrCaseCmp(str, "latchgroup") == 0)
60        *type_rtrn = XkbSA_LatchGroup;
61    else if (uStrCaseCmp(str, "lockgroup") == 0)
62        *type_rtrn = XkbSA_LockGroup;
63    else if (uStrCaseCmp(str, "moveptr") == 0)
64        *type_rtrn = XkbSA_MovePtr;
65    else if (uStrCaseCmp(str, "movepointer") == 0)
66        *type_rtrn = XkbSA_MovePtr;
67    else if (uStrCaseCmp(str, "ptrbtn") == 0)
68        *type_rtrn = XkbSA_PtrBtn;
69    else if (uStrCaseCmp(str, "pointerbutton") == 0)
70        *type_rtrn = XkbSA_PtrBtn;
71    else if (uStrCaseCmp(str, "lockptrbtn") == 0)
72        *type_rtrn = XkbSA_LockPtrBtn;
73    else if (uStrCaseCmp(str, "lockpointerbutton") == 0)
74        *type_rtrn = XkbSA_LockPtrBtn;
75    else if (uStrCaseCmp(str, "lockptrbutton") == 0)
76        *type_rtrn = XkbSA_LockPtrBtn;
77    else if (uStrCaseCmp(str, "lockpointerbtn") == 0)
78        *type_rtrn = XkbSA_LockPtrBtn;
79    else if (uStrCaseCmp(str, "setptrdflt") == 0)
80        *type_rtrn = XkbSA_SetPtrDflt;
81    else if (uStrCaseCmp(str, "setpointerdefault") == 0)
82        *type_rtrn = XkbSA_SetPtrDflt;
83    else if (uStrCaseCmp(str, "isolock") == 0)
84        *type_rtrn = XkbSA_ISOLock;
85    else if (uStrCaseCmp(str, "terminate") == 0)
86        *type_rtrn = XkbSA_Terminate;
87    else if (uStrCaseCmp(str, "terminateserver") == 0)
88        *type_rtrn = XkbSA_Terminate;
89    else if (uStrCaseCmp(str, "switchscreen") == 0)
90        *type_rtrn = XkbSA_SwitchScreen;
91    else if (uStrCaseCmp(str, "setcontrols") == 0)
92        *type_rtrn = XkbSA_SetControls;
93    else if (uStrCaseCmp(str, "lockcontrols") == 0)
94        *type_rtrn = XkbSA_LockControls;
95    else if (uStrCaseCmp(str, "actionmessage") == 0)
96        *type_rtrn = XkbSA_ActionMessage;
97    else if (uStrCaseCmp(str, "messageaction") == 0)
98        *type_rtrn = XkbSA_ActionMessage;
99    else if (uStrCaseCmp(str, "message") == 0)
100        *type_rtrn = XkbSA_ActionMessage;
101    else if (uStrCaseCmp(str, "redirect") == 0)
102        *type_rtrn = XkbSA_RedirectKey;
103    else if (uStrCaseCmp(str, "redirectkey") == 0)
104        *type_rtrn = XkbSA_RedirectKey;
105    else if (uStrCaseCmp(str, "devbtn") == 0)
106        *type_rtrn = XkbSA_DeviceBtn;
107    else if (uStrCaseCmp(str, "devicebtn") == 0)
108        *type_rtrn = XkbSA_DeviceBtn;
109    else if (uStrCaseCmp(str, "devbutton") == 0)
110        *type_rtrn = XkbSA_DeviceBtn;
111    else if (uStrCaseCmp(str, "devicebutton") == 0)
112        *type_rtrn = XkbSA_DeviceBtn;
113    else if (uStrCaseCmp(str, "lockdevbtn") == 0)
114        *type_rtrn = XkbSA_DeviceBtn;
115    else if (uStrCaseCmp(str, "lockdevicebtn") == 0)
116        *type_rtrn = XkbSA_LockDeviceBtn;
117    else if (uStrCaseCmp(str, "lockdevbutton") == 0)
118        *type_rtrn = XkbSA_LockDeviceBtn;
119    else if (uStrCaseCmp(str, "lockdevicebutton") == 0)
120        *type_rtrn = XkbSA_LockDeviceBtn;
121    else if (uStrCaseCmp(str, "devval") == 0)
122        *type_rtrn = XkbSA_DeviceValuator;
123    else if (uStrCaseCmp(str, "deviceval") == 0)
124        *type_rtrn = XkbSA_DeviceValuator;
125    else if (uStrCaseCmp(str, "devvaluator") == 0)
126        *type_rtrn = XkbSA_DeviceValuator;
127    else if (uStrCaseCmp(str, "devicevaluator") == 0)
128        *type_rtrn = XkbSA_DeviceValuator;
129    else if (uStrCaseCmp(str, "private") == 0)
130        *type_rtrn = PrivateAction;
131    else
132        return False;
133    return True;
134}
135
136static Bool
137stringToField(const char *str, unsigned *field_rtrn)
138{
139
140    if (str == NULL)
141        return False;
142
143    if (uStrCaseCmp(str, "clearlocks") == 0)
144        *field_rtrn = F_ClearLocks;
145    else if (uStrCaseCmp(str, "latchtolock") == 0)
146        *field_rtrn = F_LatchToLock;
147    else if (uStrCaseCmp(str, "genkeyevent") == 0)
148        *field_rtrn = F_GenKeyEvent;
149    else if (uStrCaseCmp(str, "generatekeyevent") == 0)
150        *field_rtrn = F_GenKeyEvent;
151    else if (uStrCaseCmp(str, "report") == 0)
152        *field_rtrn = F_Report;
153    else if (uStrCaseCmp(str, "default") == 0)
154        *field_rtrn = F_Default;
155    else if (uStrCaseCmp(str, "affect") == 0)
156        *field_rtrn = F_Affect;
157    else if (uStrCaseCmp(str, "increment") == 0)
158        *field_rtrn = F_Increment;
159    else if (uStrCaseCmp(str, "mods") == 0)
160        *field_rtrn = F_Modifiers;
161    else if (uStrCaseCmp(str, "modifiers") == 0)
162        *field_rtrn = F_Modifiers;
163    else if (uStrCaseCmp(str, "group") == 0)
164        *field_rtrn = F_Group;
165    else if (uStrCaseCmp(str, "x") == 0)
166        *field_rtrn = F_X;
167    else if (uStrCaseCmp(str, "y") == 0)
168        *field_rtrn = F_Y;
169    else if (uStrCaseCmp(str, "accel") == 0)
170        *field_rtrn = F_Accel;
171    else if (uStrCaseCmp(str, "accelerate") == 0)
172        *field_rtrn = F_Accel;
173    else if (uStrCaseCmp(str, "repeat") == 0)
174        *field_rtrn = F_Accel;
175    else if (uStrCaseCmp(str, "button") == 0)
176        *field_rtrn = F_Button;
177    else if (uStrCaseCmp(str, "value") == 0)
178        *field_rtrn = F_Value;
179    else if (uStrCaseCmp(str, "controls") == 0)
180        *field_rtrn = F_Controls;
181    else if (uStrCaseCmp(str, "ctrls") == 0)
182        *field_rtrn = F_Controls;
183    else if (uStrCaseCmp(str, "type") == 0)
184        *field_rtrn = F_Type;
185    else if (uStrCaseCmp(str, "count") == 0)
186        *field_rtrn = F_Count;
187    else if (uStrCaseCmp(str, "screen") == 0)
188        *field_rtrn = F_Screen;
189    else if (uStrCaseCmp(str, "same") == 0)
190        *field_rtrn = F_Same;
191    else if (uStrCaseCmp(str, "sameserver") == 0)
192        *field_rtrn = F_Same;
193    else if (uStrCaseCmp(str, "data") == 0)
194        *field_rtrn = F_Data;
195    else if (uStrCaseCmp(str, "device") == 0)
196        *field_rtrn = F_Device;
197    else if (uStrCaseCmp(str, "dev") == 0)
198        *field_rtrn = F_Device;
199    else if (uStrCaseCmp(str, "key") == 0)
200        *field_rtrn = F_Keycode;
201    else if (uStrCaseCmp(str, "keycode") == 0)
202        *field_rtrn = F_Keycode;
203    else if (uStrCaseCmp(str, "kc") == 0)
204        *field_rtrn = F_Keycode;
205    else if (uStrCaseCmp(str, "clearmods") == 0)
206        *field_rtrn = F_ModsToClear;
207    else if (uStrCaseCmp(str, "clearmodifiers") == 0)
208        *field_rtrn = F_ModsToClear;
209    else
210        return False;
211    return True;
212}
213
214static char *
215fieldText(unsigned field)
216{
217    static char buf[32];
218
219    switch (field)
220    {
221    case F_ClearLocks:
222        strcpy(buf, "clearLocks");
223        break;
224    case F_LatchToLock:
225        strcpy(buf, "latchToLock");
226        break;
227    case F_GenKeyEvent:
228        strcpy(buf, "genKeyEvent");
229        break;
230    case F_Report:
231        strcpy(buf, "report");
232        break;
233    case F_Default:
234        strcpy(buf, "default");
235        break;
236    case F_Affect:
237        strcpy(buf, "affect");
238        break;
239    case F_Increment:
240        strcpy(buf, "increment");
241        break;
242    case F_Modifiers:
243        strcpy(buf, "modifiers");
244        break;
245    case F_Group:
246        strcpy(buf, "group");
247        break;
248    case F_X:
249        strcpy(buf, "x");
250        break;
251    case F_Y:
252        strcpy(buf, "y");
253        break;
254    case F_Accel:
255        strcpy(buf, "accel");
256        break;
257    case F_Button:
258        strcpy(buf, "button");
259        break;
260    case F_Value:
261        strcpy(buf, "value");
262        break;
263    case F_Controls:
264        strcpy(buf, "controls");
265        break;
266    case F_Type:
267        strcpy(buf, "type");
268        break;
269    case F_Count:
270        strcpy(buf, "count");
271        break;
272    case F_Screen:
273        strcpy(buf, "screen");
274        break;
275    case F_Same:
276        strcpy(buf, "sameServer");
277        break;
278    case F_Data:
279        strcpy(buf, "data");
280        break;
281    case F_Device:
282        strcpy(buf, "device");
283        break;
284    case F_Keycode:
285        strcpy(buf, "keycode");
286        break;
287    case F_ModsToClear:
288        strcpy(buf, "clearmods");
289        break;
290    default:
291        strcpy(buf, "unknown");
292        break;
293    }
294    return buf;
295}
296
297/***====================================================================***/
298
299static Bool
300ReportMismatch(unsigned action, unsigned field, const char *type)
301{
302    ERROR2("Value of %s field must be of type %s\n", fieldText(field), type);
303    ACTION1("Action %s definition ignored\n",
304            XkbActionTypeText(action, XkbMessage));
305    return False;
306}
307
308static Bool
309ReportIllegal(unsigned action, unsigned field)
310{
311    ERROR2("Field %s is not defined for an action of type %s\n",
312           fieldText(field), XkbActionTypeText(action, XkbMessage));
313    ACTION("Action definition ignored\n");
314    return False;
315}
316
317static Bool
318ReportActionNotArray(unsigned action, unsigned field)
319{
320    ERROR2("The %s field in the %s action is not an array\n",
321           fieldText(field), XkbActionTypeText(action, XkbMessage));
322    ACTION("Action definition ignored\n");
323    return False;
324}
325
326static Bool
327ReportNotFound(unsigned action, unsigned field, const char *what, char *bad)
328{
329    ERROR2("%s named %s not found\n", what, bad);
330    ACTION2("Ignoring the %s field of an %s action\n", fieldText(field),
331            XkbActionTypeText(action, XkbMessage));
332    return False;
333}
334
335static Bool
336HandleNoAction(XkbDescPtr xkb,
337               XkbAnyAction * action,
338               unsigned field, ExprDef * array_ndx, ExprDef * value)
339{
340    return ReportIllegal(action->type, field);
341}
342
343static Bool
344CheckLatchLockFlags(unsigned action,
345                    unsigned field, ExprDef * value, unsigned *flags_inout)
346{
347    unsigned tmp;
348    ExprResult result;
349
350    if (field == F_ClearLocks)
351        tmp = XkbSA_ClearLocks;
352    else if (field == F_LatchToLock)
353        tmp = XkbSA_LatchToLock;
354    else
355        return False;           /* WSGO! */
356    if (!ExprResolveBoolean(value, &result, NULL, NULL))
357        return ReportMismatch(action, field, "boolean");
358    if (result.uval)
359        *flags_inout |= tmp;
360    else
361        *flags_inout &= ~tmp;
362    return True;
363}
364
365static Bool
366CheckModifierField(XkbDescPtr xkb,
367                   unsigned action,
368                   ExprDef * value,
369                   unsigned *flags_inout, unsigned *mods_rtrn)
370{
371    ExprResult rtrn;
372
373    if (value->op == ExprIdent)
374    {
375        register char *valStr;
376        valStr = XkbAtomGetString(NULL, value->value.str);
377        if (valStr && ((uStrCaseCmp(valStr, "usemodmapmods") == 0) ||
378                       (uStrCaseCmp(valStr, "modmapmods") == 0)))
379        {
380
381            *mods_rtrn = 0;
382            *flags_inout |= XkbSA_UseModMapMods;
383            return True;
384        }
385    }
386    if (!ExprResolveModMask(value, &rtrn, LookupVModMask, (XPointer) xkb))
387        return ReportMismatch(action, F_Modifiers, "modifier mask");
388    *mods_rtrn = rtrn.uval;
389    *flags_inout &= ~XkbSA_UseModMapMods;
390    return True;
391}
392
393static Bool
394HandleSetLatchMods(XkbDescPtr xkb,
395                   XkbAnyAction * action,
396                   unsigned field, ExprDef * array_ndx, ExprDef * value)
397{
398    XkbModAction *act;
399    unsigned rtrn;
400    unsigned t1, t2;
401
402    act = (XkbModAction *) action;
403    if (array_ndx != NULL)
404    {
405        switch (field)
406        {
407        case F_ClearLocks:
408        case F_LatchToLock:
409        case F_Modifiers:
410            return ReportActionNotArray(action->type, field);
411        }
412    }
413    switch (field)
414    {
415    case F_ClearLocks:
416    case F_LatchToLock:
417        rtrn = act->flags;
418        if (CheckLatchLockFlags(action->type, field, value, &rtrn))
419        {
420            act->flags = rtrn;
421            return True;
422        }
423        return False;
424    case F_Modifiers:
425        t1 = act->flags;
426        if (CheckModifierField(xkb, action->type, value, &t1, &t2))
427        {
428            act->flags = t1;
429            act->real_mods = act->mask = (t2 & 0xff);
430            t2 = (t2 >> 8) & 0xffff;
431            XkbSetModActionVMods(act, t2);
432            return True;
433        }
434        return False;
435    }
436    return ReportIllegal(action->type, field);
437}
438
439static Bool
440HandleLockMods(XkbDescPtr xkb,
441               XkbAnyAction * action,
442               unsigned field, ExprDef * array_ndx, ExprDef * value)
443{
444    XkbModAction *act;
445    unsigned t1, t2;
446
447    act = (XkbModAction *) action;
448    if ((array_ndx != NULL) && (field == F_Modifiers))
449        return ReportActionNotArray(action->type, field);
450    switch (field)
451    {
452    case F_Modifiers:
453        t1 = act->flags;
454        if (CheckModifierField(xkb, action->type, value, &t1, &t2))
455        {
456            act->flags = t1;
457            act->real_mods = act->mask = (t2 & 0xff);
458            t2 = (t2 >> 8) & 0xffff;
459            XkbSetModActionVMods(act, t2);
460            return True;
461        }
462        return False;
463    }
464    return ReportIllegal(action->type, field);
465}
466
467static LookupEntry groupNames[] = {
468    {"group1", 1},
469    {"group2", 2},
470    {"group3", 3},
471    {"group4", 4},
472    {"group5", 5},
473    {"group6", 6},
474    {"group7", 7},
475    {"group8", 8},
476    {NULL, 0},
477};
478
479static Bool
480CheckGroupField(unsigned action,
481                ExprDef * value, unsigned *flags_inout, int *grp_rtrn)
482{
483    ExprDef *spec;
484    ExprResult rtrn;
485
486    if ((value->op == OpNegate) || (value->op == OpUnaryPlus))
487    {
488        *flags_inout &= ~XkbSA_GroupAbsolute;
489        spec = value->value.child;
490    }
491    else
492    {
493        *flags_inout |= XkbSA_GroupAbsolute;
494        spec = value;
495    }
496
497    if (!ExprResolveInteger(spec, &rtrn, SimpleLookup, (XPointer) groupNames))
498        return ReportMismatch(action, F_Group, "integer (range 1..8)");
499    if ((rtrn.ival < 1) || (rtrn.ival > XkbNumKbdGroups))
500    {
501        ERROR2("Illegal group %d (must be in the range 1..%d)\n", rtrn.ival,
502               XkbNumKbdGroups);
503        ACTION1("Action %s definition ignored\n",
504                XkbActionTypeText(action, XkbMessage));
505        return False;
506    }
507    if (value->op == OpNegate)
508        *grp_rtrn = -rtrn.ival;
509    else if (value->op == OpUnaryPlus)
510        *grp_rtrn = rtrn.ival;
511    else
512        *grp_rtrn = rtrn.ival - 1;
513    return True;
514}
515
516static Bool
517HandleSetLatchGroup(XkbDescPtr xkb,
518                    XkbAnyAction * action,
519                    unsigned field, ExprDef * array_ndx, ExprDef * value)
520{
521    XkbGroupAction *act;
522    unsigned rtrn;
523    unsigned t1;
524    int t2;
525
526    act = (XkbGroupAction *) action;
527    if (array_ndx != NULL)
528    {
529        switch (field)
530        {
531        case F_ClearLocks:
532        case F_LatchToLock:
533        case F_Group:
534            return ReportActionNotArray(action->type, field);
535        }
536    }
537    switch (field)
538    {
539    case F_ClearLocks:
540    case F_LatchToLock:
541        rtrn = act->flags;
542        if (CheckLatchLockFlags(action->type, field, value, &rtrn))
543        {
544            act->flags = rtrn;
545            return True;
546        }
547        return False;
548    case F_Group:
549        t1 = act->flags;
550        if (CheckGroupField(action->type, value, &t1, &t2))
551        {
552            act->flags = t1;
553            XkbSASetGroup(act, t2);
554            return True;
555        }
556        return False;
557    }
558    return ReportIllegal(action->type, field);
559}
560
561static Bool
562HandleLockGroup(XkbDescPtr xkb,
563                XkbAnyAction * action,
564                unsigned field, ExprDef * array_ndx, ExprDef * value)
565{
566    XkbGroupAction *act;
567    unsigned t1;
568    int t2;
569
570    act = (XkbGroupAction *) action;
571    if ((array_ndx != NULL) && (field == F_Group))
572        return ReportActionNotArray(action->type, field);
573    if (field == F_Group)
574    {
575        t1 = act->flags;
576        if (CheckGroupField(action->type, value, &t1, &t2))
577        {
578            act->flags = t1;
579            XkbSASetGroup(act, t2);
580            return True;
581        }
582        return False;
583    }
584    return ReportIllegal(action->type, field);
585}
586
587static Bool
588HandleMovePtr(XkbDescPtr xkb,
589              XkbAnyAction * action,
590              unsigned field, ExprDef * array_ndx, ExprDef * value)
591{
592    ExprResult rtrn;
593    XkbPtrAction *act;
594    Bool absolute;
595
596    act = (XkbPtrAction *) action;
597    if ((array_ndx != NULL) && ((field == F_X) || (field == F_Y)))
598        return ReportActionNotArray(action->type, field);
599
600    if ((field == F_X) || (field == F_Y))
601    {
602        if ((value->op == OpNegate) || (value->op == OpUnaryPlus))
603            absolute = False;
604        else
605            absolute = True;
606        if (!ExprResolveInteger(value, &rtrn, NULL, NULL))
607            return ReportMismatch(action->type, field, "integer");
608        if (field == F_X)
609        {
610            if (absolute)
611                act->flags |= XkbSA_MoveAbsoluteX;
612            XkbSetPtrActionX(act, rtrn.ival);
613        }
614        else
615        {
616            if (absolute)
617                act->flags |= XkbSA_MoveAbsoluteY;
618            XkbSetPtrActionY(act, rtrn.ival);
619        }
620        return True;
621    }
622    else if (field == F_Accel)
623    {
624        if (!ExprResolveBoolean(value, &rtrn, NULL, NULL))
625            return ReportMismatch(action->type, field, "boolean");
626        if (rtrn.uval)
627            act->flags &= ~XkbSA_NoAcceleration;
628        else
629            act->flags |= XkbSA_NoAcceleration;
630    }
631    return ReportIllegal(action->type, field);
632}
633
634static LookupEntry btnNames[] = {
635    {"button1", 1},
636    {"button2", 2},
637    {"button3", 3},
638    {"button4", 4},
639    {"button5", 5},
640    {"default", 0},
641    {NULL, 0}
642};
643
644static LookupEntry lockWhich[] = {
645    {"both", 0},
646    {"lock", XkbSA_LockNoUnlock},
647    {"neither", (XkbSA_LockNoLock | XkbSA_LockNoUnlock)},
648    {"unlock", XkbSA_LockNoLock},
649    {NULL, 0}
650};
651
652static Bool
653HandlePtrBtn(XkbDescPtr xkb,
654             XkbAnyAction * action,
655             unsigned field, ExprDef * array_ndx, ExprDef * value)
656{
657    ExprResult rtrn;
658    XkbPtrBtnAction *act;
659
660    act = (XkbPtrBtnAction *) action;
661    if (field == F_Button)
662    {
663        if (array_ndx != NULL)
664            return ReportActionNotArray(action->type, field);
665        if (!ExprResolveInteger
666            (value, &rtrn, SimpleLookup, (XPointer) btnNames))
667            return ReportMismatch(action->type, field,
668                                  "integer (range 1..5)");
669        if ((rtrn.ival < 0) || (rtrn.ival > 5))
670        {
671            ERROR("Button must specify default or be in the range 1..5\n");
672            ACTION1("Illegal button value %d ignored\n", rtrn.ival);
673            return False;
674        }
675        act->button = rtrn.ival;
676        return True;
677    }
678    else if ((action->type == XkbSA_LockPtrBtn) && (field == F_Affect))
679    {
680        if (array_ndx != NULL)
681            return ReportActionNotArray(action->type, field);
682        if (!ExprResolveEnum(value, &rtrn, lockWhich))
683            return ReportMismatch(action->type, field, "lock or unlock");
684        act->flags &= ~(XkbSA_LockNoLock | XkbSA_LockNoUnlock);
685        act->flags |= rtrn.ival;
686        return True;
687    }
688    else if (field == F_Count)
689    {
690        if (array_ndx != NULL)
691            return ReportActionNotArray(action->type, field);
692        if (!ExprResolveInteger
693            (value, &rtrn, SimpleLookup, (XPointer) btnNames))
694            return ReportMismatch(action->type, field, "integer");
695        if ((rtrn.ival < 0) || (rtrn.ival > 255))
696        {
697            ERROR("The count field must have a value in the range 0..255\n");
698            ACTION1("Illegal count %d ignored\n", rtrn.ival);
699            return False;
700        }
701        act->count = rtrn.ival;
702        return True;
703    }
704    return ReportIllegal(action->type, field);
705}
706
707static LookupEntry ptrDflts[] = {
708    {"dfltbtn", XkbSA_AffectDfltBtn},
709    {"defaultbutton", XkbSA_AffectDfltBtn},
710    {"button", XkbSA_AffectDfltBtn},
711    {NULL, 0}
712};
713
714static Bool
715HandleSetPtrDflt(XkbDescPtr xkb,
716                 XkbAnyAction * action,
717                 unsigned field, ExprDef * array_ndx, ExprDef * value)
718{
719    ExprResult rtrn;
720    XkbPtrDfltAction *act;
721
722    act = (XkbPtrDfltAction *) action;
723    if (field == F_Affect)
724    {
725        if (array_ndx != NULL)
726            return ReportActionNotArray(action->type, field);
727        if (!ExprResolveEnum(value, &rtrn, ptrDflts))
728            return ReportMismatch(action->type, field, "pointer component");
729        act->affect = rtrn.uval;
730        return True;
731    }
732    else if ((field == F_Button) || (field == F_Value))
733    {
734        ExprDef *btn;
735        if (array_ndx != NULL)
736            return ReportActionNotArray(action->type, field);
737        if ((value->op == OpNegate) || (value->op == OpUnaryPlus))
738        {
739            act->flags &= ~XkbSA_DfltBtnAbsolute;
740            btn = value->value.child;
741        }
742        else
743        {
744            act->flags |= XkbSA_DfltBtnAbsolute;
745            btn = value;
746        }
747
748        if (!ExprResolveInteger
749            (btn, &rtrn, SimpleLookup, (XPointer) btnNames))
750            return ReportMismatch(action->type, field,
751                                  "integer (range 1..5)");
752        if ((rtrn.ival < 0) || (rtrn.ival > 5))
753        {
754            ERROR("New default button value must be in the range 1..5\n");
755            ACTION1("Illegal default button value %d ignored\n", rtrn.ival);
756            return False;
757        }
758        if (rtrn.ival == 0)
759        {
760            ERROR("Cannot set default pointer button to \"default\"\n");
761            ACTION("Illegal default button setting ignored\n");
762            return False;
763        }
764        if (value->op == OpNegate)
765            XkbSASetPtrDfltValue(act, -rtrn.ival);
766        else
767            XkbSASetPtrDfltValue(act, rtrn.ival);
768        return True;
769    }
770    return ReportIllegal(action->type, field);
771}
772
773static LookupEntry isoNames[] = {
774    {"mods", XkbSA_ISONoAffectMods},
775    {"modifiers", XkbSA_ISONoAffectMods},
776    {"group", XkbSA_ISONoAffectGroup},
777    {"groups", XkbSA_ISONoAffectGroup},
778    {"ptr", XkbSA_ISONoAffectPtr},
779    {"pointer", XkbSA_ISONoAffectPtr},
780    {"ctrls", XkbSA_ISONoAffectCtrls},
781    {"controls", XkbSA_ISONoAffectCtrls},
782    {"all", ~((unsigned) 0)},
783    {"none", 0},
784    {NULL, 0},
785};
786
787static Bool
788HandleISOLock(XkbDescPtr xkb,
789              XkbAnyAction * action,
790              unsigned field, ExprDef * array_ndx, ExprDef * value)
791{
792    ExprResult rtrn;
793    XkbISOAction *act;
794    unsigned flags, mods;
795    int group;
796
797    act = (XkbISOAction *) action;
798    switch (field)
799    {
800    case F_Modifiers:
801        if (array_ndx != NULL)
802            return ReportActionNotArray(action->type, field);
803        flags = act->flags;
804        if (CheckModifierField(xkb, action->type, value, &flags, &mods))
805        {
806            act->flags = flags & (~XkbSA_ISODfltIsGroup);
807            act->real_mods = mods & 0xff;
808            mods = (mods >> 8) & 0xff;
809            XkbSetModActionVMods(act, mods);
810            return True;
811        }
812        return False;
813    case F_Group:
814        if (array_ndx != NULL)
815            return ReportActionNotArray(action->type, field);
816        flags = act->flags;
817        if (CheckGroupField(action->type, value, &flags, &group))
818        {
819            act->flags = flags | XkbSA_ISODfltIsGroup;
820            XkbSASetGroup(act, group);
821            return True;
822        }
823        return False;
824    case F_Affect:
825        if (array_ndx != NULL)
826            return ReportActionNotArray(action->type, field);
827        if (!ExprResolveMask(value, &rtrn, SimpleLookup, (XPointer) isoNames))
828            return ReportMismatch(action->type, field, "keyboard component");
829        act->affect = (~rtrn.uval) & XkbSA_ISOAffectMask;
830        return True;
831    }
832    return ReportIllegal(action->type, field);
833}
834
835static Bool
836HandleSwitchScreen(XkbDescPtr xkb,
837                   XkbAnyAction * action,
838                   unsigned field, ExprDef * array_ndx, ExprDef * value)
839{
840    ExprResult rtrn;
841    XkbSwitchScreenAction *act;
842
843    act = (XkbSwitchScreenAction *) action;
844    if (field == F_Screen)
845    {
846        ExprDef *scrn;
847        if (array_ndx != NULL)
848            return ReportActionNotArray(action->type, field);
849        if ((value->op == OpNegate) || (value->op == OpUnaryPlus))
850        {
851            act->flags &= ~XkbSA_SwitchAbsolute;
852            scrn = value->value.child;
853        }
854        else
855        {
856            act->flags |= XkbSA_SwitchAbsolute;
857            scrn = value;
858        }
859
860        if (!ExprResolveInteger(scrn, &rtrn, NULL, NULL))
861            return ReportMismatch(action->type, field, "integer (0..255)");
862        if ((rtrn.ival < 0) || (rtrn.ival > 255))
863        {
864            ERROR("Screen index must be in the range 1..255\n");
865            ACTION1("Illegal screen value %d ignored\n", rtrn.ival);
866            return False;
867        }
868        if (value->op == OpNegate)
869            XkbSASetScreen(act, -rtrn.ival);
870        else
871            XkbSASetScreen(act, rtrn.ival);
872        return True;
873    }
874    else if (field == F_Same)
875    {
876        if (array_ndx != NULL)
877            return ReportActionNotArray(action->type, field);
878        if (!ExprResolveBoolean(value, &rtrn, NULL, NULL))
879            return ReportMismatch(action->type, field, "boolean");
880        if (rtrn.uval)
881            act->flags &= ~XkbSA_SwitchApplication;
882        else
883            act->flags |= XkbSA_SwitchApplication;
884        return True;
885    }
886    return ReportIllegal(action->type, field);
887}
888
889LookupEntry ctrlNames[] = {
890    {"repeatkeys", XkbRepeatKeysMask}
891    ,
892    {"repeat", XkbRepeatKeysMask}
893    ,
894    {"autorepeat", XkbRepeatKeysMask}
895    ,
896    {"slowkeys", XkbSlowKeysMask}
897    ,
898    {"bouncekeys", XkbBounceKeysMask}
899    ,
900    {"stickykeys", XkbStickyKeysMask}
901    ,
902    {"mousekeys", XkbMouseKeysMask}
903    ,
904    {"mousekeysaccel", XkbMouseKeysAccelMask}
905    ,
906    {"accessxkeys", XkbAccessXKeysMask}
907    ,
908    {"accessxtimeout", XkbAccessXTimeoutMask}
909    ,
910    {"accessxfeedback", XkbAccessXFeedbackMask}
911    ,
912    {"audiblebell", XkbAudibleBellMask}
913    ,
914    {"overlay1", XkbOverlay1Mask}
915    ,
916    {"overlay2", XkbOverlay2Mask}
917    ,
918    {"ignoregrouplock", XkbIgnoreGroupLockMask}
919    ,
920    {"all", XkbAllBooleanCtrlsMask}
921    ,
922    {"none", 0}
923    ,
924    {NULL, 0}
925};
926
927static Bool
928HandleSetLockControls(XkbDescPtr xkb,
929                      XkbAnyAction * action,
930                      unsigned field, ExprDef * array_ndx, ExprDef * value)
931{
932    ExprResult rtrn;
933    XkbCtrlsAction *act;
934
935    act = (XkbCtrlsAction *) action;
936    if (field == F_Controls)
937    {
938        if (array_ndx != NULL)
939            return ReportActionNotArray(action->type, field);
940        if (!ExprResolveMask
941            (value, &rtrn, SimpleLookup, (XPointer) ctrlNames))
942            return ReportMismatch(action->type, field, "controls mask");
943        XkbActionSetCtrls(act, rtrn.uval);
944        return True;
945    }
946    return ReportIllegal(action->type, field);
947}
948
949static LookupEntry evNames[] = {
950    {"press", XkbSA_MessageOnPress},
951    {"keypress", XkbSA_MessageOnPress},
952    {"release", XkbSA_MessageOnRelease},
953    {"keyrelease", XkbSA_MessageOnRelease},
954    {"all", XkbSA_MessageOnPress | XkbSA_MessageOnRelease},
955    {"none", 0},
956    {NULL, 0}
957};
958
959static Bool
960HandleActionMessage(XkbDescPtr xkb,
961                    XkbAnyAction * action,
962                    unsigned field, ExprDef * array_ndx, ExprDef * value)
963{
964    ExprResult rtrn;
965    XkbMessageAction *act;
966
967    act = (XkbMessageAction *) action;
968    switch (field)
969    {
970    case F_Report:
971        if (array_ndx != NULL)
972            return ReportActionNotArray(action->type, field);
973        if (!ExprResolveMask(value, &rtrn, SimpleLookup, (XPointer) evNames))
974            return ReportMismatch(action->type, field, "key event mask");
975        act->flags &= ~(XkbSA_MessageOnPress | XkbSA_MessageOnRelease);
976        act->flags =
977            rtrn.uval & (XkbSA_MessageOnPress | XkbSA_MessageOnRelease);
978        return True;
979    case F_GenKeyEvent:
980        if (array_ndx != NULL)
981            return ReportActionNotArray(action->type, field);
982        if (!ExprResolveBoolean(value, &rtrn, NULL, NULL))
983            return ReportMismatch(action->type, field, "boolean");
984        if (rtrn.uval)
985            act->flags |= XkbSA_MessageGenKeyEvent;
986        else
987            act->flags &= ~XkbSA_MessageGenKeyEvent;
988        return True;
989    case F_Data:
990        if (array_ndx == NULL)
991        {
992            if (!ExprResolveString(value, &rtrn, NULL, NULL))
993                return ReportMismatch(action->type, field, "string");
994            else
995            {
996                int len = strlen(rtrn.str);
997                if ((len < 1) || (len > 6))
998                {
999                    WARN("An action message can hold only 6 bytes\n");
1000                    ACTION1("Extra %d bytes ignored\n", len - 6);
1001                }
1002                strncpy((char *) act->message, rtrn.str, 6);
1003            }
1004            return True;
1005        }
1006        else
1007        {
1008            unsigned ndx;
1009            if (!ExprResolveInteger(array_ndx, &rtrn, NULL, NULL))
1010            {
1011                ERROR("Array subscript must be integer\n");
1012                ACTION("Illegal subscript ignored\n");
1013                return False;
1014            }
1015            ndx = rtrn.uval;
1016            if (ndx > 5)
1017            {
1018                ERROR("An action message is at most 6 bytes long\n");
1019                ACTION1("Attempt to use data[%d] ignored\n", ndx);
1020                return False;
1021            }
1022            if (!ExprResolveInteger(value, &rtrn, NULL, NULL))
1023                return ReportMismatch(action->type, field, "integer");
1024            if ((rtrn.ival < 0) || (rtrn.ival > 255))
1025            {
1026                ERROR("Message data must be in the range 0..255\n");
1027                ACTION1("Illegal datum %d ignored\n", rtrn.ival);
1028                return False;
1029            }
1030            act->message[ndx] = rtrn.uval;
1031        }
1032        return True;
1033    }
1034    return ReportIllegal(action->type, field);
1035}
1036
1037static Bool
1038HandleRedirectKey(XkbDescPtr xkb,
1039                  XkbAnyAction * action,
1040                  unsigned field, ExprDef * array_ndx, ExprDef * value)
1041{
1042    ExprResult rtrn;
1043    XkbRedirectKeyAction *act;
1044    unsigned t1, t2, vmods, vmask;
1045    unsigned long tmp;
1046
1047    if (array_ndx != NULL)
1048        return ReportActionNotArray(action->type, field);
1049
1050    act = (XkbRedirectKeyAction *) action;
1051    switch (field)
1052    {
1053    case F_Keycode:
1054        if (!ExprResolveKeyName(value, &rtrn, NULL, NULL))
1055            return ReportMismatch(action->type, field, "key name");
1056        tmp = KeyNameToLong(rtrn.keyName.name);
1057        if (!FindNamedKey(xkb, tmp, &t1, True, CreateKeyNames(xkb), 0))
1058        {
1059            return ReportNotFound(action->type, field, "Key",
1060                                  XkbKeyNameText(rtrn.keyName.name,
1061                                                 XkbMessage));
1062        }
1063        act->new_key = t1;
1064        return True;
1065    case F_ModsToClear:
1066    case F_Modifiers:
1067        t1 = 0;
1068        if (CheckModifierField(xkb, action->type, value, &t1, &t2))
1069        {
1070            act->mods_mask |= (t2 & 0xff);
1071            if (field == F_Modifiers)
1072                act->mods |= (t2 & 0xff);
1073            else
1074                act->mods &= ~(t2 & 0xff);
1075
1076            t2 = (t2 >> 8) & 0xffff;
1077            vmods = XkbSARedirectVMods(act);
1078            vmask = XkbSARedirectVModsMask(act);
1079            vmask |= t2;
1080            if (field == F_Modifiers)
1081                vmods |= t2;
1082            else
1083                vmods &= ~t2;
1084            XkbSARedirectSetVMods(act, vmods);
1085            XkbSARedirectSetVModsMask(act, vmask);
1086            return True;
1087        }
1088        return True;
1089    }
1090    return ReportIllegal(action->type, field);
1091}
1092
1093static Bool
1094HandleDeviceBtn(XkbDescPtr xkb,
1095                XkbAnyAction * action,
1096                unsigned field, ExprDef * array_ndx, ExprDef * value)
1097{
1098    ExprResult rtrn;
1099    XkbDeviceBtnAction *act;
1100
1101    act = (XkbDeviceBtnAction *) action;
1102    if (field == F_Button)
1103    {
1104        if (array_ndx != NULL)
1105            return ReportActionNotArray(action->type, field);
1106        if (!ExprResolveInteger(value, &rtrn, NULL, NULL))
1107            return ReportMismatch(action->type, field,
1108                                  "integer (range 1..255)");
1109        if ((rtrn.ival < 0) || (rtrn.ival > 255))
1110        {
1111            ERROR("Button must specify default or be in the range 1..255\n");
1112            ACTION1("Illegal button value %d ignored\n", rtrn.ival);
1113            return False;
1114        }
1115        act->button = rtrn.ival;
1116        return True;
1117    }
1118    else if ((action->type == XkbSA_LockDeviceBtn) && (field == F_Affect))
1119    {
1120        if (array_ndx != NULL)
1121            return ReportActionNotArray(action->type, field);
1122        if (!ExprResolveEnum(value, &rtrn, lockWhich))
1123            return ReportMismatch(action->type, field, "lock or unlock");
1124        act->flags &= ~(XkbSA_LockNoLock | XkbSA_LockNoUnlock);
1125        act->flags |= rtrn.ival;
1126        return True;
1127    }
1128    else if (field == F_Count)
1129    {
1130        if (array_ndx != NULL)
1131            return ReportActionNotArray(action->type, field);
1132        if (!ExprResolveInteger
1133            (value, &rtrn, SimpleLookup, (XPointer) btnNames))
1134            return ReportMismatch(action->type, field, "integer");
1135        if ((rtrn.ival < 0) || (rtrn.ival > 255))
1136        {
1137            ERROR("The count field must have a value in the range 0..255\n");
1138            ACTION1("Illegal count %d ignored\n", rtrn.ival);
1139            return False;
1140        }
1141        act->count = rtrn.ival;
1142        return True;
1143    }
1144    else if (field == F_Device)
1145    {
1146        if (array_ndx != NULL)
1147            return ReportActionNotArray(action->type, field);
1148        if (!ExprResolveInteger(value, &rtrn, NULL, NULL))
1149            return ReportMismatch(action->type, field,
1150                                  "integer (range 1..255)");
1151        if ((rtrn.ival < 0) || (rtrn.ival > 255))
1152        {
1153            ERROR("Device must specify default or be in the range 1..255\n");
1154            ACTION1("Illegal device value %d ignored\n", rtrn.ival);
1155            return False;
1156        }
1157        act->device = rtrn.ival;
1158        return True;
1159    }
1160    return ReportIllegal(action->type, field);
1161}
1162
1163static Bool
1164HandleDeviceValuator(XkbDescPtr xkb,
1165                     XkbAnyAction * action,
1166                     unsigned field, ExprDef * array_ndx, ExprDef * value)
1167{
1168#if 0
1169    ExprResult rtrn;
1170    XkbDeviceValuatorAction *act;
1171
1172    act = (XkbDeviceValuatorAction *) action;
1173    /*  XXX - Not yet implemented */
1174#endif
1175    return False;
1176}
1177
1178static Bool
1179HandlePrivate(XkbDescPtr xkb,
1180              XkbAnyAction * action,
1181              unsigned field, ExprDef * array_ndx, ExprDef * value)
1182{
1183    ExprResult rtrn;
1184
1185    switch (field)
1186    {
1187    case F_Type:
1188        if (!ExprResolveInteger(value, &rtrn, NULL, NULL))
1189            return ReportMismatch(PrivateAction, field, "integer");
1190        if ((rtrn.ival < 0) || (rtrn.ival > 255))
1191        {
1192            ERROR("Private action type must be in the range 0..255\n");
1193            ACTION1("Illegal type %d ignored\n", rtrn.ival);
1194            return False;
1195        }
1196        action->type = rtrn.uval;
1197        return True;
1198    case F_Data:
1199        if (array_ndx == NULL)
1200        {
1201            if (!ExprResolveString(value, &rtrn, NULL, NULL))
1202                return ReportMismatch(action->type, field, "string");
1203            else
1204            {
1205                int len = strlen(rtrn.str);
1206                if ((len < 1) || (len > 7))
1207                {
1208                    WARN("A private action has 7 data bytes\n");
1209                    ACTION1("Extra %d bytes ignored\n", len - 6);
1210                    return False;
1211                }
1212                strncpy((char *) action->data, rtrn.str, 7);
1213            }
1214            return True;
1215        }
1216        else
1217        {
1218            unsigned ndx;
1219            if (!ExprResolveInteger(array_ndx, &rtrn, NULL, NULL))
1220            {
1221                ERROR("Array subscript must be integer\n");
1222                ACTION("Illegal subscript ignored\n");
1223                return False;
1224            }
1225            ndx = rtrn.uval;
1226            if (ndx > 6)
1227            {
1228                ERROR("The data for a private action is 7 bytes long\n");
1229                ACTION1("Attempt to use data[%d] ignored\n", ndx);
1230                return False;
1231            }
1232            if (!ExprResolveInteger(value, &rtrn, NULL, NULL))
1233                return ReportMismatch(action->type, field, "integer");
1234            if ((rtrn.ival < 0) || (rtrn.ival > 255))
1235            {
1236                ERROR("All data for a private action must be 0..255\n");
1237                ACTION1("Illegal datum %d ignored\n", rtrn.ival);
1238                return False;
1239            }
1240            action->data[ndx] = rtrn.uval;
1241            return True;
1242        }
1243    }
1244    return ReportIllegal(PrivateAction, field);
1245}
1246
1247typedef Bool(*actionHandler) (XkbDescPtr /* xkb */ ,
1248                              XkbAnyAction * /* action */ ,
1249                              unsigned /* field */ ,
1250                              ExprDef * /* array_ndx */ ,
1251                              ExprDef * /* value */
1252    );
1253
1254static actionHandler handleAction[XkbSA_NumActions + 1] = {
1255    HandleNoAction /* NoAction     */ ,
1256    HandleSetLatchMods /* SetMods      */ ,
1257    HandleSetLatchMods /* LatchMods    */ ,
1258    HandleLockMods /* LockMods     */ ,
1259    HandleSetLatchGroup /* SetGroup     */ ,
1260    HandleSetLatchGroup /* LatchGroup   */ ,
1261    HandleLockGroup /* LockGroup    */ ,
1262    HandleMovePtr /* MovePtr      */ ,
1263    HandlePtrBtn /* PtrBtn       */ ,
1264    HandlePtrBtn /* LockPtrBtn   */ ,
1265    HandleSetPtrDflt /* SetPtrDflt   */ ,
1266    HandleISOLock /* ISOLock      */ ,
1267    HandleNoAction /* Terminate    */ ,
1268    HandleSwitchScreen /* SwitchScreen */ ,
1269    HandleSetLockControls /* SetControls  */ ,
1270    HandleSetLockControls /* LockControls */ ,
1271    HandleActionMessage /* ActionMessage */ ,
1272    HandleRedirectKey /* RedirectKey  */ ,
1273    HandleDeviceBtn /* DeviceBtn    */ ,
1274    HandleDeviceBtn /* LockDeviceBtn */ ,
1275    HandleDeviceValuator /* DeviceValuatr */ ,
1276    HandlePrivate               /* Private      */
1277};
1278
1279/***====================================================================***/
1280
1281static void
1282ApplyActionFactoryDefaults(XkbAction * action)
1283{
1284    if (action->type == XkbSA_SetPtrDflt)
1285    {                           /* increment default button */
1286        action->dflt.affect = XkbSA_AffectDfltBtn;
1287        action->dflt.flags = 0;
1288        XkbSASetPtrDfltValue(&action->dflt, 1);
1289    }
1290    else if (action->type == XkbSA_ISOLock)
1291    {
1292        action->iso.real_mods = LockMask;
1293    }
1294    return;
1295}
1296
1297
1298int
1299HandleActionDef(ExprDef * def,
1300                XkbDescPtr xkb,
1301                XkbAnyAction * action, unsigned mergeMode, ActionInfo * info)
1302{
1303    ExprDef *arg;
1304    register char *str;
1305    unsigned tmp, hndlrType;
1306
1307    if (!actionsInitialized)
1308        ActionsInit();
1309
1310    if (def->op != ExprActionDecl)
1311    {
1312        ERROR1("Expected an action definition, found %s\n",
1313               exprOpText(def->op));
1314        return False;
1315    }
1316    str = XkbAtomGetString(NULL, def->value.action.name);
1317    if (!str)
1318    {
1319        WSGO("Missing name in action definition!!\n");
1320        return False;
1321    }
1322    if (!stringToAction(str, &tmp))
1323    {
1324        ERROR1("Unknown action %s\n", str);
1325        return False;
1326    }
1327    action->type = hndlrType = tmp;
1328    if (action->type != XkbSA_NoAction)
1329    {
1330        ApplyActionFactoryDefaults((XkbAction *) action);
1331        while (info)
1332        {
1333            if ((info->action == XkbSA_NoAction)
1334                || (info->action == hndlrType))
1335            {
1336                if (!(*handleAction[hndlrType]) (xkb, action,
1337                                                 info->field,
1338                                                 info->array_ndx,
1339                                                 info->value))
1340                {
1341                    return False;
1342                }
1343            }
1344            info = info->next;
1345        }
1346    }
1347    for (arg = def->value.action.args; arg != NULL;
1348         arg = (ExprDef *) arg->common.next)
1349    {
1350        ExprDef *field, *value, *arrayRtrn;
1351        ExprResult elemRtrn, fieldRtrn;
1352        unsigned fieldNdx;
1353
1354        if (arg->op == OpAssign)
1355        {
1356            field = arg->value.binary.left;
1357            value = arg->value.binary.right;
1358        }
1359        else
1360        {
1361            if ((arg->op == OpNot) || (arg->op == OpInvert))
1362            {
1363                field = arg->value.child;
1364                value = &constFalse;
1365            }
1366            else
1367            {
1368                field = arg;
1369                value = &constTrue;
1370            }
1371        }
1372        if (!ExprResolveLhs(field, &elemRtrn, &fieldRtrn, &arrayRtrn))
1373            return False;       /* internal error -- already reported */
1374
1375        if (elemRtrn.str != NULL)
1376        {
1377            ERROR("Cannot change defaults in an action definition\n");
1378            ACTION2("Ignoring attempt to change %s.%s\n", elemRtrn.str,
1379                    fieldRtrn.str);
1380            return False;
1381        }
1382        if (!stringToField(fieldRtrn.str, &fieldNdx))
1383        {
1384            ERROR1("Unknown field name %s\n", uStringText(fieldRtrn.str));
1385            return False;
1386        }
1387        if (!(*handleAction[hndlrType])
1388            (xkb, action, fieldNdx, arrayRtrn, value))
1389        {
1390            return False;
1391        }
1392    }
1393    return True;
1394}
1395
1396/***====================================================================***/
1397
1398int
1399SetActionField(XkbDescPtr xkb,
1400               const char *elem,
1401               const char *field,
1402               ExprDef * array_ndx, ExprDef * value, ActionInfo ** info_rtrn)
1403{
1404    ActionInfo *new, *old;
1405
1406    if (!actionsInitialized)
1407        ActionsInit();
1408
1409    new = uTypedAlloc(ActionInfo);
1410    if (new == NULL)
1411    {
1412        WSGO("Couldn't allocate space for action default\n");
1413        return False;
1414    }
1415    if (uStrCaseCmp(elem, "action") == 0)
1416        new->action = XkbSA_NoAction;
1417    else
1418    {
1419        if (!stringToAction(elem, &new->action))
1420            return False;
1421        if (new->action == XkbSA_NoAction)
1422        {
1423            ERROR1("\"%s\" is not a valid field in a NoAction action\n",
1424                   field);
1425            return False;
1426        }
1427    }
1428    if (!stringToField(field, &new->field))
1429    {
1430        ERROR1("\"%s\" is not a legal field name\n", field);
1431        return False;
1432    }
1433    new->array_ndx = array_ndx;
1434    new->value = value;
1435    new->next = NULL;
1436    old = *info_rtrn;
1437    while ((old) && (old->next))
1438        old = old->next;
1439    if (old == NULL)
1440        *info_rtrn = new;
1441    else
1442        old->next = new;
1443    return True;
1444}
1445
1446/***====================================================================***/
1447
1448void
1449ActionsInit(void)
1450{
1451    if (!actionsInitialized)
1452    {
1453        bzero((char *) &constTrue, sizeof(constTrue));
1454        bzero((char *) &constFalse, sizeof(constFalse));
1455        constTrue.common.stmtType = StmtExpr;
1456        constTrue.common.next = NULL;
1457        constTrue.op = ExprIdent;
1458        constTrue.type = TypeBoolean;
1459        constTrue.value.str = XkbInternAtom(NULL, "true", False);
1460        constFalse.common.stmtType = StmtExpr;
1461        constFalse.common.next = NULL;
1462        constFalse.op = ExprIdent;
1463        constFalse.type = TypeBoolean;
1464        constFalse.value.str = XkbInternAtom(NULL, "false", False);
1465        actionsInitialized = 1;
1466    }
1467    return;
1468}
1469