/* $Xorg: xkbUtils.c,v 1.3 2000/08/17 19:53:48 cpqbld Exp $ */
/************************************************************
Copyright (c) 1993 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.

********************************************************/
/* $XFree86: xc/programs/Xserver/xkb/xkbUtils.c,v 3.15 2003/07/16 01:39:11 dawes Exp $ */

#include <stdio.h>
#include <ctype.h>
#include <math.h>
#define NEED_EVENTS 1
#include <X11/X.h>
#include <X11/Xproto.h>
#define	XK_CYRILLIC
#include <X11/keysym.h>
#include "misc.h"
#include "inputstr.h"

#define	XKBSRV_NEED_FILE_FUNCS
#include "XKBsrv.h"
#include "extensions/XKBgeom.h"
#include "xkb.h"

#ifdef MODE_SWITCH
extern Bool noKME; /* defined in os/utils.c */
#endif

int	XkbDisableLockActions = 0;

/***====================================================================***/

#ifndef RETURN_SHOULD_REPEAT
#if (defined(__osf__) && defined(__alpha))
#define RETURN_SHOULD_REPEAT 1
#else
#define	RETURN_SHOULD_REPEAT 0
#endif
#endif

/***====================================================================***/

DeviceIntPtr
#if NeedFunctionPrototypes
_XkbLookupAnyDevice(int id,int *why_rtrn)
#else
_XkbLookupAnyDevice(id,why_rtrn)
    int id;
    int *why_rtrn;
#endif
{
DeviceIntPtr dev = NULL;

    dev= (DeviceIntPtr)LookupKeyboardDevice();
    if ((id==XkbUseCoreKbd)||(dev->id==id))
	return dev;

    dev= (DeviceIntPtr)LookupPointerDevice();
    if ((id==XkbUseCorePtr)||(dev->id==id))
	return dev;

    if (id&(~0xff))
	 dev = NULL;

    dev= (DeviceIntPtr)LookupDevice(id);
    if (dev!=NULL) 
	return dev;
    if ((!dev)&&(why_rtrn))
	*why_rtrn= XkbErr_BadDevice;
    return dev;
}

DeviceIntPtr
#if NeedFunctionPrototypes
_XkbLookupKeyboard(int id,int *why_rtrn)
#else
_XkbLookupKeyboard(id,why_rtrn)
    int id;
    int *why_rtrn;
#endif
{
DeviceIntPtr dev = NULL;

    if ((dev= _XkbLookupAnyDevice(id,why_rtrn))==NULL)
	return NULL;
    else if ((!dev->key)||(!dev->key->xkbInfo)) {
	if (why_rtrn)
	   *why_rtrn= XkbErr_BadClass;
	return NULL;
    }
    return dev;
}

DeviceIntPtr
#if NeedFunctionPrototypes
_XkbLookupBellDevice(int id,int *why_rtrn)
#else
_XkbLookupBellDevice(id,why_rtrn)
    int id;
    int *why_rtrn;
#endif
{
DeviceIntPtr dev = NULL;

    if ((dev= _XkbLookupAnyDevice(id,why_rtrn))==NULL)
	return NULL;
    else if ((!dev->kbdfeed)&&(!dev->bell)) {
	if (why_rtrn)
	   *why_rtrn= XkbErr_BadClass;
	return NULL;
    }
    return dev;
}

DeviceIntPtr
#if NeedFunctionPrototypes
_XkbLookupLedDevice(int id,int *why_rtrn)
#else
_XkbLookupLedDevice(id,why_rtrn)
    int id;
    int *why_rtrn;
#endif
{
DeviceIntPtr dev = NULL;

    if ((dev= _XkbLookupAnyDevice(id,why_rtrn))==NULL)
	return NULL;
    else if ((!dev->kbdfeed)&&(!dev->leds)) {
	if (why_rtrn)
	   *why_rtrn= XkbErr_BadClass;
	return NULL;
    }
    return dev;
}

DeviceIntPtr
#if NeedFunctionPrototypes
_XkbLookupButtonDevice(int id,int *why_rtrn)
#else
_XkbLookupButtonDevice(id,why_rtrn)
    int id;
    int *why_rtrn;
#endif
{
DeviceIntPtr dev = NULL;

    if ((dev= _XkbLookupAnyDevice(id,why_rtrn))==NULL)
	return NULL;
    else if (!dev->button) {
	if (why_rtrn)
	   *why_rtrn= XkbErr_BadClass;
	return NULL;
    }
    return dev;
}

void
#if NeedFunctionPrototypes
XkbSetActionKeyMods(XkbDescPtr xkb,XkbAction *act,unsigned mods)
#else
XkbSetActionKeyMods(xkb,act,mods)
    XkbDescPtr	xkb;
    XkbAction *	act;
    unsigned	mods;
#endif
{
register unsigned	tmp;

    switch (act->type) {
	case XkbSA_SetMods: case XkbSA_LatchMods: case XkbSA_LockMods:
	    if (act->mods.flags&XkbSA_UseModMapMods)
		act->mods.real_mods= act->mods.mask= mods;
	    if ((tmp= XkbModActionVMods(&act->mods))!=0)
		act->mods.mask|= XkbMaskForVMask(xkb,tmp);
	    break;
	case XkbSA_ISOLock:
	    if (act->iso.flags&XkbSA_UseModMapMods)
		act->iso.real_mods= act->iso.mask= mods;
	    if ((tmp= XkbModActionVMods(&act->iso))!=0)
		act->iso.mask|= XkbMaskForVMask(xkb,tmp);
	    break;
    }
    return;
}

