Home | History | Annotate | Download | only in xkbcomp
      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 /*
     28  * Copyright  2012 Intel Corporation
     29  * Copyright  2012 Ran Benita <ran234 (at) gmail.com>
     30  *
     31  * Permission is hereby granted, free of charge, to any person obtaining a
     32  * copy of this software and associated documentation files (the "Software"),
     33  * to deal in the Software without restriction, including without limitation
     34  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
     35  * and/or sell copies of the Software, and to permit persons to whom the
     36  * Software is furnished to do so, subject to the following conditions:
     37  *
     38  * The above copyright notice and this permission notice (including the next
     39  * paragraph) shall be included in all copies or substantial portions of the
     40  * Software.
     41  *
     42  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     43  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     44  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
     45  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     46  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
     47  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
     48  * DEALINGS IN THE SOFTWARE.
     49  *
     50  * Author: Daniel Stone <daniel (at) fooishbar.org>
     51  *         Ran Benita <ran234 (at) gmail.com>
     52  */
     53 
     54 #include "xkbcomp-priv.h"
     55 #include "text.h"
     56 #include "expr.h"
     57 #include "action.h"
     58 
     59 static const ExprBoolean constTrue = {
     60     .expr = {
     61         .common = { .type = STMT_EXPR, .next = NULL },
     62         .op = EXPR_VALUE,
     63         .value_type = EXPR_TYPE_BOOLEAN,
     64     },
     65     .set = true,
     66 };
     67 
     68 static const ExprBoolean constFalse = {
     69     .expr = {
     70         .common = { .type = STMT_EXPR, .next = NULL },
     71         .op = EXPR_VALUE,
     72         .value_type = EXPR_TYPE_BOOLEAN,
     73     },
     74     .set = false,
     75 };
     76 
     77 enum action_field {
     78     ACTION_FIELD_CLEAR_LOCKS,
     79     ACTION_FIELD_LATCH_TO_LOCK,
     80     ACTION_FIELD_GEN_KEY_EVENT,
     81     ACTION_FIELD_REPORT,
     82     ACTION_FIELD_DEFAULT,
     83     ACTION_FIELD_AFFECT,
     84     ACTION_FIELD_INCREMENT,
     85     ACTION_FIELD_MODIFIERS,
     86     ACTION_FIELD_GROUP,
     87     ACTION_FIELD_X,
     88     ACTION_FIELD_Y,
     89     ACTION_FIELD_ACCEL,
     90     ACTION_FIELD_BUTTON,
     91     ACTION_FIELD_VALUE,
     92     ACTION_FIELD_CONTROLS,
     93     ACTION_FIELD_TYPE,
     94     ACTION_FIELD_COUNT,
     95     ACTION_FIELD_SCREEN,
     96     ACTION_FIELD_SAME,
     97     ACTION_FIELD_DATA,
     98     ACTION_FIELD_DEVICE,
     99     ACTION_FIELD_KEYCODE,
    100     ACTION_FIELD_MODS_TO_CLEAR,
    101 };
    102 
    103 ActionsInfo *
    104 NewActionsInfo(void)
    105 {
    106     enum xkb_action_type type;
    107     ActionsInfo *info;
    108 
    109     info = calloc(1, sizeof(*info));
    110     if (!info)
    111         return NULL;
    112 
    113     for (type = 0; type < _ACTION_TYPE_NUM_ENTRIES; type++)
    114         info->actions[type].type = type;
    115 
    116     /* Apply some "factory defaults". */
    117 
    118     /* Increment default button. */
    119     info->actions[ACTION_TYPE_PTR_DEFAULT].dflt.flags = 0;
    120     info->actions[ACTION_TYPE_PTR_DEFAULT].dflt.value = 1;
    121     info->actions[ACTION_TYPE_PTR_MOVE].ptr.flags = ACTION_ACCEL;
    122     info->actions[ACTION_TYPE_SWITCH_VT].screen.flags = ACTION_SAME_SCREEN;
    123 
    124     return info;
    125 }
    126 
    127 void
    128 FreeActionsInfo(ActionsInfo *info)
    129 {
    130     free(info);
    131 }
    132 
    133 static const LookupEntry fieldStrings[] = {
    134     { "clearLocks",       ACTION_FIELD_CLEAR_LOCKS   },
    135     { "latchToLock",      ACTION_FIELD_LATCH_TO_LOCK },
    136     { "genKeyEvent",      ACTION_FIELD_GEN_KEY_EVENT },
    137     { "generateKeyEvent", ACTION_FIELD_GEN_KEY_EVENT },
    138     { "report",           ACTION_FIELD_REPORT        },
    139     { "default",          ACTION_FIELD_DEFAULT       },
    140     { "affect",           ACTION_FIELD_AFFECT        },
    141     { "increment",        ACTION_FIELD_INCREMENT     },
    142     { "modifiers",        ACTION_FIELD_MODIFIERS     },
    143     { "mods",             ACTION_FIELD_MODIFIERS     },
    144     { "group",            ACTION_FIELD_GROUP         },
    145     { "x",                ACTION_FIELD_X             },
    146     { "y",                ACTION_FIELD_Y             },
    147     { "accel",            ACTION_FIELD_ACCEL         },
    148     { "accelerate",       ACTION_FIELD_ACCEL         },
    149     { "repeat",           ACTION_FIELD_ACCEL         },
    150     { "button",           ACTION_FIELD_BUTTON        },
    151     { "value",            ACTION_FIELD_VALUE         },
    152     { "controls",         ACTION_FIELD_CONTROLS      },
    153     { "ctrls",            ACTION_FIELD_CONTROLS      },
    154     { "type",             ACTION_FIELD_TYPE          },
    155     { "count",            ACTION_FIELD_COUNT         },
    156     { "screen",           ACTION_FIELD_SCREEN        },
    157     { "same",             ACTION_FIELD_SAME          },
    158     { "sameServer",       ACTION_FIELD_SAME          },
    159     { "data",             ACTION_FIELD_DATA          },
    160     { "device",           ACTION_FIELD_DEVICE        },
    161     { "dev",              ACTION_FIELD_DEVICE        },
    162     { "key",              ACTION_FIELD_KEYCODE       },
    163     { "keycode",          ACTION_FIELD_KEYCODE       },
    164     { "kc",               ACTION_FIELD_KEYCODE       },
    165     { "clearmods",        ACTION_FIELD_MODS_TO_CLEAR },
    166     { "clearmodifiers",   ACTION_FIELD_MODS_TO_CLEAR },
    167     { NULL,               0                          }
    168 };
    169 
    170 static bool
    171 stringToAction(const char *str, enum xkb_action_type *type_rtrn)
    172 {
    173     return LookupString(actionTypeNames, str, type_rtrn);
    174 }
    175 
    176 static bool
    177 stringToField(const char *str, enum action_field *field_rtrn)
    178 {
    179     return LookupString(fieldStrings, str, field_rtrn);
    180 }
    181 
    182 static const char *
    183 fieldText(enum action_field field)
    184 {
    185     return LookupValue(fieldStrings, field);
    186 }
    187 
    188 /***====================================================================***/
    189 
    190 static inline bool
    191 ReportMismatch(struct xkb_context *ctx, enum xkb_action_type action,
    192                enum action_field field, const char *type)
    193 {
    194     log_err(ctx,
    195             "Value of %s field must be of type %s; "
    196             "Action %s definition ignored\n",
    197             fieldText(field), type, ActionTypeText(action));
    198     return false;
    199 }
    200 
    201 static inline bool
    202 ReportIllegal(struct xkb_context *ctx, enum xkb_action_type action,
    203               enum action_field field)
    204 {
    205     log_err(ctx,
    206             "Field %s is not defined for an action of type %s; "
    207             "Action definition ignored\n",
    208             fieldText(field), ActionTypeText(action));
    209     return false;
    210 }
    211 
    212 static inline bool
    213 ReportActionNotArray(struct xkb_context *ctx, enum xkb_action_type action,
    214                      enum action_field field)
    215 {
    216     log_err(ctx,
    217             "The %s field in the %s action is not an array; "
    218             "Action definition ignored\n",
    219             fieldText(field), ActionTypeText(action));
    220     return false;
    221 }
    222 
    223 static bool
    224 HandleNoAction(struct xkb_context *ctx, const struct xkb_mod_set *mods,
    225                union xkb_action *action, enum action_field field,
    226                const ExprDef *array_ndx, const ExprDef *value)
    227 
    228 {
    229     return true;
    230 }
    231 
    232 static bool
    233 CheckBooleanFlag(struct xkb_context *ctx, enum xkb_action_type action,
    234                  enum action_field field, enum xkb_action_flags flag,
    235                  const ExprDef *array_ndx, const ExprDef *value,
    236                  enum xkb_action_flags *flags_inout)
    237 {
    238     bool set;
    239 
    240     if (array_ndx)
    241         return ReportActionNotArray(ctx, action, field);
    242 
    243     if (!ExprResolveBoolean(ctx, value, &set))
    244         return ReportMismatch(ctx, action, field, "boolean");
    245 
    246     if (set)
    247         *flags_inout |= flag;
    248     else
    249         *flags_inout &= ~flag;
    250 
    251     return true;
    252 }
    253 
    254 static bool
    255 CheckModifierField(struct xkb_context *ctx, const struct xkb_mod_set *mods,
    256                    enum xkb_action_type action, const ExprDef *array_ndx,
    257                    const ExprDef *value, enum xkb_action_flags *flags_inout,
    258                    xkb_mod_mask_t *mods_rtrn)
    259 {
    260     if (array_ndx)
    261         return ReportActionNotArray(ctx, action, ACTION_FIELD_MODIFIERS);
    262 
    263     if (value->expr.op == EXPR_IDENT) {
    264         const char *valStr;
    265         valStr = xkb_atom_text(ctx, value->ident.ident);
    266         if (valStr && (istreq(valStr, "usemodmapmods") ||
    267                        istreq(valStr, "modmapmods"))) {
    268             *mods_rtrn = 0;
    269             *flags_inout |= ACTION_MODS_LOOKUP_MODMAP;
    270             return true;
    271         }
    272     }
    273 
    274     if (!ExprResolveModMask(ctx, value, MOD_BOTH, mods, mods_rtrn))
    275         return ReportMismatch(ctx, action,
    276                               ACTION_FIELD_MODIFIERS, "modifier mask");
    277 
    278     *flags_inout &= ~ACTION_MODS_LOOKUP_MODMAP;
    279     return true;
    280 }
    281 
    282 static const LookupEntry lockWhich[] = {
    283     { "both", 0 },
    284     { "lock", ACTION_LOCK_NO_UNLOCK },
    285     { "neither", (ACTION_LOCK_NO_LOCK | ACTION_LOCK_NO_UNLOCK) },
    286     { "unlock", ACTION_LOCK_NO_LOCK },
    287     { NULL, 0 }
    288 };
    289 
    290 static bool
    291 CheckAffectField(struct xkb_context *ctx, enum xkb_action_type action,
    292                  const ExprDef *array_ndx, const ExprDef *value,
    293                  enum xkb_action_flags *flags_inout)
    294 {
    295     enum xkb_action_flags flags;
    296 
    297     if (array_ndx)
    298         return ReportActionNotArray(ctx, action, ACTION_FIELD_AFFECT);
    299 
    300     if (!ExprResolveEnum(ctx, value, &flags, lockWhich))
    301         return ReportMismatch(ctx, action, ACTION_FIELD_AFFECT,
    302                               "lock, unlock, both, neither");
    303 
    304     *flags_inout &= ~(ACTION_LOCK_NO_LOCK | ACTION_LOCK_NO_UNLOCK);
    305     *flags_inout |= flags;
    306     return true;
    307 }
    308 
    309 static bool
    310 HandleSetLatchLockMods(struct xkb_context *ctx, const struct xkb_mod_set *mods,
    311                        union xkb_action *action, enum action_field field,
    312                        const ExprDef *array_ndx, const ExprDef *value)
    313 {
    314     struct xkb_mod_action *act = &action->mods;
    315     const enum xkb_action_type type = action->type;
    316 
    317     if (field == ACTION_FIELD_MODIFIERS)
    318         return CheckModifierField(ctx, mods, action->type, array_ndx, value,
    319                                   &act->flags, &act->mods.mods);
    320     if ((type == ACTION_TYPE_MOD_SET || type == ACTION_TYPE_MOD_LATCH) &&
    321         field == ACTION_FIELD_CLEAR_LOCKS)
    322         return CheckBooleanFlag(ctx, action->type, field,
    323                                 ACTION_LOCK_CLEAR, array_ndx, value,
    324                                 &act->flags);
    325     if (type == ACTION_TYPE_MOD_LATCH &&
    326         field == ACTION_FIELD_LATCH_TO_LOCK)
    327         return CheckBooleanFlag(ctx, action->type, field,
    328                                 ACTION_LATCH_TO_LOCK, array_ndx, value,
    329                                 &act->flags);
    330     if (type == ACTION_TYPE_MOD_LOCK &&
    331         field == ACTION_FIELD_AFFECT)
    332         return CheckAffectField(ctx, action->type, array_ndx, value,
    333                                 &act->flags);
    334 
    335     return ReportIllegal(ctx, action->type, field);
    336 }
    337 
    338 static bool
    339 CheckGroupField(struct xkb_context *ctx, enum xkb_action_type action,
    340                 const ExprDef *array_ndx, const ExprDef *value,
    341                 enum xkb_action_flags *flags_inout, int32_t *group_rtrn)
    342 {
    343     const ExprDef *spec;
    344     xkb_layout_index_t idx;
    345     enum xkb_action_flags flags = *flags_inout;
    346 
    347     if (array_ndx)
    348         return ReportActionNotArray(ctx, action, ACTION_FIELD_GROUP);
    349 
    350     if (value->expr.op == EXPR_NEGATE || value->expr.op == EXPR_UNARY_PLUS) {
    351         flags &= ~ACTION_ABSOLUTE_SWITCH;
    352         spec = value->unary.child;
    353     }
    354     else {
    355         flags |= ACTION_ABSOLUTE_SWITCH;
    356         spec = value;
    357     }
    358 
    359     if (!ExprResolveGroup(ctx, spec, &idx))
    360         return ReportMismatch(ctx, action, ACTION_FIELD_GROUP,
    361                               "integer (range 1..8)");
    362 
    363     /* +n, -n are relative, n is absolute. */
    364     if (value->expr.op == EXPR_NEGATE || value->expr.op == EXPR_UNARY_PLUS) {
    365         *group_rtrn = (int32_t) idx;
    366         if (value->expr.op == EXPR_NEGATE)
    367             *group_rtrn = -*group_rtrn;
    368     }
    369     else {
    370         *group_rtrn = (int32_t) (idx - 1);
    371     }
    372     *flags_inout = flags;
    373     return true;
    374 }
    375 
    376 static bool
    377 HandleSetLatchLockGroup(struct xkb_context *ctx, const struct xkb_mod_set *mods,
    378                         union xkb_action *action, enum action_field field,
    379                         const ExprDef *array_ndx, const ExprDef *value)
    380 {
    381     struct xkb_group_action *act = &action->group;
    382     const enum xkb_action_type type = action->type;
    383 
    384     if (field == ACTION_FIELD_GROUP)
    385         return CheckGroupField(ctx, action->type, array_ndx, value,
    386                                &act->flags, &act->group);
    387     if ((type == ACTION_TYPE_GROUP_SET || type == ACTION_TYPE_GROUP_LATCH) &&
    388         field == ACTION_FIELD_CLEAR_LOCKS)
    389         return CheckBooleanFlag(ctx, action->type, field,
    390                                 ACTION_LOCK_CLEAR, array_ndx, value,
    391                                 &act->flags);
    392     if (type == ACTION_TYPE_GROUP_LATCH &&
    393         field == ACTION_FIELD_LATCH_TO_LOCK)
    394         return CheckBooleanFlag(ctx, action->type, field,
    395                                 ACTION_LATCH_TO_LOCK, array_ndx, value,
    396                                 &act->flags);
    397 
    398     return ReportIllegal(ctx, action->type, field);
    399 }
    400 
    401 static bool
    402 HandleMovePtr(struct xkb_context *ctx, const struct xkb_mod_set *mods,
    403               union xkb_action *action, enum action_field field,
    404               const ExprDef *array_ndx, const ExprDef *value)
    405 {
    406     struct xkb_pointer_action *act = &action->ptr;
    407 
    408     if (field == ACTION_FIELD_X || field == ACTION_FIELD_Y) {
    409         int val;
    410         const bool absolute = (value->expr.op != EXPR_NEGATE &&
    411                                value->expr.op != EXPR_UNARY_PLUS);
    412 
    413         if (array_ndx)
    414             return ReportActionNotArray(ctx, action->type, field);
    415 
    416         if (!ExprResolveInteger(ctx, value, &val))
    417             return ReportMismatch(ctx, action->type, field, "integer");
    418 
    419         if (val < INT16_MIN || val > INT16_MAX) {
    420             log_err(ctx,
    421                     "The %s field in the %s action must be in range %d..%d; "
    422                     "Action definition ignored\n",
    423                     fieldText(field), ActionTypeText(action->type),
    424                     INT16_MIN, INT16_MAX);
    425             return false;
    426         }
    427 
    428         if (field == ACTION_FIELD_X) {
    429             if (absolute)
    430                 act->flags |= ACTION_ABSOLUTE_X;
    431             act->x = (int16_t) val;
    432         }
    433         else {
    434             if (absolute)
    435                 act->flags |= ACTION_ABSOLUTE_Y;
    436             act->y = (int16_t) val;
    437         }
    438 
    439         return true;
    440     }
    441     else if (field == ACTION_FIELD_ACCEL) {
    442         return CheckBooleanFlag(ctx, action->type, field,
    443                                 ACTION_ACCEL, array_ndx, value, &act->flags);
    444     }
    445 
    446     return ReportIllegal(ctx, action->type, field);
    447 }
    448 
    449 static bool
    450 HandlePtrBtn(struct xkb_context *ctx, const struct xkb_mod_set *mods,
    451              union xkb_action *action, enum action_field field,
    452              const ExprDef *array_ndx, const ExprDef *value)
    453 {
    454     struct xkb_pointer_button_action *act = &action->btn;
    455 
    456     if (field == ACTION_FIELD_BUTTON) {
    457         int btn;
    458 
    459         if (array_ndx)
    460             return ReportActionNotArray(ctx, action->type, field);
    461 
    462         if (!ExprResolveButton(ctx, value, &btn))
    463             return ReportMismatch(ctx, action->type, field,
    464                                   "integer (range 1..5)");
    465 
    466         if (btn < 0 || btn > 5) {
    467             log_err(ctx,
    468                     "Button must specify default or be in the range 1..5; "
    469                     "Illegal button value %d ignored\n", btn);
    470             return false;
    471         }
    472 
    473         act->button = btn;
    474         return true;
    475     }
    476     else if (action->type == ACTION_TYPE_PTR_LOCK &&
    477              field == ACTION_FIELD_AFFECT) {
    478         return CheckAffectField(ctx, action->type, array_ndx, value,
    479                                 &act->flags);
    480     }
    481     else if (field == ACTION_FIELD_COUNT) {
    482         int val;
    483 
    484         if (array_ndx)
    485             return ReportActionNotArray(ctx, action->type, field);
    486 
    487         if (!ExprResolveInteger(ctx, value, &val))
    488             return ReportMismatch(ctx, action->type, field, "integer");
    489 
    490         if (val < 0 || val > 255) {
    491             log_err(ctx,
    492                     "The count field must have a value in the range 0..255; "
    493                     "Illegal count %d ignored\n", val);
    494             return false;
    495         }
    496 
    497         act->count = (uint8_t) val;
    498         return true;
    499     }
    500 
    501     return ReportIllegal(ctx, action->type, field);
    502 }
    503 
    504 static const LookupEntry ptrDflts[] = {
    505     { "dfltbtn", 1 },
    506     { "defaultbutton", 1 },
    507     { "button", 1 },
    508     { NULL, 0 }
    509 };
    510 
    511 static bool
    512 HandleSetPtrDflt(struct xkb_context *ctx, const struct xkb_mod_set *mods,
    513                  union xkb_action *action, enum action_field field,
    514                  const ExprDef *array_ndx, const ExprDef *value)
    515 {
    516     struct xkb_pointer_default_action *act = &action->dflt;
    517 
    518     if (field == ACTION_FIELD_AFFECT) {
    519         unsigned int val;
    520 
    521         if (array_ndx)
    522             return ReportActionNotArray(ctx, action->type, field);
    523 
    524         if (!ExprResolveEnum(ctx, value, &val, ptrDflts))
    525             return ReportMismatch(ctx, action->type, field,
    526                                   "pointer component");
    527         return true;
    528     }
    529     else if (field == ACTION_FIELD_BUTTON || field == ACTION_FIELD_VALUE) {
    530         const ExprDef *button;
    531         int btn;
    532 
    533         if (array_ndx)
    534             return ReportActionNotArray(ctx, action->type, field);
    535 
    536         if (value->expr.op == EXPR_NEGATE ||
    537             value->expr.op == EXPR_UNARY_PLUS) {
    538             act->flags &= ~ACTION_ABSOLUTE_SWITCH;
    539             button = value->unary.child;
    540         }
    541         else {
    542             act->flags |= ACTION_ABSOLUTE_SWITCH;
    543             button = value;
    544         }
    545 
    546         if (!ExprResolveButton(ctx, button, &btn))
    547             return ReportMismatch(ctx, action->type, field,
    548                                   "integer (range 1..5)");
    549 
    550         if (btn < 0 || btn > 5) {
    551             log_err(ctx,
    552                     "New default button value must be in the range 1..5; "
    553                     "Illegal default button value %d ignored\n", btn);
    554             return false;
    555         }
    556         if (btn == 0) {
    557             log_err(ctx,
    558                     "Cannot set default pointer button to \"default\"; "
    559                     "Illegal default button setting ignored\n");
    560             return false;
    561         }
    562 
    563         act->value = (value->expr.op == EXPR_NEGATE ? -btn: btn);
    564         return true;
    565     }
    566 
    567     return ReportIllegal(ctx, action->type, field);
    568 }
    569 
    570 static bool
    571 HandleSwitchScreen(struct xkb_context *ctx, const struct xkb_mod_set *mods,
    572                    union xkb_action *action, enum action_field field,
    573                    const ExprDef *array_ndx, const ExprDef *value)
    574 {
    575     struct xkb_switch_screen_action *act = &action->screen;
    576 
    577     if (field == ACTION_FIELD_SCREEN) {
    578         const ExprDef *scrn;
    579         int val;
    580 
    581         if (array_ndx)
    582             return ReportActionNotArray(ctx, action->type, field);
    583 
    584         if (value->expr.op == EXPR_NEGATE ||
    585             value->expr.op == EXPR_UNARY_PLUS) {
    586             act->flags &= ~ACTION_ABSOLUTE_SWITCH;
    587             scrn = value->unary.child;
    588         }
    589         else {
    590             act->flags |= ACTION_ABSOLUTE_SWITCH;
    591             scrn = value;
    592         }
    593 
    594         if (!ExprResolveInteger(ctx, scrn, &val))
    595             return ReportMismatch(ctx, action->type, field,
    596                                   "integer (0..255)");
    597 
    598         if (val < 0 || val > 255) {
    599             log_err(ctx,
    600                     "Screen index must be in the range 1..255; "
    601                     "Illegal screen value %d ignored\n", val);
    602             return false;
    603         }
    604 
    605         act->screen = (value->expr.op == EXPR_NEGATE ? -val : val);
    606         return true;
    607     }
    608     else if (field == ACTION_FIELD_SAME) {
    609         return CheckBooleanFlag(ctx, action->type, field,
    610                                 ACTION_SAME_SCREEN, array_ndx, value,
    611                                 &act->flags);
    612     }
    613 
    614     return ReportIllegal(ctx, action->type, field);
    615 }
    616 
    617 static bool
    618 HandleSetLockControls(struct xkb_context *ctx, const struct xkb_mod_set *mods,
    619                       union xkb_action *action, enum action_field field,
    620                       const ExprDef *array_ndx, const ExprDef *value)
    621 {
    622     struct xkb_controls_action *act = &action->ctrls;
    623 
    624     if (field == ACTION_FIELD_CONTROLS) {
    625         enum xkb_action_controls mask;
    626 
    627         if (array_ndx)
    628             return ReportActionNotArray(ctx, action->type, field);
    629 
    630         if (!ExprResolveMask(ctx, value, &mask, ctrlMaskNames))
    631             return ReportMismatch(ctx, action->type, field,
    632                                   "controls mask");
    633 
    634         act->ctrls = mask;
    635         return true;
    636     }
    637     else if (field == ACTION_FIELD_AFFECT) {
    638         return CheckAffectField(ctx, action->type, array_ndx, value,
    639                                 &act->flags);
    640     }
    641 
    642     return ReportIllegal(ctx, action->type, field);
    643 }
    644 
    645 static bool
    646 HandlePrivate(struct xkb_context *ctx, const struct xkb_mod_set *mods,
    647               union xkb_action *action, enum action_field field,
    648               const ExprDef *array_ndx, const ExprDef *value)
    649 {
    650     struct xkb_private_action *act = &action->priv;
    651 
    652     if (field == ACTION_FIELD_TYPE) {
    653         int type;
    654 
    655         if (array_ndx)
    656             return ReportActionNotArray(ctx, action->type, field);
    657 
    658         if (!ExprResolveInteger(ctx, value, &type))
    659             return ReportMismatch(ctx, ACTION_TYPE_PRIVATE, field, "integer");
    660 
    661         if (type < 0 || type > 255) {
    662             log_err(ctx,
    663                     "Private action type must be in the range 0..255; "
    664                     "Illegal type %d ignored\n", type);
    665             return false;
    666         }
    667 
    668         /*
    669          * It's possible for someone to write something like this:
    670          *      actions = [ Private(type=3,data[0]=1,data[1]=3,data[2]=3) ]
    671          * where the type refers to some existing action type, e.g. LockMods.
    672          * This assumes that this action's struct is laid out in memory
    673          * exactly as described in the XKB specification and libraries.
    674          * We, however, have changed these structs in various ways, so this
    675          * assumption is no longer true. Since this is a lousy "feature", we
    676          * make actions like these no-ops for now.
    677          */
    678         if (type < ACTION_TYPE_PRIVATE) {
    679             log_info(ctx,
    680                      "Private actions of type %s are not supported; Ignored\n",
    681                      ActionTypeText(type));
    682             act->type = ACTION_TYPE_NONE;
    683         }
    684         else {
    685             act->type = (enum xkb_action_type) type;
    686         }
    687 
    688         return true;
    689     }
    690     else if (field == ACTION_FIELD_DATA) {
    691         if (array_ndx == NULL) {
    692             xkb_atom_t val;
    693             const char *str;
    694             size_t len;
    695 
    696             if (!ExprResolveString(ctx, value, &val))
    697                 return ReportMismatch(ctx, action->type, field, "string");
    698 
    699             str = xkb_atom_text(ctx, val);
    700             len = strlen(str);
    701             if (len < 1 || len > 7) {
    702                 log_warn(ctx,
    703                          "A private action has 7 data bytes; "
    704                          "Illegal data ignored\n");
    705                 return false;
    706             }
    707 
    708             strncpy((char *) act->data, str, sizeof(act->data));
    709             return true;
    710         }
    711         else {
    712             int ndx, datum;
    713 
    714             if (!ExprResolveInteger(ctx, array_ndx, &ndx)) {
    715                 log_err(ctx,
    716                         "Array subscript must be integer; "
    717                         "Illegal subscript ignored\n");
    718                 return false;
    719             }
    720 
    721             if (ndx < 0 || (size_t) ndx >= sizeof(act->data)) {
    722                 log_err(ctx,
    723                         "The data for a private action is %lu bytes long; "
    724                         "Attempt to use data[%d] ignored\n",
    725                         (unsigned long) sizeof(act->data), ndx);
    726                 return false;
    727             }
    728 
    729             if (!ExprResolveInteger(ctx, value, &datum))
    730                 return ReportMismatch(ctx, act->type, field, "integer");
    731 
    732             if (datum < 0 || datum > 255) {
    733                 log_err(ctx,
    734                         "All data for a private action must be 0..255; "
    735                         "Illegal datum %d ignored\n", datum);
    736                 return false;
    737             }
    738 
    739             act->data[ndx] = (uint8_t) datum;
    740             return true;
    741         }
    742     }
    743 
    744     return ReportIllegal(ctx, ACTION_TYPE_NONE, field);
    745 }
    746 
    747 typedef bool (*actionHandler)(struct xkb_context *ctx,
    748                               const struct xkb_mod_set *mods,
    749                               union xkb_action *action,
    750                               enum action_field field,
    751                               const ExprDef *array_ndx,
    752                               const ExprDef *value);
    753 
    754 static const actionHandler handleAction[_ACTION_TYPE_NUM_ENTRIES] = {
    755     [ACTION_TYPE_NONE] = HandleNoAction,
    756     [ACTION_TYPE_MOD_SET] = HandleSetLatchLockMods,
    757     [ACTION_TYPE_MOD_LATCH] = HandleSetLatchLockMods,
    758     [ACTION_TYPE_MOD_LOCK] = HandleSetLatchLockMods,
    759     [ACTION_TYPE_GROUP_SET] = HandleSetLatchLockGroup,
    760     [ACTION_TYPE_GROUP_LATCH] = HandleSetLatchLockGroup,
    761     [ACTION_TYPE_GROUP_LOCK] = HandleSetLatchLockGroup,
    762     [ACTION_TYPE_PTR_MOVE] = HandleMovePtr,
    763     [ACTION_TYPE_PTR_BUTTON] = HandlePtrBtn,
    764     [ACTION_TYPE_PTR_LOCK] = HandlePtrBtn,
    765     [ACTION_TYPE_PTR_DEFAULT] = HandleSetPtrDflt,
    766     [ACTION_TYPE_TERMINATE] = HandleNoAction,
    767     [ACTION_TYPE_SWITCH_VT] = HandleSwitchScreen,
    768     [ACTION_TYPE_CTRL_SET] = HandleSetLockControls,
    769     [ACTION_TYPE_CTRL_LOCK] = HandleSetLockControls,
    770     [ACTION_TYPE_PRIVATE] = HandlePrivate,
    771 };
    772 
    773 /***====================================================================***/
    774 
    775 bool
    776 HandleActionDef(struct xkb_context *ctx, ActionsInfo *info,
    777                 const struct xkb_mod_set *mods, ExprDef *def,
    778                 union xkb_action *action)
    779 {
    780     ExprDef *arg;
    781     const char *str;
    782     enum xkb_action_type handler_type;
    783 
    784     if (def->expr.op != EXPR_ACTION_DECL) {
    785         log_err(ctx, "Expected an action definition, found %s\n",
    786                 expr_op_type_to_string(def->expr.op));
    787         return false;
    788     }
    789 
    790     str = xkb_atom_text(ctx, def->action.name);
    791     if (!stringToAction(str, &handler_type)) {
    792         log_err(ctx, "Unknown action %s\n", str);
    793         return false;
    794     }
    795 
    796     /*
    797      * Get the default values for this action type, as modified by
    798      * statements such as:
    799      *     latchMods.clearLocks = True;
    800      */
    801     *action = info->actions[handler_type];
    802 
    803     /*
    804      * Now change the action properties as specified for this
    805      * particular instance, e.g. "modifiers" and "clearLocks" in:
    806      *     SetMods(modifiers=Alt,clearLocks);
    807      */
    808     for (arg = def->action.args; arg != NULL;
    809          arg = (ExprDef *) arg->common.next) {
    810         const ExprDef *value;
    811         ExprDef *field, *arrayRtrn;
    812         const char *elemRtrn, *fieldRtrn;
    813         enum action_field fieldNdx;
    814 
    815         if (arg->expr.op == EXPR_ASSIGN) {
    816             field = arg->binary.left;
    817             value = arg->binary.right;
    818         }
    819         else if (arg->expr.op == EXPR_NOT || arg->expr.op == EXPR_INVERT) {
    820             field = arg->unary.child;
    821             value = (const ExprDef *) &constFalse;
    822         }
    823         else {
    824             field = arg;
    825             value = (const ExprDef *) &constTrue;
    826         }
    827 
    828         if (!ExprResolveLhs(ctx, field, &elemRtrn, &fieldRtrn, &arrayRtrn))
    829             return false;
    830 
    831         if (elemRtrn) {
    832             log_err(ctx,
    833                     "Cannot change defaults in an action definition; "
    834                     "Ignoring attempt to change %s.%s\n",
    835                     elemRtrn, fieldRtrn);
    836             return false;
    837         }
    838 
    839         if (!stringToField(fieldRtrn, &fieldNdx)) {
    840             log_err(ctx, "Unknown field name %s\n", fieldRtrn);
    841             return false;
    842         }
    843 
    844         if (!handleAction[handler_type](ctx, mods, action, fieldNdx,
    845                                         arrayRtrn, value))
    846             return false;
    847     }
    848 
    849     return true;
    850 }
    851 
    852 bool
    853 SetActionField(struct xkb_context *ctx, ActionsInfo *info,
    854                struct xkb_mod_set *mods, const char *elem,
    855                const char *field, ExprDef *array_ndx, ExprDef *value)
    856 {
    857     enum xkb_action_type action;
    858     enum action_field action_field;
    859 
    860     if (!stringToAction(elem, &action))
    861         return false;
    862 
    863     if (!stringToField(field, &action_field)) {
    864         log_err(ctx, "\"%s\" is not a legal field name\n", field);
    865         return false;
    866     }
    867 
    868     return handleAction[action](ctx, mods, &info->actions[action],
    869                                 action_field, array_ndx, value);
    870 }
    871