#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;
int errorCount;
unsigned fileID;
unsigned merge;
int computedMin;
int computedMax;
int explicitMin;
int explicitMax;
int effectiveMin;
int effectiveMax;
unsigned long names[XkbMaxLegalKeyCode+1];
unsigned files[XkbMaxLegalKeyCode+1];
unsigned char has_alt_forms[XkbMaxLegalKeyCode+1];
IndicatorNameInfo * leds;
AliasInfo * aliases;
} KeyNamesInfo;
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;
char * action;
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;
action= "Using %s instead of %s\n";
}
else {
action= "Identical definitions ignored\n";
}
ACTION2(action,(old->virtual?"virtual":"real"),
(old->virtual?"real":"virtual"));
return True;
}
else {
if (replace) action= "Ignoring %d, using %d\n";
else action= "Using %d, ignoring %d\n";
ACTION2(action,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 {
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",new->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;
}
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;
}
typedef void (*FileHandler)(
XkbFile * ,
XkbDescPtr ,
unsigned ,
KeyNamesInfo *
);
static Bool
HandleIncludeKeycodes( IncludeStmt * stmt,
XkbDescPtr xkb,
KeyNamesInfo * info,
FileHandler hndlr)
{
unsigned newMerge;
XkbFile * rtrn;
KeyNamesInfo included;
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);
}
else if (ProcessIncludeFile(stmt,XkmKeyNamesIndex,&rtrn,&newMerge)) {
InitKeyNamesInfo(&included);
(*hndlr)(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;
return False;
}
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);
(*hndlr)(rtrn,xkb,MergeOverride,&next_incl);
MergeIncludedKeycodes(&included,&next_incl,op);
ClearKeyNamesInfo(&next_incl);
}
else {
info->errorCount+= 10;
return False;
}
}
}
if (haveSelf)
*info= included;
else {
MergeIncludedKeycodes(info,&included,newMerge);
ClearKeyNamesInfo(&included);
}
return (info->errorCount==0);
}
static int
HandleKeycodeDef( KeycodeDef * stmt,
XkbDescPtr xkb,
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
static int
HandleKeyNameVar(VarDef *stmt,XkbDescPtr xkb,unsigned merge,KeyNamesInfo *info)
{
ExprResult tmp,field;
ExprDef * arrayNdx;
int which;
if (ExprResolveLhs(stmt->name,&tmp,&field,&arrayNdx)==0)
return 0;
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,
XkbDescPtr xkb,
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];
sprintf(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;
}
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:
if (!HandleIncludeKeycodes((IncludeStmt *)stmt,xkb,info,
HandleKeycodesFile))
info->errorCount++;
break;
case StmtKeycodeDef:
if (!HandleKeycodeDef((KeycodeDef *)stmt,xkb,merge,info))
info->errorCount++;
break;
case StmtKeyAliasDef:
if (!HandleAliasDef((KeyAliasDef *)stmt,
merge,info->fileID,&info->aliases))
info->errorCount++;
break;
case StmtVarDef:
if (!HandleKeyNameVar((VarDef *)stmt,xkb,merge,info))
info->errorCount++;
break;
case StmtIndicatorNameDef:
if (!HandleIndicatorNameDef((IndicatorNameDef *)stmt,xkb,
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;
}
Bool
CompileKeycodes(XkbFile *file,XkbFileInfo *result,unsigned merge)
{
KeyNamesInfo info;
XkbDescPtr xkb;
xkb= result->xkb;
InitKeyNamesInfo(&info);
HandleKeycodesFile(file,xkb,merge,&info);
if (info.errorCount==0) {
if (info.explicitMin>0)
xkb->min_key_code= info.effectiveMin;
else xkb->min_key_code= info.computedMin;
if (info.explicitMax>0)
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;
}