unsigned
#if NeedFunctionPrototypes
XkbMaskForVMask(XkbDescPtr xkb,unsigned vmask)
#else
XkbMaskForVMask(xkb,vmask)
    XkbDescPtr	xkb;
    unsigned	vmask;
#endif
{
register int i,bit;
register unsigned mask;
    
    for (mask=i=0,bit=1;i<XkbNumVirtualMods;i++,bit<<=1) {
	if (vmask&bit)
	    mask|= xkb->server->vmods[i];
    }
    return mask;
}


Bool
#if NeedFunctionPrototypes
XkbApplyVModChanges(	XkbSrvInfoPtr		xkbi,
			unsigned		changed,
			XkbChangesPtr		changes,
			unsigned *		needChecksRtrn,
			XkbEventCausePtr	cause)
#else
XkbApplyVModChanges(xkbi,changed,changes,needChecksRtrn,cause)
    XkbSrvInfoPtr	xkbi;
    unsigned		changed;
    XkbChangesPtr	changes;
    unsigned *		needChecksRtrn;
    XkbEventCausePtr	cause;
#endif
{
XkbDescPtr		xkb;
Bool			check;

    xkb= xkbi->desc;
#ifdef DEBUG
{
register unsigned i,bit;
    for (i=0,bit=1;i<XkbNumVirtualMods;i++,bit<<=1) {
	if ((changed&bit)==0)
	    continue;
	if (xkbDebugFlags)
	    ErrorF("Should be applying: change vmod %d to 0x%x\n",i,
					xkb->server->vmods[i]);
    }
}
#endif
    check= XkbApplyVirtualModChanges(xkb,changed,changes);
    XkbApplyVModChangesToAllDevices(xkbi->device,xkb,changed,cause);

    if (needChecksRtrn!=NULL)  {
	if (check)
	     *needChecksRtrn= XkbStateNotifyMask|XkbIndicatorStateNotifyMask;
	else *needChecksRtrn= 0;
    }
    else if (check) {
	/* 7/12/95 (ef) -- XXX check compatibility and/or indicator state */
    }
    return 1;
}

/***====================================================================***/

#if NeedFunctionPrototypes
void
XkbUpdateKeyTypesFromCore(	DeviceIntPtr	pXDev,
				KeyCode	 	first,
				CARD8	 	num,
				XkbChangesPtr	changes)
#else
void
XkbUpdateKeyTypesFromCore(pXDev,first,num,changes)
    DeviceIntPtr pXDev;
    KeyCode first;
    CARD8 num;
    XkbChangesPtr changes;
#endif
{
XkbDescPtr		xkb;
unsigned		key,nG,explicit;
KeySymsPtr		pCore;
int			types[XkbNumKbdGroups];
KeySym			tsyms[XkbMaxSymsPerKey],*syms;
XkbMapChangesPtr	mc;

    xkb= pXDev->key->xkbInfo->desc;
#ifdef NOTYET
    if (first<xkb->min_key_code) {
	if (first>=XkbMinLegalKeyCode) {
	    xkb->min_key_code= first;
	    /* 1/12/95 (ef) -- XXX! should zero out the new maps */
	    changes->map.changed|= XkbKeycodesMask;
generate a NewKeyboard notify here?
	}
    }
#endif
    if (first+num-1>xkb->max_key_code) {
	/* 1/12/95 (ef) -- XXX! should allow XKB structures to grow */
	num= xkb->max_key_code-first+1;
    }

    mc= (changes?(&changes->map):NULL);

    pCore= &pXDev->key->curKeySyms;
    syms= &pCore->map[(first-xkb->min_key_code)*pCore->mapWidth];
    for (key=first; key<(first+num); key++,syms+= pCore->mapWidth) {
        explicit= xkb->server->explicit[key]&XkbExplicitKeyTypesMask;
        types[XkbGroup1Index]= XkbKeyKeyTypeIndex(xkb,key,XkbGroup1Index);
        types[XkbGroup2Index]= XkbKeyKeyTypeIndex(xkb,key,XkbGroup2Index);
        types[XkbGroup3Index]= XkbKeyKeyTypeIndex(xkb,key,XkbGroup3Index);
        types[XkbGroup4Index]= XkbKeyKeyTypeIndex(xkb,key,XkbGroup4Index);
        nG= XkbKeyTypesForCoreSymbols(xkb,pCore->mapWidth,syms,explicit,types,
									tsyms);
	XkbChangeTypesOfKey(xkb,key,nG,XkbAllGroupsMask,types,mc);
	memcpy((char *)XkbKeySymsPtr(xkb,key),(char *)tsyms,
					XkbKeyNumSyms(xkb,key)*sizeof(KeySym));
    }
    if (changes->map.changed&XkbKeySymsMask) {
	CARD8 oldLast,newLast;
	oldLast = changes->map.first_key_sym+changes->map.num_key_syms-1;
	newLast = first+num-1;

	if (first<changes->map.first_key_sym)
	    changes->map.first_key_sym = first;
	if (oldLast>newLast)
	    newLast= oldLast;
	changes->map.num_key_syms = newLast-changes->map.first_key_sym+1;
    }
    else {
	changes->map.changed|= XkbKeySymsMask;
	changes->map.first_key_sym = first;
	changes->map.num_key_syms = num;
    }
    return;
}

void
#if NeedFunctionPrototypes
XkbUpdateDescActions(	XkbDescPtr		xkb,
			KeyCode		 	first,
			CARD8		 	num,
			XkbChangesPtr	 	changes)
