895 lines
27 KiB
C
895 lines
27 KiB
C
/************************************************************
|
|
Copyright (c) 1994 by Silicon Graphics Computer Systems, Inc.
|
|
|
|
Permission to use, copy, modify, and distribute this
|
|
software and its documentation for any purpose and without
|
|
fee is hereby granted, provided that the above copyright
|
|
notice appear in all copies and that both that copyright
|
|
notice and this permission notice appear in supporting
|
|
documentation, and that the name of Silicon Graphics not be
|
|
used in advertising or publicity pertaining to distribution
|
|
of the software without specific prior written permission.
|
|
Silicon Graphics makes no representation about the suitability
|
|
of this software for any purpose. It is provided "as is"
|
|
without any express or implied warranty.
|
|
|
|
SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
|
|
SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON
|
|
GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
|
|
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
|
|
DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
|
OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH
|
|
THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
********************************************************/
|
|
|
|
#include "xkbcomp.h"
|
|
#include "tokens.h"
|
|
#include "expr.h"
|
|
#include "keycodes.h"
|
|
#include "misc.h"
|
|
#include "alias.h"
|
|
|
|
char *
|
|
longText(unsigned long val, unsigned format)
|
|
{
|
|
char buf[4];
|
|
|
|
LongToKeyName(val, buf);
|
|
return XkbKeyNameText(buf, format);
|
|
}
|
|
|
|
/***====================================================================***/
|
|
|
|
void
|
|
LongToKeyName(unsigned long val, char *name)
|
|
{
|
|
name[0] = ((val >> 24) & 0xff);
|
|
name[1] = ((val >> 16) & 0xff);
|
|
name[2] = ((val >> 8) & 0xff);
|
|
name[3] = (val & 0xff);
|
|
return;
|
|
}
|
|
|
|
/***====================================================================***/
|
|
|
|
typedef struct _IndicatorNameInfo
|
|
{
|
|
CommonInfo defs;
|
|
int ndx;
|
|
Atom name;
|
|
Bool virtual;
|
|
} IndicatorNameInfo;
|
|
|
|
typedef struct _KeyNamesInfo
|
|
{
|
|
char *name; /* e.g. evdev+aliases(qwerty) */
|
|
int errorCount;
|
|
unsigned fileID;
|
|
unsigned merge;
|
|
int computedMin; /* lowest keycode stored */
|
|
int computedMax; /* highest keycode stored */
|
|
int explicitMin;
|
|
int explicitMax;
|
|
int effectiveMin;
|
|
int effectiveMax;
|
|
unsigned long names[XkbMaxLegalKeyCode + 1]; /* 4-letter name of key, keycode is the index */
|
|
unsigned files[XkbMaxLegalKeyCode + 1];
|
|
unsigned char has_alt_forms[XkbMaxLegalKeyCode + 1];
|
|
IndicatorNameInfo *leds;
|
|
AliasInfo *aliases;
|
|
} KeyNamesInfo;
|
|
|
|
static void HandleKeycodesFile(XkbFile * file,
|
|
XkbDescPtr xkb,
|
|
unsigned merge,
|
|
KeyNamesInfo * info);
|
|
|
|
static void
|
|
InitIndicatorNameInfo(IndicatorNameInfo * ii, KeyNamesInfo * info)
|
|
{
|
|
ii->defs.defined = 0;
|
|
ii->defs.merge = info->merge;
|
|
ii->defs.fileID = info->fileID;
|
|
ii->defs.next = NULL;
|
|
ii->ndx = 0;
|
|
ii->name = None;
|
|
ii->virtual = False;
|
|
return;
|
|
}
|
|
|
|
static void
|
|
ClearIndicatorNameInfo(IndicatorNameInfo * ii, KeyNamesInfo * info)
|
|
{
|
|
if (ii == info->leds)
|
|
{
|
|
ClearCommonInfo(&ii->defs);
|
|
info->leds = NULL;
|
|
}
|
|
return;
|
|
}
|
|
|
|
static IndicatorNameInfo *
|
|
NextIndicatorName(KeyNamesInfo * info)
|
|
{
|
|
IndicatorNameInfo *ii;
|
|
|
|
ii = uTypedAlloc(IndicatorNameInfo);
|
|
if (ii)
|
|
{
|
|
InitIndicatorNameInfo(ii, info);
|
|
info->leds = (IndicatorNameInfo *) AddCommonInfo(&info->leds->defs,
|
|
(CommonInfo *) ii);
|
|
}
|
|
return ii;
|
|
}
|
|
|
|
static IndicatorNameInfo *
|
|
FindIndicatorByIndex(KeyNamesInfo * info, int ndx)
|
|
{
|
|
IndicatorNameInfo *old;
|
|
|
|
for (old = info->leds; old != NULL;
|
|
old = (IndicatorNameInfo *) old->defs.next)
|
|
{
|
|
if (old->ndx == ndx)
|
|
return old;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static IndicatorNameInfo *
|
|
FindIndicatorByName(KeyNamesInfo * info, Atom name)
|
|
{
|
|
IndicatorNameInfo *old;
|
|
|
|
for (old = info->leds; old != NULL;
|
|
old = (IndicatorNameInfo *) old->defs.next)
|
|
{
|
|
if (old->name == name)
|
|
return old;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static Bool
|
|
AddIndicatorName(KeyNamesInfo * info, IndicatorNameInfo * new)
|
|
{
|
|
IndicatorNameInfo *old;
|
|
Bool replace;
|
|
|
|
replace = (new->defs.merge == MergeReplace) ||
|
|
(new->defs.merge == MergeOverride);
|
|
old = FindIndicatorByName(info, new->name);
|
|
if (old)
|
|
{
|
|
if (((old->defs.fileID == new->defs.fileID) && (warningLevel > 0))
|
|
|| (warningLevel > 9))
|
|
{
|
|
WARN1("Multiple indicators named %s\n",
|
|
XkbAtomText(NULL, new->name, XkbMessage));
|
|
if (old->ndx == new->ndx)
|
|
{
|
|
if (old->virtual != new->virtual)
|
|
{
|
|
if (replace)
|
|
old->virtual = new->virtual;
|
|
ACTION2("Using %s instead of %s\n",
|
|
(old->virtual ? "virtual" : "real"),
|
|
(old->virtual ? "real" : "virtual"));
|
|
}
|
|
else
|
|
{
|
|
ACTION("Identical definitions ignored\n");
|
|
}
|
|
return True;
|
|
}
|
|
else
|
|
{
|
|
if (replace)
|
|
ACTION2("Ignoring %d, using %d\n", old->ndx, new->ndx);
|
|
else
|
|
ACTION2("Using %d, ignoring %d\n", old->ndx, new->ndx);
|
|
}
|
|
if (replace)
|
|
{
|
|
if (info->leds == old)
|
|
info->leds = (IndicatorNameInfo *) old->defs.next;
|
|
else
|
|
{
|
|
IndicatorNameInfo *tmp;
|
|
tmp = info->leds;
|
|
for (; tmp != NULL;
|
|
tmp = (IndicatorNameInfo *) tmp->defs.next)
|
|
{
|
|
if (tmp->defs.next == (CommonInfo *) old)
|
|
{
|
|
tmp->defs.next = old->defs.next;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
uFree(old);
|
|
}
|
|
}
|
|
}
|
|
old = FindIndicatorByIndex(info, new->ndx);
|
|
if (old)
|
|
{
|
|
if (((old->defs.fileID == new->defs.fileID) && (warningLevel > 0))
|
|
|| (warningLevel > 9))
|
|
{
|
|
WARN1("Multiple names for indicator %d\n", new->ndx);
|
|
if ((old->name == new->name) && (old->virtual == new->virtual))
|
|
ACTION("Identical definitions ignored\n");
|
|
else
|
|
{
|
|
const char *oldType, *newType;
|
|
Atom using, ignoring;
|
|
if (old->virtual)
|
|
oldType = "virtual indicator";
|
|
else
|
|
oldType = "real indicator";
|
|
if (new->virtual)
|
|
newType = "virtual indicator";
|
|
else
|
|
newType = "real indicator";
|
|
if (replace)
|
|
{
|
|
using = new->name;
|
|
ignoring = old->name;
|
|
}
|
|
else
|
|
{
|
|
using = old->name;
|
|
ignoring = new->name;
|
|
}
|
|
ACTION4("Using %s %s, ignoring %s %s\n",
|
|
oldType, XkbAtomText(NULL, using, XkbMessage),
|
|
newType, XkbAtomText(NULL, ignoring, XkbMessage));
|
|
}
|
|
}
|
|
if (replace)
|
|
{
|
|
old->name = new->name;
|
|
old->virtual = new->virtual;
|
|
}
|
|
return True;
|
|
}
|
|
old = new;
|
|
new = NextIndicatorName(info);
|
|
if (!new)
|
|
{
|
|
WSGO1("Couldn't allocate name for indicator %d\n", old->ndx);
|
|
ACTION("Ignored\n");
|
|
return False;
|
|
}
|
|
new->name = old->name;
|
|
new->ndx = old->ndx;
|
|
new->virtual = old->virtual;
|
|
return True;
|
|
}
|
|
|
|
static void
|
|
ClearKeyNamesInfo(KeyNamesInfo * info)
|
|
{
|
|
if (info->name != NULL)
|
|
uFree(info->name);
|
|
info->name = NULL;
|
|
info->computedMax = info->explicitMax = info->explicitMin = -1;
|
|
info->computedMin = 256;
|
|
info->effectiveMin = 8;
|
|
info->effectiveMax = 255;
|
|
bzero((char *) info->names, sizeof(info->names));
|
|
bzero((char *) info->files, sizeof(info->files));
|
|
bzero((char *) info->has_alt_forms, sizeof(info->has_alt_forms));
|
|
if (info->leds)
|
|
ClearIndicatorNameInfo(info->leds, info);
|
|
if (info->aliases)
|
|
ClearAliases(&info->aliases);
|
|
return;
|
|
}
|
|
|
|
static void
|
|
InitKeyNamesInfo(KeyNamesInfo * info)
|
|
{
|
|
info->name = NULL;
|
|
info->leds = NULL;
|
|
info->aliases = NULL;
|
|
ClearKeyNamesInfo(info);
|
|
info->errorCount = 0;
|
|
return;
|
|
}
|
|
|
|
static int
|
|
FindKeyByLong(KeyNamesInfo * info, unsigned long name)
|
|
{
|
|
register int i;
|
|
|
|
for (i = info->effectiveMin; i <= info->effectiveMax; i++)
|
|
{
|
|
if (info->names[i] == name)
|
|
return i;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Store the name of the key as a long in the info struct under the given
|
|
* keycode. If the same keys is referred to twice, print a warning.
|
|
* Note that the key's name is stored as a long, the keycode is the index.
|
|
*/
|
|
static Bool
|
|
AddKeyName(KeyNamesInfo * info,
|
|
int kc,
|
|
char *name, unsigned merge, unsigned fileID, Bool reportCollisions)
|
|
{
|
|
int old;
|
|
unsigned long lval;
|
|
|
|
if ((kc < info->effectiveMin) || (kc > info->effectiveMax))
|
|
{
|
|
ERROR2("Illegal keycode %d for name <%s>\n", kc, name);
|
|
ACTION2("Must be in the range %d-%d inclusive\n",
|
|
info->effectiveMin, info->effectiveMax);
|
|
return False;
|
|
}
|
|
if (kc < info->computedMin)
|
|
info->computedMin = kc;
|
|
if (kc > info->computedMax)
|
|
info->computedMax = kc;
|
|
lval = KeyNameToLong(name);
|
|
|
|
if (reportCollisions)
|
|
{
|
|
reportCollisions = ((warningLevel > 7) ||
|
|
((warningLevel > 0)
|
|
&& (fileID == info->files[kc])));
|
|
}
|
|
|
|
if (info->names[kc] != 0)
|
|
{
|
|
char buf[6];
|
|
|
|
LongToKeyName(info->names[kc], buf);
|
|
buf[4] = '\0';
|
|
if (info->names[kc] == lval)
|
|
{
|
|
if (info->has_alt_forms[kc] || (merge == MergeAltForm))
|
|
{
|
|
info->has_alt_forms[kc] = True;
|
|
}
|
|
else if (reportCollisions)
|
|
{
|
|
WARN("Multiple identical key name definitions\n");
|
|
ACTION2("Later occurences of \"<%s> = %d\" ignored\n",
|
|
buf, kc);
|
|
}
|
|
return True;
|
|
}
|
|
if (merge == MergeAugment)
|
|
{
|
|
if (reportCollisions)
|
|
{
|
|
WARN1("Multiple names for keycode %d\n", kc);
|
|
ACTION2("Using <%s>, ignoring <%s>\n", buf, name);
|
|
}
|
|
return True;
|
|
}
|
|
else
|
|
{
|
|
if (reportCollisions)
|
|
{
|
|
WARN1("Multiple names for keycode %d\n", kc);
|
|
ACTION2("Using <%s>, ignoring <%s>\n", name, buf);
|
|
}
|
|
info->names[kc] = 0;
|
|
info->files[kc] = 0;
|
|
}
|
|
}
|
|
old = FindKeyByLong(info, lval);
|
|
if ((old != 0) && (old != kc))
|
|
{
|
|
if (merge == MergeOverride)
|
|
{
|
|
info->names[old] = 0;
|
|
info->files[old] = 0;
|
|
info->has_alt_forms[old] = True;
|
|
if (reportCollisions)
|
|
{
|
|
WARN1("Key name <%s> assigned to multiple keys\n", name);
|
|
ACTION2("Using %d, ignoring %d\n", kc, old);
|
|
}
|
|
}
|
|
else if (merge != MergeAltForm)
|
|
{
|
|
if ((reportCollisions) && (warningLevel > 3))
|
|
{
|
|
WARN1("Key name <%s> assigned to multiple keys\n", name);
|
|
ACTION2("Using %d, ignoring %d\n", old, kc);
|
|
ACTION
|
|
("Use 'alternate' keyword to assign the same name to multiple keys\n");
|
|
}
|
|
return True;
|
|
}
|
|
else
|
|
{
|
|
info->has_alt_forms[old] = True;
|
|
}
|
|
}
|
|
info->names[kc] = lval;
|
|
info->files[kc] = fileID;
|
|
info->has_alt_forms[kc] = (merge == MergeAltForm);
|
|
return True;
|
|
}
|
|
|
|
/***====================================================================***/
|
|
|
|
static void
|
|
MergeIncludedKeycodes(KeyNamesInfo * into, KeyNamesInfo * from,
|
|
unsigned merge)
|
|
{
|
|
register int i;
|
|
char buf[5];
|
|
|
|
if (from->errorCount > 0)
|
|
{
|
|
into->errorCount += from->errorCount;
|
|
return;
|
|
}
|
|
if (into->name == NULL)
|
|
{
|
|
into->name = from->name;
|
|
from->name = NULL;
|
|
}
|
|
for (i = from->computedMin; i <= from->computedMax; i++)
|
|
{
|
|
unsigned thisMerge;
|
|
if (from->names[i] == 0)
|
|
continue;
|
|
LongToKeyName(from->names[i], buf);
|
|
buf[4] = '\0';
|
|
if (from->has_alt_forms[i])
|
|
thisMerge = MergeAltForm;
|
|
else
|
|
thisMerge = merge;
|
|
if (!AddKeyName(into, i, buf, thisMerge, from->fileID, False))
|
|
into->errorCount++;
|
|
}
|
|
if (from->leds)
|
|
{
|
|
IndicatorNameInfo *led, *next;
|
|
for (led = from->leds; led != NULL; led = next)
|
|
{
|
|
if (merge != MergeDefault)
|
|
led->defs.merge = merge;
|
|
if (!AddIndicatorName(into, led))
|
|
into->errorCount++;
|
|
next = (IndicatorNameInfo *) led->defs.next;
|
|
}
|
|
}
|
|
if (!MergeAliases(&into->aliases, &from->aliases, merge))
|
|
into->errorCount++;
|
|
if (from->explicitMin > 0)
|
|
{
|
|
if ((into->explicitMin < 0)
|
|
|| (into->explicitMin > from->explicitMin))
|
|
into->effectiveMin = into->explicitMin = from->explicitMin;
|
|
}
|
|
if (from->explicitMax > 0)
|
|
{
|
|
if ((into->explicitMax < 0)
|
|
|| (into->explicitMax < from->explicitMax))
|
|
into->effectiveMax = into->explicitMax = from->explicitMax;
|
|
}
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* Handle the given include statement (e.g. "include "evdev+aliases(qwerty)").
|
|
*
|
|
* @param stmt The include statement from the keymap file.
|
|
* @param xkb Unused for all but the xkb->flags.
|
|
* @param info Struct to store the key info in.
|
|
*/
|
|
static Bool
|
|
HandleIncludeKeycodes(IncludeStmt * stmt, XkbDescPtr xkb, KeyNamesInfo * info)
|
|
{
|
|
unsigned newMerge;
|
|
XkbFile *rtrn;
|
|
KeyNamesInfo included = {NULL};
|
|
Bool haveSelf;
|
|
|
|
haveSelf = False;
|
|
if ((stmt->file == NULL) && (stmt->map == NULL))
|
|
{
|
|
haveSelf = True;
|
|
included = *info;
|
|
bzero(info, sizeof(KeyNamesInfo));
|
|
}
|
|
else if (strcmp(stmt->file, "computed") == 0)
|
|
{
|
|
xkb->flags |= AutoKeyNames;
|
|
info->explicitMin = XkbMinLegalKeyCode;
|
|
info->explicitMax = XkbMaxLegalKeyCode;
|
|
return (info->errorCount == 0);
|
|
} /* parse file, store returned info in the xkb struct */
|
|
else if (ProcessIncludeFile(stmt, XkmKeyNamesIndex, &rtrn, &newMerge))
|
|
{
|
|
InitKeyNamesInfo(&included);
|
|
HandleKeycodesFile(rtrn, xkb, MergeOverride, &included);
|
|
if (stmt->stmt != NULL)
|
|
{
|
|
if (included.name != NULL)
|
|
uFree(included.name);
|
|
included.name = stmt->stmt;
|
|
stmt->stmt = NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
info->errorCount += 10; /* XXX: why 10?? */
|
|
return False;
|
|
}
|
|
/* Do we have more than one include statement? */
|
|
if ((stmt->next != NULL) && (included.errorCount < 1))
|
|
{
|
|
IncludeStmt *next;
|
|
unsigned op;
|
|
KeyNamesInfo next_incl;
|
|
|
|
for (next = stmt->next; next != NULL; next = next->next)
|
|
{
|
|
if ((next->file == NULL) && (next->map == NULL))
|
|
{
|
|
haveSelf = True;
|
|
MergeIncludedKeycodes(&included, info, next->merge);
|
|
ClearKeyNamesInfo(info);
|
|
}
|
|
else if (ProcessIncludeFile(next, XkmKeyNamesIndex, &rtrn, &op))
|
|
{
|
|
InitKeyNamesInfo(&next_incl);
|
|
HandleKeycodesFile(rtrn, xkb, MergeOverride, &next_incl);
|
|
MergeIncludedKeycodes(&included, &next_incl, op);
|
|
ClearKeyNamesInfo(&next_incl);
|
|
}
|
|
else
|
|
{
|
|
info->errorCount += 10; /* XXX: Why 10?? */
|
|
return False;
|
|
}
|
|
}
|
|
}
|
|
if (haveSelf)
|
|
*info = included;
|
|
else
|
|
{
|
|
MergeIncludedKeycodes(info, &included, newMerge);
|
|
ClearKeyNamesInfo(&included);
|
|
}
|
|
return (info->errorCount == 0);
|
|
}
|
|
|
|
/**
|
|
* Parse the given statement and store the output in the info struct.
|
|
* e.g. <ESC> = 9
|
|
*/
|
|
static int
|
|
HandleKeycodeDef(KeycodeDef * stmt, unsigned merge, KeyNamesInfo * info)
|
|
{
|
|
int code;
|
|
ExprResult result;
|
|
|
|
if (!ExprResolveInteger(stmt->value, &result, NULL, NULL))
|
|
{
|
|
ACTION1("No value keycode assigned to name <%s>\n", stmt->name);
|
|
return 0;
|
|
}
|
|
code = result.ival;
|
|
if ((code < info->effectiveMin) || (code > info->effectiveMax))
|
|
{
|
|
ERROR2("Illegal keycode %d for name <%s>\n", code, stmt->name);
|
|
ACTION2("Must be in the range %d-%d inclusive\n",
|
|
info->effectiveMin, info->effectiveMax);
|
|
return 0;
|
|
}
|
|
if (stmt->merge != MergeDefault)
|
|
{
|
|
if (stmt->merge == MergeReplace)
|
|
merge = MergeOverride;
|
|
else
|
|
merge = stmt->merge;
|
|
}
|
|
return AddKeyName(info, code, stmt->name, merge, info->fileID, True);
|
|
}
|
|
|
|
#define MIN_KEYCODE_DEF 0
|
|
#define MAX_KEYCODE_DEF 1
|
|
|
|
/**
|
|
* Handle the minimum/maximum statement of the xkb file.
|
|
* Sets explicitMin/Max and effectiveMin/Max of the info struct.
|
|
*
|
|
* @return 1 on success, 0 otherwise.
|
|
*/
|
|
static int
|
|
HandleKeyNameVar(VarDef * stmt, KeyNamesInfo * info)
|
|
{
|
|
ExprResult tmp, field;
|
|
ExprDef *arrayNdx;
|
|
int which;
|
|
|
|
if (ExprResolveLhs(stmt->name, &tmp, &field, &arrayNdx) == 0)
|
|
return 0; /* internal error, already reported */
|
|
|
|
if (tmp.str != NULL)
|
|
{
|
|
ERROR1("Unknown element %s encountered\n", tmp.str);
|
|
ACTION1("Default for field %s ignored\n", field.str);
|
|
return 0;
|
|
}
|
|
if (uStrCaseCmp(field.str, "minimum") == 0)
|
|
which = MIN_KEYCODE_DEF;
|
|
else if (uStrCaseCmp(field.str, "maximum") == 0)
|
|
which = MAX_KEYCODE_DEF;
|
|
else
|
|
{
|
|
ERROR("Unknown field encountered\n");
|
|
ACTION1("Assigment to field %s ignored\n", field.str);
|
|
return 0;
|
|
}
|
|
if (arrayNdx != NULL)
|
|
{
|
|
ERROR1("The %s setting is not an array\n", field.str);
|
|
ACTION("Illegal array reference ignored\n");
|
|
return 0;
|
|
}
|
|
|
|
if (ExprResolveInteger(stmt->value, &tmp, NULL, NULL) == 0)
|
|
{
|
|
ACTION1("Assignment to field %s ignored\n", field.str);
|
|
return 0;
|
|
}
|
|
if ((tmp.ival < XkbMinLegalKeyCode) || (tmp.ival > XkbMaxLegalKeyCode))
|
|
{
|
|
ERROR3
|
|
("Illegal keycode %d (must be in the range %d-%d inclusive)\n",
|
|
tmp.ival, XkbMinLegalKeyCode, XkbMaxLegalKeyCode);
|
|
ACTION1("Value of \"%s\" not changed\n", field.str);
|
|
return 0;
|
|
}
|
|
if (which == MIN_KEYCODE_DEF)
|
|
{
|
|
if ((info->explicitMax > 0) && (info->explicitMax < tmp.ival))
|
|
{
|
|
ERROR2
|
|
("Minimum key code (%d) must be <= maximum key code (%d)\n",
|
|
tmp.ival, info->explicitMax);
|
|
ACTION("Minimum key code value not changed\n");
|
|
return 0;
|
|
}
|
|
if ((info->computedMax > 0) && (info->computedMin < tmp.ival))
|
|
{
|
|
ERROR2
|
|
("Minimum key code (%d) must be <= lowest defined key (%d)\n",
|
|
tmp.ival, info->computedMin);
|
|
ACTION("Minimum key code value not changed\n");
|
|
return 0;
|
|
}
|
|
info->explicitMin = tmp.ival;
|
|
info->effectiveMin = tmp.ival;
|
|
}
|
|
if (which == MAX_KEYCODE_DEF)
|
|
{
|
|
if ((info->explicitMin > 0) && (info->explicitMin > tmp.ival))
|
|
{
|
|
ERROR2("Maximum code (%d) must be >= minimum key code (%d)\n",
|
|
tmp.ival, info->explicitMin);
|
|
ACTION("Maximum code value not changed\n");
|
|
return 0;
|
|
}
|
|
if ((info->computedMax > 0) && (info->computedMax > tmp.ival))
|
|
{
|
|
ERROR2
|
|
("Maximum code (%d) must be >= highest defined key (%d)\n",
|
|
tmp.ival, info->computedMax);
|
|
ACTION("Maximum code value not changed\n");
|
|
return 0;
|
|
}
|
|
info->explicitMax = tmp.ival;
|
|
info->effectiveMax = tmp.ival;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
HandleIndicatorNameDef(IndicatorNameDef * def,
|
|
unsigned merge, KeyNamesInfo * info)
|
|
{
|
|
IndicatorNameInfo ii;
|
|
ExprResult tmp;
|
|
|
|
if ((def->ndx < 1) || (def->ndx > XkbNumIndicators))
|
|
{
|
|
info->errorCount++;
|
|
ERROR1("Name specified for illegal indicator index %d\n", def->ndx);
|
|
ACTION("Ignored\n");
|
|
return False;
|
|
}
|
|
InitIndicatorNameInfo(&ii, info);
|
|
ii.ndx = def->ndx;
|
|
if (!ExprResolveString(def->name, &tmp, NULL, NULL))
|
|
{
|
|
char buf[20];
|
|
snprintf(buf, sizeof(buf), "%d", def->ndx);
|
|
info->errorCount++;
|
|
return ReportBadType("indicator", "name", buf, "string");
|
|
}
|
|
ii.name = XkbInternAtom(NULL, tmp.str, False);
|
|
ii.virtual = def->virtual;
|
|
if (!AddIndicatorName(info, &ii))
|
|
return False;
|
|
return True;
|
|
}
|
|
|
|
/**
|
|
* Handle the xkb_keycodes section of a xkb file.
|
|
* All information about parsed keys is stored in the info struct.
|
|
*
|
|
* Such a section may have include statements, in which case this function is
|
|
* semi-recursive (it calls HandleIncludeKeycodes, which may call
|
|
* HandleKeycodesFile again).
|
|
*
|
|
* @param file The input file (parsed xkb_keycodes section)
|
|
* @param xkb Necessary to pass down, may have flags changed.
|
|
* @param merge Merge strategy (MergeOverride, etc.)
|
|
* @param info Struct to contain the fully parsed key information.
|
|
*/
|
|
static void
|
|
HandleKeycodesFile(XkbFile * file,
|
|
XkbDescPtr xkb, unsigned merge, KeyNamesInfo * info)
|
|
{
|
|
ParseCommon *stmt;
|
|
|
|
info->name = uStringDup(file->name);
|
|
stmt = file->defs;
|
|
while (stmt)
|
|
{
|
|
switch (stmt->stmtType)
|
|
{
|
|
case StmtInclude: /* e.g. include "evdev+aliases(qwerty)" */
|
|
if (!HandleIncludeKeycodes((IncludeStmt *) stmt, xkb, info))
|
|
info->errorCount++;
|
|
break;
|
|
case StmtKeycodeDef: /* e.g. <ESC> = 9; */
|
|
if (!HandleKeycodeDef((KeycodeDef *) stmt, merge, info))
|
|
info->errorCount++;
|
|
break;
|
|
case StmtKeyAliasDef: /* e.g. alias <MENU> = <COMP>; */
|
|
if (!HandleAliasDef((KeyAliasDef *) stmt,
|
|
merge, info->fileID, &info->aliases))
|
|
info->errorCount++;
|
|
break;
|
|
case StmtVarDef: /* e.g. minimum, maximum */
|
|
if (!HandleKeyNameVar((VarDef *) stmt, info))
|
|
info->errorCount++;
|
|
break;
|
|
case StmtIndicatorNameDef: /* e.g. indicator 1 = "Caps Lock"; */
|
|
if (!HandleIndicatorNameDef((IndicatorNameDef *) stmt,
|
|
merge, info))
|
|
{
|
|
info->errorCount++;
|
|
}
|
|
break;
|
|
case StmtInterpDef:
|
|
case StmtVModDef:
|
|
ERROR("Keycode files may define key and indicator names only\n");
|
|
ACTION1("Ignoring definition of %s\n",
|
|
((stmt->stmtType ==
|
|
StmtInterpDef) ? "a symbol interpretation" :
|
|
"virtual modifiers"));
|
|
info->errorCount++;
|
|
break;
|
|
default:
|
|
WSGO1("Unexpected statement type %d in HandleKeycodesFile\n",
|
|
stmt->stmtType);
|
|
break;
|
|
}
|
|
stmt = stmt->next;
|
|
if (info->errorCount > 10)
|
|
{
|
|
#ifdef NOISY
|
|
ERROR("Too many errors\n");
|
|
#endif
|
|
ACTION1("Abandoning keycodes file \"%s\"\n", file->topName);
|
|
break;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* Compile the xkb_keycodes section, parse it's output, return the results.
|
|
*
|
|
* @param file The parsed XKB file (may have include statements requiring
|
|
* further parsing)
|
|
* @param result The effective keycodes, as gathered from the file.
|
|
* @param merge Merge strategy.
|
|
*
|
|
* @return True on success, False otherwise.
|
|
*/
|
|
Bool
|
|
CompileKeycodes(XkbFile * file, XkbFileInfo * result, unsigned merge)
|
|
{
|
|
KeyNamesInfo info; /* contains all the info after parsing */
|
|
XkbDescPtr xkb;
|
|
|
|
xkb = result->xkb;
|
|
InitKeyNamesInfo(&info);
|
|
HandleKeycodesFile(file, xkb, merge, &info);
|
|
|
|
/* all the keys are now stored in info */
|
|
|
|
if (info.errorCount == 0)
|
|
{
|
|
if (info.explicitMin > 0) /* if "minimum" statement was present */
|
|
xkb->min_key_code = info.effectiveMin;
|
|
else
|
|
xkb->min_key_code = info.computedMin;
|
|
if (info.explicitMax > 0) /* if "maximum" statement was present */
|
|
xkb->max_key_code = info.effectiveMax;
|
|
else
|
|
xkb->max_key_code = info.computedMax;
|
|
if (XkbAllocNames(xkb, XkbKeyNamesMask | XkbIndicatorNamesMask, 0, 0)
|
|
== Success)
|
|
{
|
|
register int i;
|
|
xkb->names->keycodes = XkbInternAtom(xkb->dpy, info.name, False);
|
|
uDEBUG2(1, "key range: %d..%d\n", xkb->min_key_code,
|
|
xkb->max_key_code);
|
|
for (i = info.computedMin; i <= info.computedMax; i++)
|
|
{
|
|
LongToKeyName(info.names[i], xkb->names->keys[i].name);
|
|
uDEBUG2(2, "key %d = %s\n", i,
|
|
XkbKeyNameText(xkb->names->keys[i].name, XkbMessage));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
WSGO("Cannot create XkbNamesRec in CompileKeycodes\n");
|
|
return False;
|
|
}
|
|
if (info.leds)
|
|
{
|
|
IndicatorNameInfo *ii;
|
|
if (XkbAllocIndicatorMaps(xkb) != Success)
|
|
{
|
|
WSGO("Couldn't allocate IndicatorRec in CompileKeycodes\n");
|
|
ACTION("Physical indicators not set\n");
|
|
}
|
|
for (ii = info.leds; ii != NULL;
|
|
ii = (IndicatorNameInfo *) ii->defs.next)
|
|
{
|
|
xkb->names->indicators[ii->ndx - 1] =
|
|
XkbInternAtom(xkb->dpy,
|
|
XkbAtomGetString(NULL, ii->name), False);
|
|
if (xkb->indicators != NULL)
|
|
{
|
|
register unsigned bit;
|
|
bit = 1 << (ii->ndx - 1);
|
|
if (ii->virtual)
|
|
xkb->indicators->phys_indicators &= ~bit;
|
|
else
|
|
xkb->indicators->phys_indicators |= bit;
|
|
}
|
|
}
|
|
}
|
|
if (info.aliases)
|
|
ApplyAliases(xkb, False, &info.aliases);
|
|
return True;
|
|
}
|
|
ClearKeyNamesInfo(&info);
|
|
return False;
|
|
}
|