#include "xkbcomp.h"
#include "tokens.h"
#include "expr.h"
#include "vmod.h"
#include "action.h"
#include "misc.h"
typedef struct _PreserveInfo
{
CommonInfo defs;
short matchingMapIndex;
unsigned char indexMods;
unsigned char preMods;
unsigned short indexVMods;
unsigned short preVMods;
} PreserveInfo;
#define _KT_Name (1<<0)
#define _KT_Mask (1<<1)
#define _KT_Map (1<<2)
#define _KT_Preserve (1<<3)
#define _KT_LevelNames (1<<4)
typedef struct _KeyTypeInfo
{
CommonInfo defs;
Display *dpy;
Atom name;
int fileID;
unsigned mask;
unsigned vmask;
Bool groupInfo;
int numLevels;
int nEntries;
int szEntries;
XkbKTMapEntryPtr entries;
PreserveInfo *preserve;
int szNames;
Atom *lvlNames;
} KeyTypeInfo;
typedef struct _KeyTypesInfo
{
Display *dpy;
char *name;
int errorCount;
int fileID;
unsigned stdPresent;
int nTypes;
KeyTypeInfo *types;
KeyTypeInfo dflt;
VModInfo vmods;
} KeyTypesInfo;
Atom tok_ONE_LEVEL;
Atom tok_TWO_LEVEL;
Atom tok_ALPHABETIC;
Atom tok_KEYPAD;
#define ReportTypeShouldBeArray(t,f) \
ReportShouldBeArray("key type",(f),TypeTxt(t))
#define ReportTypeBadType(t,f,w) \
ReportBadType("key type",(f),TypeTxt(t),(w))
extern Bool AddMapEntry(XkbDescPtr ,
KeyTypeInfo * ,
XkbKTMapEntryPtr ,
Bool ,
Bool
);
extern Bool AddPreserve(XkbDescPtr ,
KeyTypeInfo * ,
PreserveInfo * ,
Bool ,
Bool
);
extern Bool AddLevelName(KeyTypeInfo * ,
unsigned ,
Atom ,
Bool ,
Bool
);
#define MapEntryTxt(t,x,e) \
XkbVModMaskText((t)->dpy,(x),(e)->mods.real_mods,(e)->mods.vmods,XkbMessage)
#define PreserveIndexTxt(t,x,p) \
XkbVModMaskText((t)->dpy,(x),(p)->indexMods,(p)->indexVMods,XkbMessage)
#define PreserveTxt(t,x,p) \
XkbVModMaskText((t)->dpy,(x),(p)->preMods,(p)->preVMods,XkbMessage)
#define TypeTxt(t) XkbAtomText((t)->dpy,(t)->name,XkbMessage)
#define TypeMaskTxt(t,x) \
XkbVModMaskText((t)->dpy,(x),(t)->mask,(t)->vmask,XkbMessage)
static void
InitKeyTypesInfo(KeyTypesInfo * info, XkbDescPtr xkb, KeyTypesInfo * from)
{
tok_ONE_LEVEL = XkbInternAtom(NULL, "ONE_LEVEL", False);
tok_TWO_LEVEL = XkbInternAtom(NULL, "TWO_LEVEL", False);
tok_ALPHABETIC = XkbInternAtom(NULL, "ALPHABETIC", False);
tok_KEYPAD = XkbInternAtom(NULL, "KEYPAD", False);
info->dpy = NULL;
info->name = uStringDup("default");
info->errorCount = 0;
info->stdPresent = 0;
info->nTypes = 0;
info->types = NULL;
info->dflt.defs.defined = 0;
info->dflt.defs.fileID = 0;
info->dflt.defs.merge = MergeOverride;
info->dflt.defs.next = NULL;
info->dflt.name = None;
info->dflt.mask = 0;
info->dflt.vmask = 0;
info->dflt.groupInfo = False;
info->dflt.numLevels = 1;
info->dflt.nEntries = info->dflt.szEntries = 0;
info->dflt.entries = NULL;
info->dflt.szNames = 0;
info->dflt.lvlNames = NULL;
info->dflt.preserve = NULL;
InitVModInfo(&info->vmods, xkb);
if (from != NULL)
{
info->dpy = from->dpy;
info->dflt = from->dflt;
if (from->dflt.entries)
{
info->dflt.entries = uTypedCalloc(from->dflt.szEntries,
XkbKTMapEntryRec);
if (info->dflt.entries)
{
unsigned sz = from->dflt.nEntries * sizeof(XkbKTMapEntryRec);
memcpy(info->dflt.entries, from->dflt.entries, sz);
}
}
if (from->dflt.lvlNames)
{
info->dflt.lvlNames = uTypedCalloc(from->dflt.szNames, Atom);
if (info->dflt.lvlNames)
{
register unsigned sz = from->dflt.szNames * sizeof(Atom);
memcpy(info->dflt.lvlNames, from->dflt.lvlNames, sz);
}
}
if (from->dflt.preserve)
{
PreserveInfo *old, *new, *last;
last = NULL;
old = from->dflt.preserve;
for (; old; old = (PreserveInfo *) old->defs.next)
{
new = uTypedAlloc(PreserveInfo);
if (!new)
return;
*new = *old;
new->defs.next = NULL;
if (last)
last->defs.next = (CommonInfo *) new;
else
info->dflt.preserve = new;
last = new;
}
}
}
return;
}
static void
FreeKeyTypeInfo(KeyTypeInfo * type)
{
if (type->entries != NULL)
{
uFree(type->entries);
type->entries = NULL;
}
if (type->lvlNames != NULL)
{
uFree(type->lvlNames);
type->lvlNames = NULL;
}
if (type->preserve != NULL)
{
ClearCommonInfo(&type->preserve->defs);
type->preserve = NULL;
}
return;
}
static void
FreeKeyTypesInfo(KeyTypesInfo * info)
{
info->dpy = NULL;
if (info->name)
uFree(info->name);
info->name = NULL;
if (info->types)
{
register KeyTypeInfo *type;
for (type = info->types; type; type = (KeyTypeInfo *) type->defs.next)
{
FreeKeyTypeInfo(type);
}
info->types = (KeyTypeInfo *) ClearCommonInfo(&info->types->defs);
}
FreeKeyTypeInfo(&info->dflt);
return;
}
static KeyTypeInfo *
NextKeyType(KeyTypesInfo * info)
{
KeyTypeInfo *type;
type = uTypedAlloc(KeyTypeInfo);
if (type != NULL)
{
bzero(type, sizeof(KeyTypeInfo));
type->defs.fileID = info->fileID;
type->dpy = info->dpy;
info->types = (KeyTypeInfo *) AddCommonInfo(&info->types->defs,
(CommonInfo *) type);
info->nTypes++;
}
return type;
}
static KeyTypeInfo *
FindMatchingKeyType(KeyTypesInfo * info, KeyTypeInfo * new)
{
KeyTypeInfo *old;
for (old = info->types; old; old = (KeyTypeInfo *) old->defs.next)
{
if (old->name == new->name)
return old;
}
return NULL;
}
static Bool
ReportTypeBadWidth(const char *type, int has, int needs)
{
ERROR3("Key type \"%s\" has %d levels, must have %d\n", type, has, needs);
ACTION("Illegal type definition ignored\n");
return False;
}
static Bool
AddKeyType(XkbDescPtr xkb, KeyTypesInfo * info, KeyTypeInfo * new)
{
KeyTypeInfo *old;
if (new->name == tok_ONE_LEVEL)
{
if (new->numLevels > 1)
return ReportTypeBadWidth("ONE_LEVEL", new->numLevels, 1);
info->stdPresent |= XkbOneLevelMask;
}
else if (new->name == tok_TWO_LEVEL)
{
if (new->numLevels > 2)
return ReportTypeBadWidth("TWO_LEVEL", new->numLevels, 2);
else if (new->numLevels < 2)
new->numLevels = 2;
info->stdPresent |= XkbTwoLevelMask;
}
else if (new->name == tok_ALPHABETIC)
{
if (new->numLevels > 2)
return ReportTypeBadWidth("ALPHABETIC", new->numLevels, 2);
else if (new->numLevels < 2)
new->numLevels = 2;
info->stdPresent |= XkbAlphabeticMask;
}
else if (new->name == tok_KEYPAD)
{
if (new->numLevels > 2)
return ReportTypeBadWidth("KEYPAD", new->numLevels, 2);
else if (new->numLevels < 2)
new->numLevels = 2;
info->stdPresent |= XkbKeypadMask;
}
old = FindMatchingKeyType(info, new);
if (old != NULL)
{
Bool report;
if ((new->defs.merge == MergeReplace)
|| (new->defs.merge == MergeOverride))
{
KeyTypeInfo *next = (KeyTypeInfo *) old->defs.next;
if (((old->defs.fileID == new->defs.fileID)
&& (warningLevel > 0)) || (warningLevel > 9))
{
WARN1("Multiple definitions of the %s key type\n",
XkbAtomGetString(NULL, new->name));
ACTION("Earlier definition ignored\n");
}
FreeKeyTypeInfo(old);
*old = *new;
new->szEntries = new->nEntries = 0;
new->entries = NULL;
new->preserve = NULL;
new->lvlNames = NULL;
old->defs.next = &next->defs;
return True;
}
report = (old->defs.fileID == new->defs.fileID) && (warningLevel > 0);
if (report)
{
WARN1("Multiple definitions of the %s key type\n",
XkbAtomGetString(NULL, new->name));
ACTION("Later definition ignored\n");
}
FreeKeyTypeInfo(new);
return True;
}
old = NextKeyType(info);
if (old == NULL)
return False;
*old = *new;
old->defs.next = NULL;
new->nEntries = new->szEntries = 0;
new->entries = NULL;
new->szNames = 0;
new->lvlNames = NULL;
new->preserve = NULL;
return True;
}
static void
MergeIncludedKeyTypes(KeyTypesInfo * into,
KeyTypesInfo * from, unsigned merge, XkbDescPtr xkb)
{
KeyTypeInfo *type;
if (from->errorCount > 0)
{
into->errorCount += from->errorCount;
return;
}
if (into->name == NULL)
{
into->name = from->name;
from->name = NULL;
}
for (type = from->types; type; type = (KeyTypeInfo *) type->defs.next)
{
if (merge != MergeDefault)
type->defs.merge = merge;
if (!AddKeyType(xkb, into, type))
into->errorCount++;
}
into->stdPresent |= from->stdPresent;
return;
}
typedef void (*FileHandler) (XkbFile * ,
XkbDescPtr ,
unsigned ,
KeyTypesInfo *
);
static Bool
HandleIncludeKeyTypes(IncludeStmt * stmt,
XkbDescPtr xkb, KeyTypesInfo * info, FileHandler hndlr)
{
unsigned newMerge;
XkbFile *rtrn;
KeyTypesInfo included;
Bool haveSelf;
haveSelf = False;
if ((stmt->file == NULL) && (stmt->map == NULL))
{
haveSelf = True;
included = *info;
bzero(info, sizeof(KeyTypesInfo));
}
else if (ProcessIncludeFile(stmt, XkmTypesIndex, &rtrn, &newMerge))
{
InitKeyTypesInfo(&included, xkb, info);
included.fileID = included.dflt.defs.fileID = rtrn->id;
included.dflt.defs.merge = newMerge;
(*hndlr) (rtrn, xkb, newMerge, &included);
if (stmt->stmt != NULL)
{
if (included.name != NULL)
uFree(included.name);
included.name = stmt->stmt;
stmt->stmt = NULL;
}
}
else
{
info->errorCount += 10;
return False;
}
if ((stmt->next != NULL) && (included.errorCount < 1))
{
IncludeStmt *next;
unsigned op;
KeyTypesInfo next_incl;
for (next = stmt->next; next != NULL; next = next->next)
{
if ((next->file == NULL) && (next->map == NULL))
{
haveSelf = True;
MergeIncludedKeyTypes(&included, info, next->merge, xkb);
FreeKeyTypesInfo(info);
}
else if (ProcessIncludeFile(next, XkmTypesIndex, &rtrn, &op))
{
InitKeyTypesInfo(&next_incl, xkb, &included);
next_incl.fileID = next_incl.dflt.defs.fileID = rtrn->id;
next_incl.dflt.defs.merge = op;
(*hndlr) (rtrn, xkb, op, &next_incl);
MergeIncludedKeyTypes(&included, &next_incl, op, xkb);
FreeKeyTypesInfo(&next_incl);
}
else
{
info->errorCount += 10;
return False;
}
}
}
if (haveSelf)
*info = included;
else
{
MergeIncludedKeyTypes(info, &included, newMerge, xkb);
FreeKeyTypesInfo(&included);
}
return (info->errorCount == 0);
}
static XkbKTMapEntryPtr
FindMatchingMapEntry(KeyTypeInfo * type, unsigned mask, unsigned vmask)
{
register int i;
XkbKTMapEntryPtr entry;
for (i = 0, entry = type->entries; i < type->nEntries; i++, entry++)
{
if ((entry->mods.real_mods == mask) && (entry->mods.vmods == vmask))
return entry;
}
return NULL;
}
static void
DeleteLevel1MapEntries(KeyTypeInfo * type)
{
register int i, n;
for (i = 0; i < type->nEntries; i++)
{
if (type->entries[i].level == 0)
{
for (n = i; n < type->nEntries - 1; n++)
{
type->entries[n] = type->entries[n + 1];
}
type->nEntries--;
}
}
return;
}
static XkbKTMapEntryPtr
NextMapEntry(KeyTypeInfo * type)
{
if (type->entries == NULL)
{
type->entries = uTypedCalloc(2, XkbKTMapEntryRec);
if (type->entries == NULL)
{
ERROR1("Couldn't allocate map entries for %s\n", TypeTxt(type));
ACTION("Map entries lost\n");
return NULL;
}
type->szEntries = 2;
type->nEntries = 0;
}
else if (type->nEntries >= type->szEntries)
{
type->szEntries *= 2;
type->entries = uTypedRecalloc(type->entries,
type->nEntries, type->szEntries,
XkbKTMapEntryRec);
if (type->entries == NULL)
{
ERROR1("Couldn't reallocate map entries for %s\n", TypeTxt(type));
ACTION("Map entries lost\n");
return NULL;
}
}
return &type->entries[type->nEntries++];
}
Bool
AddPreserve(XkbDescPtr xkb,
KeyTypeInfo * type, PreserveInfo * new, Bool clobber, Bool report)
{
PreserveInfo *old;
old = type->preserve;
while (old != NULL)
{
if ((old->indexMods != new->indexMods) ||
(old->indexVMods != new->indexVMods))
{
old = (PreserveInfo *) old->defs.next;
continue;
}
if ((old->preMods == new->preMods)
&& (old->preVMods == new->preVMods))
{
if (warningLevel > 9)
{
WARN2("Identical definitions for preserve[%s] in %s\n",
PreserveIndexTxt(type, xkb, old), TypeTxt(type));
ACTION("Ignored\n");
}
return True;
}
if (report && (warningLevel > 0))
{
char *str;
WARN2("Multiple definitions for preserve[%s] in %s\n",
PreserveIndexTxt(type, xkb, old), TypeTxt(type));
if (clobber)
str = PreserveTxt(type, xkb, new);
else
str = PreserveTxt(type, xkb, old);
ACTION1("Using %s, ", str);
if (clobber)
str = PreserveTxt(type, xkb, old);
else
str = PreserveTxt(type, xkb, new);
INFO1("ignoring %s\n", str);
}
if (clobber)
{
old->preMods = new->preMods;
old->preVMods = new->preVMods;
}
return True;
}
old = uTypedAlloc(PreserveInfo);
if (!old)
{
WSGO1("Couldn't allocate preserve in %s\n", TypeTxt(type));
ACTION1("Preserve[%s] lost\n", PreserveIndexTxt(type, xkb, old));
return False;
}
*old = *new;
old->matchingMapIndex = -1;
type->preserve =
(PreserveInfo *) AddCommonInfo(&type->preserve->defs, &old->defs);
return True;
}
Bool
AddMapEntry(XkbDescPtr xkb,
KeyTypeInfo * type,
XkbKTMapEntryPtr new, Bool clobber, Bool report)
{
XkbKTMapEntryPtr old;
if ((old =
FindMatchingMapEntry(type, new->mods.real_mods, new->mods.vmods)))
{
if (report && (old->level != new->level))
{
unsigned use, ignore;
if (clobber)
{
use = new->level + 1;
ignore = old->level + 1;
}
else
{
use = old->level + 1;
ignore = new->level + 1;
}
WARN2("Multiple map entries for %s in %s\n",
MapEntryTxt(type, xkb, new), TypeTxt(type));
ACTION2("Using %d, ignoring %d\n", use, ignore);
}
else if (warningLevel > 9)
{
WARN3("Multiple occurences of map[%s]= %d in %s\n",
MapEntryTxt(type, xkb, new), new->level + 1, TypeTxt(type));
ACTION("Ignored\n");
return True;
}
if (clobber)
old->level = new->level;
return True;
}
if ((old = NextMapEntry(type)) == NULL)
return False;
if (new->level >= type->numLevels)
type->numLevels = new->level + 1;
if (new->mods.vmods == 0)
old->active = True;
else
old->active = False;
old->mods.mask = new->mods.real_mods;
old->mods.real_mods = new->mods.real_mods;
old->mods.vmods = new->mods.vmods;
old->level = new->level;
return True;
}
static LookupEntry lnames[] = {
{"level1", 1},
{"level2", 2},
{"level3", 3},
{"level4", 4},
{"level5", 5},
{"level6", 6},
{"level7", 7},
{"level8", 8},
{NULL, 0}
};
static Bool
SetMapEntry(KeyTypeInfo * type,
XkbDescPtr xkb, ExprDef * arrayNdx, ExprDef * value)
{
ExprResult rtrn;
XkbKTMapEntryRec entry;
if (arrayNdx == NULL)
return ReportTypeShouldBeArray(type, "map entry");
if (!ExprResolveModMask(arrayNdx, &rtrn, LookupVModMask, (XPointer) xkb))
return ReportTypeBadType(type, "map entry", "modifier mask");
entry.mods.real_mods = rtrn.uval & 0xff;
entry.mods.vmods = (rtrn.uval >> 8) & 0xffff;
if ((entry.mods.real_mods & (~type->mask)) ||
((entry.mods.vmods & (~type->vmask)) != 0))
{
if (warningLevel > 0)
{
WARN1("Map entry for unused modifiers in %s\n", TypeTxt(type));
ACTION1("Using %s instead of ",
XkbVModMaskText(type->dpy, xkb,
entry.mods.real_mods & type->mask,
entry.mods.vmods & type->vmask,
XkbMessage));
INFO1("%s\n", MapEntryTxt(type, xkb, &entry));
}
entry.mods.real_mods &= type->mask;
entry.mods.vmods &= type->vmask;
}
if (!ExprResolveInteger(value, &rtrn, SimpleLookup, (XPointer) lnames))
{
ERROR("Level specifications in a key type must be integer\n");
ACTION("Ignoring malformed level specification\n");
return False;
}
if ((rtrn.ival < 1) || (rtrn.ival > XkbMaxShiftLevel + 1))
{
ERROR3("Shift level %d out of range (1..%d) in key type %s\n",
XkbMaxShiftLevel + 1, rtrn.ival, TypeTxt(type));
ACTION1("Ignoring illegal definition of map[%s]\n",
MapEntryTxt(type, xkb, &entry));
return False;
}
entry.level = rtrn.ival - 1;
return AddMapEntry(xkb, type, &entry, True, True);
}
static Bool
SetPreserve(KeyTypeInfo * type,
XkbDescPtr xkb, ExprDef * arrayNdx, ExprDef * value)
{
ExprResult rtrn;
PreserveInfo new;
if (arrayNdx == NULL)
return ReportTypeShouldBeArray(type, "preserve entry");
if (!ExprResolveModMask(arrayNdx, &rtrn, LookupVModMask, (XPointer) xkb))
return ReportTypeBadType(type, "preserve entry", "modifier mask");
new.defs = type->defs;
new.defs.next = NULL;
new.indexMods = rtrn.uval & 0xff;
new.indexVMods = (rtrn.uval >> 8) & 0xffff;
if ((new.indexMods & (~type->mask)) || (new.indexVMods & (~type->vmask)))
{
if (warningLevel > 0)
{
WARN1("Preserve for modifiers not used by the %s type\n",
TypeTxt(type));
ACTION1("Index %s converted to ",
PreserveIndexTxt(type, xkb, &new));
}
new.indexMods &= type->mask;
new.indexVMods &= type->vmask;
if (warningLevel > 0)
INFO1("%s\n", PreserveIndexTxt(type, xkb, &new));
}
if (!ExprResolveModMask(value, &rtrn, LookupVModMask, (XPointer) xkb))
{
ERROR("Preserve value in a key type is not a modifier mask\n");
ACTION2("Ignoring preserve[%s] in type %s\n",
PreserveIndexTxt(type, xkb, &new), TypeTxt(type));
return False;
}
new.preMods = rtrn.uval & 0xff;
new.preVMods = (rtrn.uval >> 16) & 0xffff;
if ((new.preMods & (~new.indexMods))
|| (new.preVMods && (~new.indexVMods)))
{
if (warningLevel > 0)
{
WARN2("Illegal value for preserve[%s] in type %s\n",
PreserveTxt(type, xkb, &new), TypeTxt(type));
ACTION1("Converted %s to ", PreserveIndexTxt(type, xkb, &new));
}
new.preMods &= new.indexMods;
new.preVMods &= new.indexVMods;
if (warningLevel > 0)
{
INFO1("%s\n", PreserveIndexTxt(type, xkb, &new));
}
}
return AddPreserve(xkb, type, &new, True, True);
}
Bool
AddLevelName(KeyTypeInfo * type,
unsigned level, Atom name, Bool clobber, Bool report)
{
if ((type->lvlNames == NULL) || (type->szNames <= level))
{
type->lvlNames =
uTypedRecalloc(type->lvlNames, type->szNames, level + 1, Atom);
if (type->lvlNames == NULL)
{
ERROR1("Couldn't allocate level names for type %s\n",
TypeTxt(type));
ACTION("Level names lost\n");
type->szNames = 0;
return False;
}
type->szNames = level + 1;
}
else if (type->lvlNames[level] == name)
{
if (warningLevel > 9)
{
WARN2("Duplicate names for level %d of key type %s\n",
level + 1, TypeTxt(type));
ACTION("Ignored\n");
}
return True;
}
else if (type->lvlNames[level] != None)
{
if (warningLevel > 0)
{
char *old, *new;
old = XkbAtomText(type->dpy, type->lvlNames[level], XkbMessage);
new = XkbAtomText(type->dpy, name, XkbMessage);
WARN2("Multiple names for level %d of key type %s\n",
level + 1, TypeTxt(type));
if (clobber)
ACTION2("Using %s, ignoring %s\n", new, old);
else
ACTION2("Using %s, ignoring %s\n", old, new);
}
if (!clobber)
return True;
}
if (level >= type->numLevels)
type->numLevels = level + 1;
type->lvlNames[level] = name;
return True;
}
static Bool
SetLevelName(KeyTypeInfo * type, ExprDef * arrayNdx, ExprDef * value)
{
ExprResult rtrn;
unsigned level;
if (arrayNdx == NULL)
return ReportTypeShouldBeArray(type, "level name");
if (!ExprResolveInteger(arrayNdx, &rtrn, SimpleLookup, (XPointer) lnames))
return ReportTypeBadType(type, "level name", "integer");
if ((rtrn.ival < 1) || (rtrn.ival > XkbMaxShiftLevel + 1))
{
ERROR3("Level name %d out of range (1..%d) in key type %s\n",
rtrn.ival,
XkbMaxShiftLevel + 1,
XkbAtomText(type->dpy, type->name, XkbMessage));
ACTION("Ignoring illegal level name definition\n");
return False;
}
level = rtrn.ival - 1;
if (!ExprResolveString(value, &rtrn, NULL, NULL))
{
ERROR2("Non-string name for level %d in key type %s\n", level + 1,
XkbAtomText(type->dpy, type->name, XkbMessage));
ACTION("Ignoring illegal level name definition\n");
return False;
}
return
AddLevelName(type, level, XkbInternAtom(NULL, rtrn.str, False), True,
True);
}
static Bool
SetKeyTypeField(KeyTypeInfo * type,
XkbDescPtr xkb,
char *field,
ExprDef * arrayNdx, ExprDef * value, KeyTypesInfo * info)
{
ExprResult tmp;
if (uStrCaseCmp(field, "modifiers") == 0)
{
unsigned mods, vmods;
if (arrayNdx != NULL)
{
WARN("The modifiers field of a key type is not an array\n");
ACTION("Illegal array subscript ignored\n");
}
if (!ExprResolveModMask(value, &tmp, LookupVModMask, (XPointer) xkb))
{
ERROR("Key type mask field must be a modifier mask\n");
ACTION("Key type definition ignored\n");
return False;
}
mods = tmp.uval & 0xff;
vmods = (tmp.uval >> 8) & 0xffff;
if (type->defs.defined & _KT_Mask)
{
WARN1("Multiple modifier mask definitions for key type %s\n",
XkbAtomText(type->dpy, type->name, XkbMessage));
ACTION1("Using %s, ", TypeMaskTxt(type, xkb));
INFO1("ignoring %s\n", XkbVModMaskText(type->dpy, xkb, mods,
vmods, XkbMessage));
return False;
}
type->mask = mods;
type->vmask = vmods;
type->defs.defined |= _KT_Mask;
return True;
}
else if (uStrCaseCmp(field, "map") == 0)
{
type->defs.defined |= _KT_Map;
return SetMapEntry(type, xkb, arrayNdx, value);
}
else if (uStrCaseCmp(field, "preserve") == 0)
{
type->defs.defined |= _KT_Preserve;
return SetPreserve(type, xkb, arrayNdx, value);
}
else if ((uStrCaseCmp(field, "levelname") == 0) ||
(uStrCaseCmp(field, "level_name") == 0))
{
type->defs.defined |= _KT_LevelNames;
return SetLevelName(type, arrayNdx, value);
}
ERROR2("Unknown field %s in key type %s\n", field, TypeTxt(type));
ACTION("Definition ignored\n");
return False;
}
static Bool
HandleKeyTypeVar(VarDef * stmt, XkbDescPtr xkb, KeyTypesInfo * info)
{
ExprResult elem, field;
ExprDef *arrayNdx;
if (!ExprResolveLhs(stmt->name, &elem, &field, &arrayNdx))
return False;
if (elem.str && (uStrCaseCmp(elem.str, "type") == 0))
return SetKeyTypeField(&info->dflt, xkb, field.str, arrayNdx,
stmt->value, info);
if (elem.str != NULL)
{
ERROR1("Default for unknown element %s\n", uStringText(elem.str));
ACTION1("Value for field %s ignored\n", uStringText(field.str));
}
else if (field.str != NULL)
{
ERROR1("Default defined for unknown field %s\n",
uStringText(field.str));
ACTION("Ignored\n");
}
return False;
}
static int
HandleKeyTypeBody(VarDef * def,
XkbDescPtr xkb, KeyTypeInfo * type, KeyTypesInfo * info)
{
int ok = 1;
ExprResult tmp, field;
ExprDef *arrayNdx;
for (; def != NULL; def = (VarDef *) def->common.next)
{
if ((def->name) && (def->name->type == ExprFieldRef))
{
ok = HandleKeyTypeVar(def, xkb, info);
continue;
}
ok = ExprResolveLhs(def->name, &tmp, &field, &arrayNdx);
if (ok)
ok = SetKeyTypeField(type, xkb, field.str, arrayNdx, def->value,
info);
}
return ok;
}
static int
HandleKeyTypeDef(KeyTypeDef * def,
XkbDescPtr xkb, unsigned merge, KeyTypesInfo * info)
{
register int i;
KeyTypeInfo type;
if (def->merge != MergeDefault)
merge = def->merge;
type.defs.defined = 0;
type.defs.fileID = info->fileID;
type.defs.merge = merge;
type.defs.next = NULL;
type.dpy = info->dpy;
type.name = def->name;
type.mask = info->dflt.mask;
type.vmask = info->dflt.vmask;
type.groupInfo = info->dflt.groupInfo;
type.numLevels = 1;
type.nEntries = type.szEntries = 0;
type.entries = NULL;
type.szNames = 0;
type.lvlNames = NULL;
type.preserve = NULL;
if (!HandleKeyTypeBody(def->body, xkb, &type, info))
{
info->errorCount++;
return False;
}
for (i = 0; i < info->dflt.nEntries; i++)
{
XkbKTMapEntryPtr dflt;
dflt = &info->dflt.entries[i];
if (((dflt->mods.real_mods & type.mask) == dflt->mods.real_mods) &&
((dflt->mods.vmods & type.vmask) == dflt->mods.vmods))
{
AddMapEntry(xkb, &type, dflt, False, False);
}
}
if (info->dflt.preserve)
{
PreserveInfo *dflt = info->dflt.preserve;
while (dflt)
{
if (((dflt->indexMods & type.mask) == dflt->indexMods) &&
((dflt->indexVMods & type.vmask) == dflt->indexVMods))
{
AddPreserve(xkb, &type, dflt, False, False);
}
dflt = (PreserveInfo *) dflt->defs.next;
}
}
for (i = 0; i < info->dflt.szNames; i++)
{
if ((i < type.numLevels) && (info->dflt.lvlNames[i] != None))
{
AddLevelName(&type, i, info->dflt.lvlNames[i], False, False);
}
}
if (!AddKeyType(xkb, info, &type))
{
info->errorCount++;
return False;
}
return True;
}
static void
HandleKeyTypesFile(XkbFile * file,
XkbDescPtr xkb, unsigned merge, KeyTypesInfo * info)
{
ParseCommon *stmt;
info->name = uStringDup(file->name);
stmt = file->defs;
while (stmt)
{
switch (stmt->stmtType)
{
case StmtInclude:
if (!HandleIncludeKeyTypes((IncludeStmt *) stmt, xkb, info,
HandleKeyTypesFile))
info->errorCount++;
break;
case StmtKeyTypeDef:
if (!HandleKeyTypeDef((KeyTypeDef *) stmt, xkb, merge, info))
info->errorCount++;
break;
case StmtVarDef:
if (!HandleKeyTypeVar((VarDef *) stmt, xkb, info))
info->errorCount++;
break;
case StmtVModDef:
if (!HandleVModDef((VModDef *) stmt, merge, &info->vmods))
info->errorCount++;
break;
case StmtKeyAliasDef:
ERROR("Key type files may not include other declarations\n");
ACTION("Ignoring definition of key alias\n");
info->errorCount++;
break;
case StmtKeycodeDef:
ERROR("Key type files may not include other declarations\n");
ACTION("Ignoring definition of key name\n");
info->errorCount++;
break;
case StmtInterpDef:
ERROR("Key type files may not include other declarations\n");
ACTION("Ignoring definition of symbol interpretation\n");
info->errorCount++;
break;
default:
WSGO1("Unexpected statement type %d in HandleKeyTypesFile\n",
stmt->stmtType);
break;
}
stmt = stmt->next;
if (info->errorCount > 10)
{
#ifdef NOISY
ERROR("Too many errors\n");
#endif
ACTION1("Abandoning keytypes file \"%s\"\n", file->topName);
break;
}
}
return;
}
static Bool
CopyDefToKeyType(XkbDescPtr xkb, XkbKeyTypePtr type, KeyTypeInfo * def)
{
register int i;
PreserveInfo *pre;
for (pre = def->preserve; pre != NULL;
pre = (PreserveInfo *) pre->defs.next)
{
XkbKTMapEntryPtr match;
XkbKTMapEntryRec tmp;
tmp.mods.real_mods = pre->indexMods;
tmp.mods.vmods = pre->indexVMods;
tmp.level = 0;
AddMapEntry(xkb, def, &tmp, False, False);
match = FindMatchingMapEntry(def, pre->indexMods, pre->indexVMods);
if (!match)
{
WSGO("Couldn't find matching entry for preserve\n");
ACTION("Aborting\n");
return False;
}
pre->matchingMapIndex = match - def->entries;
}
type->mods.real_mods = def->mask;
type->mods.vmods = def->vmask;
type->num_levels = def->numLevels;
type->map_count = def->nEntries;
type->map = def->entries;
if (def->preserve)
{
type->preserve = uTypedCalloc(type->map_count, XkbModsRec);
if (!type->preserve)
{
WARN("Couldn't allocate preserve array in CopyDefToKeyType\n");
ACTION1("Preserve setting for type %s lost\n",
XkbAtomText(def->dpy, def->name, XkbMessage));
}
else
{
pre = def->preserve;
for (; pre != NULL; pre = (PreserveInfo *) pre->defs.next)
{
int ndx = pre->matchingMapIndex;
type->preserve[ndx].mask = pre->preMods;
type->preserve[ndx].real_mods = pre->preMods;
type->preserve[ndx].vmods = pre->preVMods;
}
}
}
else
type->preserve = NULL;
type->name = (Atom) def->name;
if (def->szNames > 0)
{
type->level_names = uTypedCalloc(def->numLevels, Atom);
for (i = 0; i < def->szNames; i++)
{
type->level_names[i] = (Atom) def->lvlNames[i];
}
}
else
{
type->level_names = NULL;
}
def->nEntries = def->szEntries = 0;
def->entries = NULL;
return XkbComputeEffectiveMap(xkb, type, NULL);
}
Bool
CompileKeyTypes(XkbFile * file, XkbFileInfo * result, unsigned merge)
{
KeyTypesInfo info;
XkbDescPtr xkb;
xkb = result->xkb;
InitKeyTypesInfo(&info, xkb, NULL);
info.fileID = file->id;
HandleKeyTypesFile(file, xkb, merge, &info);
if (info.errorCount == 0)
{
register int i;
register KeyTypeInfo *def;
register XkbKeyTypePtr type, next;
if (info.name != NULL)
{
if (XkbAllocNames(xkb, XkbTypesNameMask, 0, 0) == Success)
xkb->names->types = XkbInternAtom(xkb->dpy, info.name, False);
else
{
WSGO("Couldn't allocate space for types name\n");
ACTION2("Name \"%s\" (from %s) NOT assigned\n",
scanFile, info.name);
}
}
i = info.nTypes;
if ((info.stdPresent & XkbOneLevelMask) == 0)
i++;
if ((info.stdPresent & XkbTwoLevelMask) == 0)
i++;
if ((info.stdPresent & XkbKeypadMask) == 0)
i++;
if ((info.stdPresent & XkbAlphabeticMask) == 0)
i++;
if (XkbAllocClientMap(xkb, XkbKeyTypesMask, i) != Success)
{
WSGO("Couldn't allocate client map\n");
ACTION("Exiting\n");
return False;
}
xkb->map->num_types = i;
if (XkbAllRequiredTypes & (~info.stdPresent))
{
unsigned missing, keypadVMod;
missing = XkbAllRequiredTypes & (~info.stdPresent);
keypadVMod = FindKeypadVMod(xkb);
if (XkbInitCanonicalKeyTypes(xkb, missing, keypadVMod) != Success)
{
WSGO("Couldn't initialize canonical key types\n");
ACTION("Exiting\n");
return False;
}
if (missing & XkbOneLevelMask)
xkb->map->types[XkbOneLevelIndex].name = tok_ONE_LEVEL;
if (missing & XkbTwoLevelMask)
xkb->map->types[XkbTwoLevelIndex].name = tok_TWO_LEVEL;
if (missing & XkbAlphabeticMask)
xkb->map->types[XkbAlphabeticIndex].name = tok_ALPHABETIC;
if (missing & XkbKeypadMask)
xkb->map->types[XkbKeypadIndex].name = tok_KEYPAD;
}
next = &xkb->map->types[XkbLastRequiredType + 1];
for (i = 0, def = info.types; i < info.nTypes; i++)
{
if (def->name == tok_ONE_LEVEL)
type = &xkb->map->types[XkbOneLevelIndex];
else if (def->name == tok_TWO_LEVEL)
type = &xkb->map->types[XkbTwoLevelIndex];
else if (def->name == tok_ALPHABETIC)
type = &xkb->map->types[XkbAlphabeticIndex];
else if (def->name == tok_KEYPAD)
type = &xkb->map->types[XkbKeypadIndex];
else
type = next++;
DeleteLevel1MapEntries(def);
if (!CopyDefToKeyType(xkb, type, def))
return False;
def = (KeyTypeInfo *) def->defs.next;
}
return True;
}
return False;
}