#else
XkbUpdateDescActions(xkb,first,num,changes)
    XkbDescPtr		xkb;
    KeyCode 		first;
    CARD8 		num;
    XkbChangesPtr 	changes;
#endif
{
register unsigned	key;

    for (key=first;key<(first+num);key++) {
	XkbApplyCompatMapToKey(xkb,key,changes);
    }

    if (changes->map.changed&(XkbVirtualModMapMask|XkbModifierMapMask)) {
        unsigned char           newVMods[XkbNumVirtualMods];
        register  unsigned      bit,i;
        unsigned                present;

        bzero(newVMods,XkbNumVirtualMods);
        present= 0;
        for (key=xkb->min_key_code;key<=xkb->max_key_code;key++) {
            if (xkb->server->vmodmap[key]==0)
                continue;
            for (i=0,bit=1;i<XkbNumVirtualMods;i++,bit<<=1) {
                if (bit&xkb->server->vmodmap[key]) {
                    present|= bit;
                    newVMods[i]|= xkb->map->modmap[key];
                }
            }
        }
        for (i=0,bit=1;i<XkbNumVirtualMods;i++,bit<<=1) {
            if ((bit&present)&&(newVMods[i]!=xkb->server->vmods[i])) {
                changes->map.changed|= XkbVirtualModsMask;
                changes->map.vmods|= bit;
                xkb->server->vmods[i]= newVMods[i];
            }
        }
    }
    if (changes->map.changed&XkbVirtualModsMask)
        XkbApplyVirtualModChanges(xkb,changes->map.vmods,changes);

    if (changes->map.changed&XkbKeyActionsMask) {
	CARD8 oldLast,newLast;
	oldLast= changes->map.first_key_act+changes->map.num_key_acts-1;
	newLast = first+num-1;

	if (first<changes->map.first_key_act)
	    changes->map.first_key_act = first;
	if (newLast>oldLast)
	    newLast= oldLast;
	changes->map.num_key_acts= newLast-changes->map.first_key_act+1;
    }
    else {
	changes->map.changed|= XkbKeyActionsMask;
	changes->map.first_key_act = first;
	changes->map.num_key_acts = num;
    }
    return;
}

#if NeedFunctionPrototypes
void
XkbUpdateActions(	DeviceIntPtr	 	pXDev,
			KeyCode		 	first,
			CARD8		 	num,
			XkbChangesPtr	 	changes,
			unsigned *	 	needChecksRtrn,
			XkbEventCausePtr	cause)
#else
void
XkbUpdateActions(pXDev,first,num,changes,needChecksRtrn,cause)
    DeviceIntPtr 	pXDev;
    KeyCode 		first;
    CARD8 		num;
    XkbChangesPtr 	changes;
    unsigned *		needChecksRtrn;
    XkbEventCausePtr	cause;
#endif
{
XkbSrvInfoPtr		xkbi;
XkbDescPtr		xkb;
CARD8 *			repeat;

    if (needChecksRtrn)
	*needChecksRtrn= 0;
    xkbi= pXDev->key->xkbInfo;
    xkb= xkbi->desc;
    repeat= xkb->ctrls->per_key_repeat;

    if (pXDev->kbdfeed)
	memcpy(repeat,pXDev->kbdfeed->ctrl.autoRepeats,32);

    XkbUpdateDescActions(xkb,first,num,changes);

    if ((pXDev->kbdfeed)&&
	(changes->ctrls.enabled_ctrls_changes&XkbPerKeyRepeatMask)) {
        memcpy(pXDev->kbdfeed->ctrl.autoRepeats,repeat, 32);
	(*pXDev->kbdfeed->CtrlProc)(pXDev, &pXDev->kbdfeed->ctrl);
    }
    return;
}

void
#if NeedFunctionPrototypes
XkbUpdateCoreDescription(DeviceIntPtr keybd,Bool resize)
#else
XkbUpdateCoreDescription(keybd,resize)
    DeviceIntPtr keybd;
    Bool	resize;
