#include "xkbcomp.h"
#include "misc.h"
#include "tokens.h"
#include "expr.h"
#include "vmod.h"
#include "indicators.h"
#include "action.h"
#include "compat.h"
#define ReportIndicatorBadType(d,l,f,w) \
ReportBadType("indicator map",(f),\
XkbAtomText((d),(l)->name,XkbMessage),(w))
#define ReportIndicatorNotArray(d,l,f) \
ReportNotArray("indicator map",(f),\
XkbAtomText((d),(l)->name,XkbMessage))
void
ClearIndicatorMapInfo(Display *dpy,LEDInfo *info)
{
info->name= XkbInternAtom(dpy,"default",False);
info->indicator= _LED_NotBound;
info->flags= info->which_mods= info->real_mods= 0;
info->vmods= 0;
info->which_groups= info->groups= 0;
info->ctrls= 0;
return;
}
LEDInfo *
AddIndicatorMap(LEDInfo *oldLEDs,LEDInfo *new)
{
LEDInfo *old,*last;
unsigned collide;
last= NULL;
for (old=oldLEDs;old!=NULL;old=(LEDInfo *)old->defs.next) {
if (old->name==new->name) {
if ((old->real_mods==new->real_mods)&&
(old->vmods==new->vmods)&&
(old->groups==new->groups)&&
(old->ctrls==new->ctrls)&&
(old->which_mods==new->which_mods)&&
(old->which_groups==new->which_groups)) {
old->defs.defined|= new->defs.defined;
return oldLEDs;
}
if (new->defs.merge==MergeReplace) {
CommonInfo *next= old->defs.next;
if (((old->defs.fileID==new->defs.fileID)&&(warningLevel>0))||
(warningLevel>9)) {
WARN1("Map for indicator %s redefined\n",
XkbAtomText(NULL,old->name,XkbMessage));
ACTION("Earlier definition ignored\n");
}
*old= *new;
old->defs.next= next;
return oldLEDs;
}
collide= 0;
if (UseNewField(_LED_Index,&old->defs,&new->defs,&collide)) {
old->indicator= new->indicator;
old->defs.defined|= _LED_Index;
}
if (UseNewField(_LED_Mods,&old->defs,&new->defs,&collide)) {
old->which_mods= new->which_mods;
old->real_mods= new->real_mods;
old->vmods= new->vmods;
old->defs.defined|= _LED_Mods;
}
if (UseNewField(_LED_Groups,&old->defs,&new->defs,&collide)) {
old->which_groups= new->which_groups;
old->groups= new->groups;
old->defs.defined|= _LED_Groups;
}
if (UseNewField(_LED_Ctrls,&old->defs,&new->defs,&collide)) {
old->ctrls= new->ctrls;
old->defs.defined|= _LED_Ctrls;
}
if (UseNewField(_LED_Explicit,&old->defs,&new->defs,&collide)) {
old->flags&= ~XkbIM_NoExplicit;
old->flags|= (new->flags&XkbIM_NoExplicit);
old->defs.defined|= _LED_Explicit;
}
if (UseNewField(_LED_Automatic,&old->defs,&new->defs,&collide)) {
old->flags&= ~XkbIM_NoAutomatic;
old->flags|= (new->flags&XkbIM_NoAutomatic);
old->defs.defined|= _LED_Automatic;
}
if (UseNewField(_LED_DrivesKbd,&old->defs,&new->defs,&collide)) {
old->flags&= ~XkbIM_LEDDrivesKB;
old->flags|= (new->flags&XkbIM_LEDDrivesKB);
old->defs.defined|= _LED_DrivesKbd;
}
if (collide) {
WARN1("Map for indicator %s redefined\n",
XkbAtomText(NULL,old->name,XkbMessage));
ACTION1("Using %s definition for duplicate fields\n",
(new->defs.merge==MergeAugment?"first":"last"));
}
return oldLEDs;
}
if (old->defs.next==NULL)
last= old;
}
old= uTypedAlloc(LEDInfo);
if (!old) {
WSGO("Couldn't allocate indicator map\n");
ACTION1("Map for indicator %s not compiled\n",
XkbAtomText(NULL,new->name,XkbMessage));
return False;
}
*old= *new;
old->defs.next= NULL;
if (last) {
last->defs.next= &old->defs;
return oldLEDs;
}
return old;
}
LookupEntry modComponentNames[] = {
{ "base", XkbIM_UseBase },
{ "latched", XkbIM_UseLatched },
{ "locked", XkbIM_UseLocked },
{ "effective", XkbIM_UseEffective },
{ "compat", XkbIM_UseCompat },
{ "any", XkbIM_UseAnyMods },
{ "none", 0 },
{ NULL, 0 }
};
LookupEntry groupComponentNames[] = {
{ "base", XkbIM_UseBase },
{ "latched", XkbIM_UseLatched },
{ "locked", XkbIM_UseLocked },
{ "effective", XkbIM_UseEffective },
{ "any", XkbIM_UseAnyGroup },
{ "none", 0 },
{ NULL, 0 }
};
int
SetIndicatorMapField( LEDInfo * led,
XkbDescPtr xkb,
char * field,
ExprDef * arrayNdx,
ExprDef * value)
{
ExprResult rtrn;
Bool ok;
ok= True;
if ((uStrCaseCmp(field,"modifiers")==0)||(uStrCaseCmp(field,"mods")==0)) {
if (arrayNdx!=NULL)
return ReportIndicatorNotArray(xkb->dpy,led,field);
if (!ExprResolveModMask(value,&rtrn,LookupVModMask,(XPointer)xkb))
return ReportIndicatorBadType(xkb->dpy,led,field,"modifier mask");
led->real_mods= rtrn.uval&0xff;
led->vmods= (rtrn.uval>>8)&0xff;
led->defs.defined|= _LED_Mods;
}
else if (uStrCaseCmp(field,"groups")==0) {
if (arrayNdx!=NULL)
return ReportIndicatorNotArray(xkb->dpy,led,field);
if (!ExprResolveMask(value,&rtrn,SimpleLookup,(XPointer)groupNames))
return ReportIndicatorBadType(xkb->dpy,led,field,"group mask");
led->groups= rtrn.uval;
led->defs.defined|= _LED_Groups;
}
else if ((uStrCaseCmp(field,"controls")==0)||
(uStrCaseCmp(field,"ctrls")==0)) {
if (arrayNdx!=NULL)
return ReportIndicatorNotArray(xkb->dpy,led,field);
if (!ExprResolveMask(value,&rtrn,SimpleLookup,(XPointer)ctrlNames))
return ReportIndicatorBadType(xkb->dpy,led,field,"controls mask");
led->ctrls= rtrn.uval;
led->defs.defined|= _LED_Ctrls;
}
else if (uStrCaseCmp(field,"allowexplicit")==0) {
if (arrayNdx!=NULL)
return ReportIndicatorNotArray(xkb->dpy,led,field);
if (!ExprResolveBoolean(value,&rtrn,NULL,NULL))
return ReportIndicatorBadType(xkb->dpy,led,field,"boolean");
if (rtrn.uval) led->flags&= ~XkbIM_NoExplicit;
else led->flags|= XkbIM_NoExplicit;
led->defs.defined|= _LED_Explicit;
}
else if ((uStrCaseCmp(field,"whichmodstate")==0)||
(uStrCaseCmp(field,"whichmodifierstate")==0)) {
if (arrayNdx!=NULL)
return ReportIndicatorNotArray(xkb->dpy,led,field);
if (!ExprResolveMask(value,&rtrn,SimpleLookup,
(XPointer)modComponentNames)) {
return ReportIndicatorBadType(xkb->dpy,led,field,
"mask of modifier state components");
}
led->which_mods= rtrn.uval;
}
else if (uStrCaseCmp(field,"whichgroupstate")==0) {
if (arrayNdx!=NULL)
return ReportIndicatorNotArray(xkb->dpy,led,field);
if (!ExprResolveMask(value,&rtrn,SimpleLookup,
(XPointer)groupComponentNames)){
return ReportIndicatorBadType(xkb->dpy,led,field,
"mask of group state components");
}
led->which_groups= rtrn.uval;
}
else if ((uStrCaseCmp(field,"driveskbd")==0)||
(uStrCaseCmp(field,"driveskeyboard")==0)||
(uStrCaseCmp(field,"leddriveskbd")==0)||
(uStrCaseCmp(field,"leddriveskeyboard")==0)||
(uStrCaseCmp(field,"indicatordriveskbd")==0)||
(uStrCaseCmp(field,"indicatordriveskeyboard")==0)) {
if (arrayNdx!=NULL)
return ReportIndicatorNotArray(xkb->dpy,led,field);
if (!ExprResolveBoolean(value,&rtrn,NULL,NULL))
return ReportIndicatorBadType(xkb->dpy,led,field,"boolean");
if (rtrn.uval) led->flags|= XkbIM_LEDDrivesKB;
else led->flags&= ~XkbIM_LEDDrivesKB;
led->defs.defined|= _LED_DrivesKbd;
}
else if (uStrCaseCmp(field,"index")==0) {
if (arrayNdx!=NULL)
return ReportIndicatorNotArray(xkb->dpy,led,field);
if (!ExprResolveInteger(value,&rtrn,NULL,NULL))
return ReportIndicatorBadType(xkb->dpy,led,field,"indicator index");
if ((rtrn.uval<1)||(rtrn.uval>32)) {
ERROR2("Illegal indicator index %d (range 1..%d)\n",rtrn.uval,
XkbNumIndicators);
ACTION1("Index definition for %s indicator ignored\n",
XkbAtomText(NULL,led->name,XkbMessage));
return False;
}
led->indicator= rtrn.uval;
led->defs.defined|= _LED_Index;
}
else {
ERROR2("Unknown field %s in map for %s indicator\n",field,
XkbAtomText(NULL,led->name,XkbMessage));
ACTION("Definition ignored\n");
ok= False;
}
return ok;
}
LEDInfo *
HandleIndicatorMapDef( IndicatorMapDef * def,
XkbDescPtr xkb,
LEDInfo * dflt,
LEDInfo * oldLEDs,
unsigned merge)
{
LEDInfo led,*rtrn;
VarDef * var;
Bool ok;
if (def->merge!=MergeDefault)
merge= def->merge;
led= *dflt;
led.defs.merge= merge;
led.name= def->name;
ok= True;
for (var= def->body;var!=NULL;var= (VarDef *)var->common.next) {
ExprResult elem,field;
ExprDef * arrayNdx;
if (!ExprResolveLhs(var->name,&elem,&field,&arrayNdx)) {
ok= False;
continue;
}
if (elem.str!=NULL) {
ERROR1("Cannot set defaults for \"%s\" element in indicator map\n",
elem.str);
ACTION2("Assignment to %s.%s ignored\n",elem.str,field.str);
ok= False;
}
else {
ok=SetIndicatorMapField(&led,xkb,field.str,arrayNdx,var->value)&&ok;
}
}
if (ok) {
rtrn= AddIndicatorMap(oldLEDs,&led);
return rtrn;
}
return NULL;
}
Bool
CopyIndicatorMapDefs(XkbFileInfo *result,LEDInfo *leds,LEDInfo **unboundRtrn)
{
LEDInfo * led,*next;
LEDInfo * unbound,*last;
XkbDescPtr xkb;
xkb= result->xkb;
if (XkbAllocNames(xkb,XkbIndicatorNamesMask,0,0)!=Success) {
WSGO("Couldn't allocate names\n");
ACTION("Indicator names may be incorrect\n");
}
if (XkbAllocIndicatorMaps(xkb)!=Success) {
WSGO("Can't allocate indicator maps\n");
ACTION("Indicator map definitions may be lost\n");
return False;
}
last= unbound= (unboundRtrn?*unboundRtrn:NULL);
while ((last!=NULL) && (last->defs.next!=NULL)) {
last= (LEDInfo *)last->defs.next;
}
for (led=leds;led!=NULL;led=next) {
next= (LEDInfo *)led->defs.next;
if ((led->groups!=0)&&(led->which_groups==0))
led->which_groups= XkbIM_UseEffective;
if ((led->which_mods==0)&&((led->real_mods)||(led->vmods)))
led->which_mods= XkbIM_UseEffective;
if ((led->indicator==_LED_NotBound)||(!xkb->indicators)) {
if (unboundRtrn!=NULL) {
led->defs.next= NULL;
if (last!=NULL) last->defs.next= (CommonInfo *)led;
else unbound= led;
last= led;
}
else uFree(led);
}
else {
register XkbIndicatorMapPtr im;
im= &xkb->indicators->maps[led->indicator-1];
im->flags= led->flags;
im->which_groups= led->which_groups;
im->groups= led->groups;
im->which_mods= led->which_mods;
im->mods.mask= led->real_mods;
im->mods.real_mods= led->real_mods;
im->mods.vmods= led->vmods;
im->ctrls= led->ctrls;
if (xkb->names!=NULL)
xkb->names->indicators[led->indicator-1]= led->name;
uFree(led);
}
}
if (unboundRtrn!=NULL) {
*unboundRtrn= unbound;
}
return True;
}
Bool
BindIndicators( XkbFileInfo * result,
Bool force,
LEDInfo * unbound,
LEDInfo ** unboundRtrn)
{
XkbDescPtr xkb;
register int i;
register LEDInfo *led,*next,*last;
xkb= result->xkb;
if (xkb->names!=NULL) {
for (led=unbound;led!=NULL;led= (LEDInfo *)led->defs.next) {
if (led->indicator==_LED_NotBound) {
for (i=0;i<XkbNumIndicators;i++) {
if (xkb->names->indicators[i]==led->name) {
led->indicator= i+1;
break;
}
}
}
}
if (force) {
for (led=unbound;led!=NULL;led= (LEDInfo *)led->defs.next) {
if (led->indicator==_LED_NotBound) {
for (i=0;i<XkbNumIndicators;i++) {
if (xkb->names->indicators[i]==None) {
xkb->names->indicators[i]= led->name;
led->indicator= i+1;
xkb->indicators->phys_indicators&= ~(1<<i);
break;
}
}
if (led->indicator==_LED_NotBound) {
ERROR("No unnamed indicators found\n");
ACTION1("Virtual indicator map \"%s\" not bound\n",
XkbAtomGetString(xkb->dpy,led->name));
continue;
}
}
}
}
}
for (last=NULL,led=unbound;led!=NULL;led= next) {
next= (LEDInfo *)led->defs.next;
if (led->indicator==_LED_NotBound) {
if (force) {
unbound= next;
uFree(led);
}
else {
if (last)
last->defs.next= &led->defs;
else unbound= led;
last= led;
}
}
else {
if ((xkb->names!=NULL)&&
(xkb->names->indicators[led->indicator-1]!=led->name)) {
Atom old= xkb->names->indicators[led->indicator-1];
ERROR1("Multiple names bound to indicator %d\n",(unsigned int)led->indicator);
ACTION2("Using %s, ignoring %s\n",
XkbAtomGetString(xkb->dpy,old),
XkbAtomGetString(xkb->dpy,led->name));
led->indicator= _LED_NotBound;
if (force) {
uFree(led);
unbound= next;
}
else {
if (last)
last->defs.next= &led->defs;
else unbound= led;
last= led;
}
}
else {
XkbIndicatorMapPtr map;
map= &xkb->indicators->maps[led->indicator-1];
map->flags= led->flags;
map->which_groups= led->which_groups;
map->groups= led->groups;
map->which_mods= led->which_mods;
map->mods.mask= led->real_mods;
map->mods.real_mods= led->real_mods;
map->mods.vmods= led->vmods;
map->ctrls= led->ctrls;
if (last) last->defs.next= &next->defs;
else unbound= next;
led->defs.next= NULL;
uFree(led);
}
}
}
if (unboundRtrn) {
*unboundRtrn= unbound;
}
else if (unbound) {
for (led=unbound;led!=NULL;led=next) {
next= (LEDInfo *)led->defs.next;
uFree(led);
}
}
return True;
}