#endif
{
register int		key,tmp;
int			maxSymsPerKey,maxKeysPerMod;
int			first,last,firstCommon,lastCommon;
XkbDescPtr		xkb;
KeyClassPtr		keyc;
CARD8			keysPerMod[XkbNumModifiers];

    if (!keybd || !keybd->key || !keybd->key->xkbInfo)
	return;
    xkb= keybd->key->xkbInfo->desc;
    keyc= keybd->key;
    maxSymsPerKey= maxKeysPerMod= 0;
    bzero(keysPerMod,sizeof(keysPerMod));
    memcpy(keyc->modifierMap,xkb->map->modmap,xkb->max_key_code+1);
    if ((xkb->min_key_code==keyc->curKeySyms.minKeyCode)&&
	(xkb->max_key_code==keyc->curKeySyms.maxKeyCode)) {
	first= firstCommon= xkb->min_key_code;
	last= lastCommon= xkb->max_key_code;
    }
    else if (resize) {
	keyc->curKeySyms.minKeyCode= xkb->min_key_code;
	keyc->curKeySyms.maxKeyCode= xkb->max_key_code;
	tmp= keyc->curKeySyms.mapWidth*_XkbCoreNumKeys(keyc);
	keyc->curKeySyms.map= _XkbTypedRealloc(keyc->curKeySyms.map,tmp,KeySym);
	if (!keyc->curKeySyms.map)
	   FatalError("Couldn't allocate keysyms\n");
	first= firstCommon= xkb->min_key_code;
	last= lastCommon= xkb->max_key_code;
    }
    else {
	if (xkb->min_key_code<keyc->curKeySyms.minKeyCode) {
	    first= xkb->min_key_code;
	    firstCommon= keyc->curKeySyms.minKeyCode;
	}
	else {
	    firstCommon= xkb->min_key_code;
	    first= keyc->curKeySyms.minKeyCode;
	}
	if (xkb->max_key_code>keyc->curKeySyms.maxKeyCode) {
	    lastCommon= keyc->curKeySyms.maxKeyCode;
	    last= xkb->max_key_code;
	}
	else {
	    lastCommon= xkb->max_key_code;
	    last= keyc->curKeySyms.maxKeyCode;
	}
    }

    /* determine sizes */
    for (key=first;key<=last;key++) {
	if (XkbKeycodeInRange(xkb,key)) {
	    int	nGroups;
	    int	w;
	    nGroups= XkbKeyNumGroups(xkb,key);
	    tmp= 0;
	    if (nGroups>0) {
		if ((w=XkbKeyGroupWidth(xkb,key,XkbGroup1Index))<=2)
		     tmp+= 2;
		else tmp+= w + 2;
	    }
	    if (nGroups>1) {
                if (tmp <= 2) {
		     if ((w=XkbKeyGroupWidth(xkb,key,XkbGroup2Index))<2)
		          tmp+= 2;
		     else tmp+= w;
                } else {
                     if ((w=XkbKeyGroupWidth(xkb,key,XkbGroup2Index))>2)
                          tmp+= w - 2;
                }
	    }
	    if (nGroups>2)
		tmp+= XkbKeyGroupWidth(xkb,key,XkbGroup3Index);
	    if (nGroups>3)
		tmp+= XkbKeyGroupWidth(xkb,key,XkbGroup4Index);
	    if (tmp>maxSymsPerKey)
		maxSymsPerKey= tmp;
	}
	if (_XkbCoreKeycodeInRange(keyc,key)) {
	    if (keyc->modifierMap[key]!=0) {
		register unsigned bit,i,mask;
		mask= keyc->modifierMap[key];
		for (i=0,bit=1;i<XkbNumModifiers;i++,bit<<=1) {
		    if (mask&bit) {
			keysPerMod[i]++;
			if (keysPerMod[i]>maxKeysPerMod)
			    maxKeysPerMod= keysPerMod[i];
		    }
		}
	    }
	}
    }

    if (maxKeysPerMod>0) {
	tmp= maxKeysPerMod*XkbNumModifiers;
	if (keyc->modifierKeyMap==NULL)
	    keyc->modifierKeyMap= (KeyCode *)_XkbCalloc(1, tmp);
	else if (keyc->maxKeysPerModifier<maxKeysPerMod)
	    keyc->modifierKeyMap= (KeyCode *)_XkbRealloc(keyc->modifierKeyMap,tmp);
	if (keyc->modifierKeyMap==NULL)
	    FatalError("Couldn't allocate modifierKeyMap in UpdateCore\n");
	bzero(keyc->modifierKeyMap,tmp);
    }
    else if ((keyc->maxKeysPerModifier>0)&&(keyc->modifierKeyMap!=NULL)) {
	_XkbFree(keyc->modifierKeyMap);
	keyc->modifierKeyMap= NULL;
    }
    keyc->maxKeysPerModifier= maxKeysPerMod;

    if (maxSymsPerKey>0) {
	tmp= maxSymsPerKey*_XkbCoreNumKeys(keyc);
	keyc->curKeySyms.map= _XkbTypedRealloc(keyc->curKeySyms.map,tmp,KeySym);
	if (keyc->curKeySyms.map==NULL)
	    FatalError("Couldn't allocate symbols map in UpdateCore\n");
    }
    else if ((keyc->curKeySyms.mapWidth>0)&&(keyc->curKeySyms.map!=NULL)) {
	_XkbFree(keyc->curKeySyms.map);
	keyc->curKeySyms.map= NULL;
    }
    keyc->curKeySyms.mapWidth= maxSymsPerKey;

    bzero(keysPerMod,sizeof(keysPerMod));
    for (key=firstCommon;key<=lastCommon;key++) {
	if (keyc->curKeySyms.map!=NULL) {
	    KeySym *pCore,*pXKB;
	    unsigned nGroups,groupWidth,n,nOut;

	    nGroups= XkbKeyNumGroups(xkb,key);
	    n= (key-keyc->curKeySyms.minKeyCode)*maxSymsPerKey;
	    pCore= &keyc->curKeySyms.map[n];
	    bzero(pCore,maxSymsPerKey*sizeof(KeySym));
	    pXKB= XkbKeySymsPtr(xkb,key);
	    nOut= 2;
	    if (nGroups>0) {
		groupWidth= XkbKeyGroupWidth(xkb,key,XkbGroup1Index);
		if (groupWidth>0)	pCore[0]= pXKB[0];
		if (groupWidth>1)	pCore[1]= pXKB[1];
		for (n=2;n<groupWidth;n++) {
		    pCore[2+n]= pXKB[n];
		}
		if (groupWidth>2)
		    nOut= groupWidth;
	    }
	    pXKB+= XkbKeyGroupsWidth(xkb,key);
	    nOut+= 2;
	    if (nGroups>1) {
		groupWidth= XkbKeyGroupWidth(xkb,key,XkbGroup2Index);
		if (groupWidth>0)	pCore[2]= pXKB[0];
		if (groupWidth>1)	pCore[3]= pXKB[1];
		for (n=2;n<groupWidth;n++) {
		    pCore[nOut+(n-2)]= pXKB[n];
		}
		if (groupWidth>2)
		    nOut+= (groupWidth-2);
	    }
	    pXKB+= XkbKeyGroupsWidth(xkb,key);
	    for (n=XkbGroup3Index;n<nGroups;n++) {
		register int s;
		groupWidth= XkbKeyGroupWidth(xkb,key,n);
		for (s=0;s<groupWidth;s++) {
		    pCore[nOut++]= pXKB[s];
		}
		pXKB+= XkbKeyGroupsWidth(xkb,key);
	    }
	    if (!pCore[2] && !pCore[3] && maxSymsPerKey >= 6 &&
                (pCore[4] || pCore[5])) {
                pCore[2] = pCore[4];
                pCore[3] = pCore[5];
	    }
	}
	if (keyc->modifierMap[key]!=0) {
	    register unsigned bit,i,mask;
	    mask= keyc->modifierMap[key];
	    for (i=0,bit=1;i<XkbNumModifiers;i++,bit<<=1) {
		if (mask&bit) {
		    tmp= i*maxKeysPerMod+keysPerMod[i];
		    keyc->modifierKeyMap[tmp]= key;
		    keysPerMod[i]++;
		}
	    }
	}
    }
#ifdef MODE_SWITCH
    /* Fix up any of the KME stuff if we changed the core description.
     */
    if (!noKME)
	HandleKeyBinding(keyc, &keyc->curKeySyms);
#endif
    return;
}

void
#if NeedFunctionPrototypes
XkbSetRepeatKeys(DeviceIntPtr pXDev,int key,int onoff)
#else
XkbSetRepeatKeys(pXDev,key,onoff)
    DeviceIntPtr pXDev;
    int		 key;
    int		 onoff;
#endif
{
    if (pXDev && pXDev->key && pXDev->key->xkbInfo) {
	xkbControlsNotify	cn;
	XkbControlsPtr		ctrls = pXDev->key->xkbInfo->desc->ctrls;
	XkbControlsRec 		old;
	old = *ctrls;

	if (key== -1) {	/* global autorepeat setting changed */
	    if (onoff)	ctrls->enabled_ctrls |= XkbRepeatKeysMask;
	    else	ctrls->enabled_ctrls &= ~XkbRepeatKeysMask;
	}
	else if (pXDev->kbdfeed) {
	    ctrls->per_key_repeat[key/8] = 
		pXDev->kbdfeed->ctrl.autoRepeats[key/8];
	}
	
	if (XkbComputeControlsNotify(pXDev,&old,ctrls,&cn,True))
	    XkbSendControlsNotify(pXDev,&cn);
    }
    return;
}

#if NeedFunctionPrototypes
void
XkbApplyMappingChange(	DeviceIntPtr	kbd,
			CARD8		 request,
			KeyCode		 firstKey,
			CARD8		 num,
			ClientPtr	 client)
#else
void
XkbApplyMappingChange(kbd,request,firstKey,num,client)
    DeviceIntPtr kbd;
    CARD8 	request;
    KeyCode 	firstKey;
    CARD8 	num;
    ClientPtr	client;
#endif
{
XkbEventCauseRec	cause;
XkbChangesRec	 	changes;
unsigned	 	check;

    if (kbd->key->xkbInfo==NULL)
	XkbInitDevice(kbd);
    bzero(&changes,sizeof(XkbChangesRec));
    check= 0;
    if (request==MappingKeyboard) {
	XkbSetCauseCoreReq(&cause,X_ChangeKeyboardMapping,client);
	XkbUpdateKeyTypesFromCore(kbd,firstKey,num,&changes);
	XkbUpdateActions(kbd,firstKey,num,&changes,&check,&cause);
	if (check)
	    XkbCheckSecondaryEffects(kbd->key->xkbInfo,check,&changes,&cause);
    }
    else if (request==MappingModifier) {
	XkbDescPtr	xkb= kbd->key->xkbInfo->desc;

	XkbSetCauseCoreReq(&cause,X_SetModifierMapping,client);

	num = xkb->max_key_code-xkb->min_key_code+1;
	memcpy(xkb->map->modmap,kbd->key->modifierMap,xkb->max_key_code+1);

	changes.map.changed|= XkbModifierMapMask;
	changes.map.first_modmap_key= xkb->min_key_code;
	changes.map.num_modmap_keys= num;
	XkbUpdateActions(kbd,xkb->min_key_code,num,&changes,&check,&cause);
	if (check)
	    XkbCheckSecondaryEffects(kbd->key->xkbInfo,check,&changes,&cause);
    }
    /* 3/26/94 (ef) -- XXX! Doesn't deal with input extension requests */
    XkbSendNotification(kbd,&changes,&cause);
    return;
}

void
#if NeedFunctionPrototypes
XkbDisableComputedAutoRepeats(DeviceIntPtr dev,unsigned key)
#else
XkbDisableComputedAutoRepeats(dev,key)
    DeviceIntPtr	dev;
    unsigned		key;
#endif
{
XkbSrvInfoPtr	xkbi = dev->key->xkbInfo;
xkbMapNotify	mn;

    xkbi->desc->server->explicit[key]|= XkbExplicitAutoRepeatMask;
    bzero(&mn,sizeof(mn));
    mn.changed= XkbExplicitComponentsMask;
    mn.firstKeyExplicit= key;
    mn.nKeyExplicit= 1;
    XkbSendMapNotify(dev,&mn);
    return;
}

unsigned
#if NeedFunctionPrototypes
XkbStateChangedFlags(XkbStatePtr old,XkbStatePtr new)
#else
XkbStateChangedFlags(old,new)
    XkbStatePtr	old;
    XkbStatePtr	new;
#endif
{
int		changed;

    changed=(old->group!=new->group?XkbGroupStateMask:0);
    changed|=(old->base_group!=new->base_group?XkbGroupBaseMask:0);
    changed|=(old->latched_group!=new->latched_group?XkbGroupLatchMask:0);
    changed|=(old->locked_group!=new->locked_group?XkbGroupLockMask:0);
    changed|=(old->mods!=new->mods?XkbModifierStateMask:0);
    changed|=(old->base_mods!=new->base_mods?XkbModifierBaseMask:0);
    changed|=(old->latched_mods!=new->latched_mods?XkbModifierLatchMask:0);
    changed|=(old->locked_mods!=new->locked_mods?XkbModifierLockMask:0);
    changed|=(old->compat_state!=new->compat_state?XkbCompatStateMask:0);
    changed|=(old->grab_mods!=new->grab_mods?XkbGrabModsMask:0);
    if (old->compat_grab_mods!=new->compat_grab_mods)
	changed|= XkbCompatGrabModsMask;
    changed|=(old->lookup_mods!=new->lookup_mods?XkbLookupModsMask:0);
    if (old->compat_lookup_mods!=new->compat_lookup_mods)
	changed|= XkbCompatLookupModsMask;
    changed|=(old->ptr_buttons!=new->ptr_buttons?XkbPointerButtonMask:0);
    return changed;
}

void
#if NeedFunctionPrototypes
XkbComputeCompatState(XkbSrvInfoPtr xkbi)
#else
XkbComputeCompatState(xkbi)
    XkbSrvInfoPtr	xkbi;
#endif
{
CARD16 		grp_mask;
XkbStatePtr	state= &xkbi->state;
XkbCompatMapPtr	map;

    map= xkbi->desc->compat;
    grp_mask= map->groups[state->group].mask;
    state->compat_state = state->mods|grp_mask;
    state->compat_lookup_mods= state->lookup_mods|grp_mask;

    if (xkbi->desc->ctrls->enabled_ctrls&XkbIgnoreGroupLockMask)
	 grp_mask= map->groups[state->base_group].mask;
    state->compat_grab_mods= state->grab_mods|grp_mask;
    return;
}

unsigned
#if NeedFunctionPrototypes
XkbAdjustGroup(int group,XkbControlsPtr ctrls)
#else
XkbAdjustGroup(group,ctrls)
    int 		group;
    XkbControlsPtr	ctrls;
#endif
{
unsigned	act;

    act= XkbOutOfRangeGroupAction(ctrls->groups_wrap);
    if (group<0) {
	while ( group < 0 )  {
	    if (act==XkbClampIntoRange) {
		group= XkbGroup1Index;
	    }
	    else if (act==XkbRedirectIntoRange) {
		int newGroup;
		newGroup= XkbOutOfRangeGroupNumber(ctrls->groups_wrap);
		if (newGroup>=ctrls->num_groups)
		     group= XkbGroup1Index;
		else group= newGroup;
	    }
	    else {
		group+= ctrls->num_groups;
	    }
	}
    }
    else if (group>=ctrls->num_groups) {
	if (act==XkbClampIntoRange) {
	    group= ctrls->num_groups-1;
	}
	else if (act==XkbRedirectIntoRange) {
	    int newGroup;
	    newGroup= XkbOutOfRangeGroupNumber(ctrls->groups_wrap);
	    if (newGroup>=ctrls->num_groups)
		 group= XkbGroup1Index;
	    else group= newGroup;
	}
	else {
	    group%= ctrls->num_groups;
	}
    }
    return group;
}

void
#if NeedFunctionPrototypes
XkbComputeDerivedState(XkbSrvInfoPtr xkbi)
#else
XkbComputeDerivedState(xkbi)
    XkbSrvInfoPtr	xkbi;
#endif
{
XkbStatePtr	state= &xkbi->state;
XkbControlsPtr	ctrls= xkbi->desc->ctrls;
char		grp;

    state->mods= (state->base_mods|state->latched_mods);
    state->mods|= state->locked_mods;
    state->lookup_mods= state->mods&(~ctrls->internal.mask);
    state->grab_mods= state->lookup_mods&(~ctrls->ignore_lock.mask);
    state->grab_mods|= 
	((state->base_mods|state->latched_mods)&ctrls->ignore_lock.mask);


    grp= state->locked_group;
    if ((grp>=ctrls->num_groups) || (grp<0))
	state->locked_group= XkbAdjustGroup(grp,ctrls);

    grp= state->locked_group+state->base_group+state->latched_group;
    if ((grp>=ctrls->num_groups) || (grp<0))
	 state->group= XkbAdjustGroup(grp,ctrls);
    else state->group= grp;
    XkbComputeCompatState(xkbi);
    return;
}

/***====================================================================***/

void
#if NeedFunctionPrototypes
XkbCheckSecondaryEffects(	XkbSrvInfoPtr		xkbi,
				unsigned		which,
				XkbChangesPtr 		changes,
				XkbEventCausePtr	cause)
#else
XkbCheckSecondaryEffects(xkbi,which,changes,cause)
    XkbSrvInfoPtr	xkbi;
    unsigned		which;
    XkbChangesPtr	changes;
    XkbEventCausePtr	cause;
#endif
{
    if (which&XkbStateNotifyMask) {
	XkbStateRec old;
	old= xkbi->state;
	changes->state_changes|= XkbStateChangedFlags(&old,&xkbi->state);
	XkbComputeDerivedState(xkbi);
    }
    if (which&XkbIndicatorStateNotifyMask)
	XkbUpdateIndicators(xkbi->device,XkbAllIndicatorsMask,True,changes,
									cause);
    return;
}

/***====================================================================***/

void
#if NeedFunctionPrototypes
XkbSetPhysicalLockingKey(DeviceIntPtr dev,unsigned key)
#else
XkbSetPhysicalLockingKey(dev,key)
   DeviceIntPtr	dev;
   unsigned	key;
#endif
{
XkbDescPtr	xkb;

    xkb= dev->key->xkbInfo->desc;
    if ((key>=xkb->min_key_code) && (key<=xkb->max_key_code)) {
	xkb->server->behaviors[key].type= XkbKB_Lock|XkbKB_Permanent;
    }
    else ErrorF("Internal Error!  Bad XKB info in SetPhysicalLockingKey\n");
    return;
}

/***====================================================================***/

Bool
#if NeedFunctionPrototypes
XkbEnableDisableControls(	XkbSrvInfoPtr		xkbi,
				unsigned long		change,
				unsigned long		newValues,
				XkbChangesPtr		changes,
				XkbEventCausePtr	cause)
#else
XkbEnableDisableControls(xkbi,change,newValues,changes,cause)
    XkbSrvInfoPtr	xkbi;
    unsigned long	change;
    unsigned long	newValues;
    XkbChangesPtr	changes;
    XkbEventCausePtr	cause;
#endif
{
XkbControlsPtr		ctrls;
unsigned 		old;
XkbSrvLedInfoPtr	sli;

    ctrls= xkbi->desc->ctrls;
    old= ctrls->enabled_ctrls;
    ctrls->enabled_ctrls&= ~change;
    ctrls->enabled_ctrls|= (change&newValues);
    if (old==ctrls->enabled_ctrls)
	return False;
    if (cause!=NULL) {
	xkbControlsNotify cn;
	cn.numGroups= ctrls->num_groups;
	cn.changedControls|= XkbControlsEnabledMask;
	cn.enabledControls= ctrls->enabled_ctrls;
	cn.enabledControlChanges= (ctrls->enabled_ctrls^old);
	cn.keycode= cause->kc;
	cn.eventType= cause->event;
	cn.requestMajor= cause->mjr;
	cn.requestMinor= cause->mnr;
	XkbSendControlsNotify(xkbi->device,&cn);
    }
    else {
	/* Yes, this really should be an XOR.  If ctrls->enabled_ctrls_changes*/
	/* is non-zero, the controls in question changed already in "this" */
	/* request and this change merely undoes the previous one.  By the */
	/* same token, we have to figure out whether or not ControlsEnabled */
	/* should be set or not in the changes structure */
	changes->ctrls.enabled_ctrls_changes^= (ctrls->enabled_ctrls^old);
	if (changes->ctrls.enabled_ctrls_changes)
	     changes->ctrls.changed_ctrls|= XkbControlsEnabledMask;
	else changes->ctrls.changed_ctrls&= ~XkbControlsEnabledMask;
    }
    sli= XkbFindSrvLedInfo(xkbi->device,XkbDfltXIClass,XkbDfltXIId,0);
    XkbUpdateIndicators(xkbi->device,sli->usesControls,True,changes,cause);
    return True;
}

/***====================================================================***/

#define	MAX_TOC	16

XkbGeometryPtr 
#if NeedFunctionPrototypes
XkbLookupNamedGeometry(DeviceIntPtr dev,Atom name,Bool *shouldFree)
#else
XkbLookupNamedGeometry(dev,name,shouldFree)
    DeviceIntPtr	dev;
    Atom		name;
    Bool *		shouldFree;
#endif
{
XkbSrvInfoPtr	xkbi=	dev->key->xkbInfo;
XkbDescPtr	xkb=	xkbi->desc;

    *shouldFree= 0;
    if (name==None) {
	if (xkb->geom!=NULL)
	    return xkb->geom;
	name= xkb->names->geometry;
    }
    if ((xkb->geom!=NULL)&&(xkb->geom->name==name))
	return xkb->geom;
    else if ((name==xkb->names->geometry)&&(xkb->geom==NULL)) {
	FILE *file= XkbDDXOpenConfigFile(XkbInitialMap,NULL,0);
	if (file!=NULL) {
	    XkbFileInfo		xkbFInfo;
	    xkmFileInfo		finfo;
	    xkmSectionInfo	toc[MAX_TOC],*entry;
	    bzero(&xkbFInfo,sizeof(xkbFInfo));
	    xkbFInfo.xkb= xkb;
	    if (XkmReadTOC(file,&finfo,MAX_TOC,toc)) {
		entry= XkmFindTOCEntry(&finfo,toc,XkmGeometryIndex);
		if (entry!=NULL)
		    XkmReadFileSection(file,entry,&xkbFInfo,NULL);
	    }
	    fclose(file);
	    if (xkb->geom) {
		*shouldFree= 0;
		return xkb->geom;
	    }
	}
    }
    *shouldFree= 1;
    return NULL;
}

void
#if NeedFunctionPrototypes
XkbConvertCase(register KeySym sym, KeySym *lower, KeySym *upper)
#else
XkbConvertCase(sym, lower, upper)
    register KeySym sym;
    KeySym *lower;
    KeySym *upper;
#endif
{
    *lower = sym;
    *upper = sym;
    switch(sym >> 8) {
    case 0: /* Latin 1 */
	if ((sym >= XK_A) && (sym <= XK_Z))
	    *lower += (XK_a - XK_A);
	else if ((sym >= XK_a) && (sym <= XK_z))
	    *upper -= (XK_a - XK_A);
	else if ((sym >= XK_Agrave) && (sym <= XK_Odiaeresis))
	    *lower += (XK_agrave - XK_Agrave);
	else if ((sym >= XK_agrave) && (sym <= XK_odiaeresis))
	    *upper -= (XK_agrave - XK_Agrave);
	else if ((sym >= XK_Ooblique) && (sym <= XK_Thorn))
	    *lower += (XK_oslash - XK_Ooblique);
	else if ((sym >= XK_oslash) && (sym <= XK_thorn))
	    *upper -= (XK_oslash - XK_Ooblique);
	break;
    case 1: /* Latin 2 */
	/* Assume the KeySym is a legal value (ignore discontinuities) */
	if (sym == XK_Aogonek)
	    *lower = XK_aogonek;
	else if (sym >= XK_Lstroke && sym <= XK_Sacute)
	    *lower += (XK_lstroke - XK_Lstroke);
	else if (sym >= XK_Scaron && sym <= XK_Zacute)
	    *lower += (XK_scaron - XK_Scaron);
	else if (sym >= XK_Zcaron && sym <= XK_Zabovedot)
	    *lower += (XK_zcaron - XK_Zcaron);
	else if (sym == XK_aogonek)
	    *upper = XK_Aogonek;
	else if (sym >= XK_lstroke && sym <= XK_sacute)
	    *upper -= (XK_lstroke - XK_Lstroke);
	else if (sym >= XK_scaron && sym <= XK_zacute)
	    *upper -= (XK_scaron - XK_Scaron);
	else if (sym >= XK_zcaron && sym <= XK_zabovedot)
	    *upper -= (XK_zcaron - XK_Zcaron);
	else if (sym >= XK_Racute && sym <= XK_Tcedilla)
	    *lower += (XK_racute - XK_Racute);
	else if (sym >= XK_racute && sym <= XK_tcedilla)
	    *upper -= (XK_racute - XK_Racute);
	break;
    case 2: /* Latin 3 */
	/* Assume the KeySym is a legal value (ignore discontinuities) */
	if (sym >= XK_Hstroke && sym <= XK_Hcircumflex)
	    *lower += (XK_hstroke - XK_Hstroke);
	else if (sym >= XK_Gbreve && sym <= XK_Jcircumflex)
	    *lower += (XK_gbreve - XK_Gbreve);
	else if (sym >= XK_hstroke && sym <= XK_hcircumflex)
	    *upper -= (XK_hstroke - XK_Hstroke);
	else if (sym >= XK_gbreve && sym <= XK_jcircumflex)
	    *upper -= (XK_gbreve - XK_Gbreve);
	else if (sym >= XK_Cabovedot && sym <= XK_Scircumflex)
	    *lower += (XK_cabovedot - XK_Cabovedot);
	else if (sym >= XK_cabovedot && sym <= XK_scircumflex)
	    *upper -= (XK_cabovedot - XK_Cabovedot);
	break;
    case 3: /* Latin 4 */
	/* Assume the KeySym is a legal value (ignore discontinuities) */
	if (sym >= XK_Rcedilla && sym <= XK_Tslash)
	    *lower += (XK_rcedilla - XK_Rcedilla);
	else if (sym >= XK_rcedilla && sym <= XK_tslash)
	    *upper -= (XK_rcedilla - XK_Rcedilla);
	else if (sym == XK_ENG)
	    *lower = XK_eng;
	else if (sym == XK_eng)
	    *upper = XK_ENG;
	else if (sym >= XK_Amacron && sym <= XK_Umacron)
	    *lower += (XK_amacron - XK_Amacron);
	else if (sym >= XK_amacron && sym <= XK_umacron)
	    *upper -= (XK_amacron - XK_Amacron);
	break;
    case 6: /* Cyrillic */
	/* Assume the KeySym is a legal value (ignore discontinuities) */
	if (sym >= XK_Serbian_DJE && sym <= XK_Serbian_DZE)
	    *lower -= (XK_Serbian_DJE - XK_Serbian_dje);
	else if (sym >= XK_Serbian_dje && sym <= XK_Serbian_dze)
	    *upper += (XK_Serbian_DJE - XK_Serbian_dje);
	else if (sym >= XK_Cyrillic_YU && sym <= XK_Cyrillic_HARDSIGN)
	    *lower -= (XK_Cyrillic_YU - XK_Cyrillic_yu);
	else if (sym >= XK_Cyrillic_yu && sym <= XK_Cyrillic_hardsign)
	    *upper += (XK_Cyrillic_YU - XK_Cyrillic_yu);
        break;
    case 7: /* Greek */
	/* Assume the KeySym is a legal value (ignore discontinuities) */
	if (sym >= XK_Greek_ALPHAaccent && sym <= XK_Greek_OMEGAaccent)
	    *lower += (XK_Greek_alphaaccent - XK_Greek_ALPHAaccent);
	else if (sym >= XK_Greek_alphaaccent && sym <= XK_Greek_omegaaccent &&
		 sym != XK_Greek_iotaaccentdieresis &&
		 sym != XK_Greek_upsilonaccentdieresis)
	    *upper -= (XK_Greek_alphaaccent - XK_Greek_ALPHAaccent);
	else if (sym >= XK_Greek_ALPHA && sym <= XK_Greek_OMEGA)
	    *lower += (XK_Greek_alpha - XK_Greek_ALPHA);
	else if (sym >= XK_Greek_alpha && sym <= XK_Greek_omega &&
		 sym != XK_Greek_finalsmallsigma)
	    *upper -= (XK_Greek_alpha - XK_Greek_ALPHA);
        break;
    }
}
