1706f2543Smrg/************************************************************
2706f2543SmrgCopyright (c) 1993 by Silicon Graphics Computer Systems, Inc.
3706f2543Smrg
4706f2543SmrgPermission to use, copy, modify, and distribute this
5706f2543Smrgsoftware and its documentation for any purpose and without
6706f2543Smrgfee is hereby granted, provided that the above copyright
7706f2543Smrgnotice appear in all copies and that both that copyright
8706f2543Smrgnotice and this permission notice appear in supporting
9706f2543Smrgdocumentation, and that the name of Silicon Graphics not be
10706f2543Smrgused in advertising or publicity pertaining to distribution
11706f2543Smrgof the software without specific prior written permission.
12706f2543SmrgSilicon Graphics makes no representation about the suitability
13706f2543Smrgof this software for any purpose. It is provided "as is"
14706f2543Smrgwithout any express or implied warranty.
15706f2543Smrg
16706f2543SmrgSILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
17706f2543SmrgSOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
18706f2543SmrgAND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON
19706f2543SmrgGRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
20706f2543SmrgDAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
21706f2543SmrgDATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
22706f2543SmrgOR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION  WITH
23706f2543SmrgTHE USE OR PERFORMANCE OF THIS SOFTWARE.
24706f2543Smrg
25706f2543Smrg********************************************************/
26706f2543Smrg
27706f2543Smrg#ifdef HAVE_DIX_CONFIG_H
28706f2543Smrg#include <dix-config.h>
29706f2543Smrg#elif defined(HAVE_CONFIG_H)
30706f2543Smrg#include <config.h>
31706f2543Smrg#endif
32706f2543Smrg
33706f2543Smrg#include <stdio.h>
34706f2543Smrg#include <X11/X.h>
35706f2543Smrg#include <X11/Xproto.h>
36706f2543Smrg#include "misc.h"
37706f2543Smrg#include "inputstr.h"
38706f2543Smrg#include <X11/keysym.h>
39706f2543Smrg#define	XKBSRV_NEED_FILE_FUNCS
40706f2543Smrg#include <xkbsrv.h>
41706f2543Smrg
42706f2543Smrg/***====================================================================***/
43706f2543Smrg
44706f2543Smrg#define	CORE_SYM(i)	(i<map_width?core_syms[i]:NoSymbol)
45706f2543Smrg#define	XKB_OFFSET(g,l)	(((g)*groupsWidth)+(l))
46706f2543Smrg
47706f2543Smrgint
48706f2543SmrgXkbKeyTypesForCoreSymbols(	XkbDescPtr	xkb,
49706f2543Smrg				int		map_width,
50706f2543Smrg				KeySym *	core_syms,
51706f2543Smrg				unsigned int 	protected,
52706f2543Smrg    				int *		types_inout,
53706f2543Smrg				KeySym *	xkb_syms_rtrn)
54706f2543Smrg{
55706f2543Smrgregister int	i;
56706f2543Smrgunsigned int	empty;
57706f2543Smrgint		nSyms[XkbNumKbdGroups];
58706f2543Smrgint		nGroups,tmp,groupsWidth;
59706f2543SmrgBOOL		replicated = FALSE;
60706f2543Smrg
61706f2543Smrg    /* Section 12.2 of the protocol describes this process in more detail */
62706f2543Smrg    /* Step 1:  find the # of symbols in the core mapping per group */
63706f2543Smrg    groupsWidth= 2;
64706f2543Smrg    for (i=0;i<XkbNumKbdGroups;i++) {
65706f2543Smrg	if ((protected&(1<<i))&&(types_inout[i]<xkb->map->num_types)) {
66706f2543Smrg	    nSyms[i]= xkb->map->types[types_inout[i]].num_levels;
67706f2543Smrg	    if (nSyms[i]>groupsWidth)
68706f2543Smrg		groupsWidth= nSyms[i];
69706f2543Smrg	}
70706f2543Smrg	else {
71706f2543Smrg	    types_inout[i]= XkbTwoLevelIndex;	/* don't really know, yet */
72706f2543Smrg	    nSyms[i]= 2;
73706f2543Smrg	}
74706f2543Smrg    }
75706f2543Smrg    if (nSyms[XkbGroup1Index]<2)
76706f2543Smrg	nSyms[XkbGroup1Index]= 2;
77706f2543Smrg    if (nSyms[XkbGroup2Index]<2)
78706f2543Smrg	nSyms[XkbGroup2Index]= 2;
79706f2543Smrg    /* Step 2:	Copy the symbols from the core ordering to XKB ordering	*/
80706f2543Smrg    /*		symbols in the core are in the order:			*/
81706f2543Smrg    /*		G1L1 G1L2 G2L1 G2L2 [G1L[3-n]] [G2L[3-n]] [G3L*] [G3L*]	*/
82706f2543Smrg    xkb_syms_rtrn[XKB_OFFSET(XkbGroup1Index,0)]= CORE_SYM(0);
83706f2543Smrg    xkb_syms_rtrn[XKB_OFFSET(XkbGroup1Index,1)]= CORE_SYM(1);
84706f2543Smrg    for (i=2;i<nSyms[XkbGroup1Index];i++) {
85706f2543Smrg	xkb_syms_rtrn[XKB_OFFSET(XkbGroup1Index,i)]= CORE_SYM(2+i);
86706f2543Smrg    }
87706f2543Smrg    xkb_syms_rtrn[XKB_OFFSET(XkbGroup2Index,0)]= CORE_SYM(2);
88706f2543Smrg    xkb_syms_rtrn[XKB_OFFSET(XkbGroup2Index,1)]= CORE_SYM(3);
89706f2543Smrg    tmp= 2+(nSyms[XkbGroup1Index]-2); /* offset to extra group2 syms */
90706f2543Smrg    for (i=2;i<nSyms[XkbGroup2Index];i++) {
91706f2543Smrg	xkb_syms_rtrn[XKB_OFFSET(XkbGroup2Index,i)]= CORE_SYM(tmp+i);
92706f2543Smrg    }
93706f2543Smrg
94706f2543Smrg    /* Special case: if only the first group is explicit, and the symbols
95706f2543Smrg     * replicate across all groups, then we have a Section 12.4 replication */
96706f2543Smrg    if ((protected & ~XkbExplicitKeyType1Mask) == 0)
97706f2543Smrg    {
98706f2543Smrg        int j, width = nSyms[XkbGroup1Index];
99706f2543Smrg
100706f2543Smrg        replicated = TRUE;
101706f2543Smrg
102706f2543Smrg        /* Check ABAB in ABABCDECDEABCDE */
103706f2543Smrg        if ((width > 0 && CORE_SYM(0) != CORE_SYM(2)) ||
104706f2543Smrg            (width > 1 && CORE_SYM(1) != CORE_SYM(3)))
105706f2543Smrg            replicated = FALSE;
106706f2543Smrg
107706f2543Smrg        /* Check CDECDE in ABABCDECDEABCDE */
108706f2543Smrg        for (i = 2; i < width && replicated; i++)
109706f2543Smrg        {
110706f2543Smrg            if (CORE_SYM(2 + i) != CORE_SYM(i + width))
111706f2543Smrg                replicated = FALSE;
112706f2543Smrg        }
113706f2543Smrg
114706f2543Smrg        /* Check ABCDE in ABABCDECDEABCDE */
115706f2543Smrg        for (j = 2; replicated &&
116706f2543Smrg                    j < XkbNumKbdGroups &&
117706f2543Smrg                    map_width >= width * (j + 1); j++)
118706f2543Smrg        {
119706f2543Smrg            for (i = 0; i < width && replicated; i++)
120706f2543Smrg            {
121706f2543Smrg                if (CORE_SYM(((i < 2) ? i : 2 + i)) != CORE_SYM(i + width * j))
122706f2543Smrg                    replicated = FALSE;
123706f2543Smrg            }
124706f2543Smrg        }
125706f2543Smrg    }
126706f2543Smrg
127706f2543Smrg    if (replicated)
128706f2543Smrg    {
129706f2543Smrg	nSyms[XkbGroup2Index]= 0;
130706f2543Smrg	nSyms[XkbGroup3Index]= 0;
131706f2543Smrg	nSyms[XkbGroup4Index]= 0;
132706f2543Smrg	nGroups= 1;
133706f2543Smrg    } else
134706f2543Smrg    {
135706f2543Smrg        tmp= nSyms[XkbGroup1Index]+nSyms[XkbGroup2Index];
136706f2543Smrg        if ((tmp>=map_width)&&
137706f2543Smrg                ((protected&(XkbExplicitKeyType3Mask|XkbExplicitKeyType4Mask))==0)) {
138706f2543Smrg            nSyms[XkbGroup3Index]= 0;
139706f2543Smrg            nSyms[XkbGroup4Index]= 0;
140706f2543Smrg            nGroups= 2;
141706f2543Smrg        } else
142706f2543Smrg        {
143706f2543Smrg            nGroups= 3;
144706f2543Smrg            for (i=0;i<nSyms[XkbGroup3Index];i++,tmp++) {
145706f2543Smrg                xkb_syms_rtrn[XKB_OFFSET(XkbGroup3Index,i)]= CORE_SYM(tmp);
146706f2543Smrg            }
147706f2543Smrg            if ((tmp<map_width)||(protected&XkbExplicitKeyType4Mask)) {
148706f2543Smrg                nGroups= 4;
149706f2543Smrg                for (i=0;i<nSyms[XkbGroup4Index];i++,tmp++) {
150706f2543Smrg                    xkb_syms_rtrn[XKB_OFFSET(XkbGroup4Index,i)]= CORE_SYM(tmp);
151706f2543Smrg                }
152706f2543Smrg            }
153706f2543Smrg            else {
154706f2543Smrg                nSyms[XkbGroup4Index]= 0;
155706f2543Smrg            }
156706f2543Smrg        }
157706f2543Smrg    }
158706f2543Smrg    /* steps 3&4: alphanumeric expansion,  assign canonical types */
159706f2543Smrg    empty= 0;
160706f2543Smrg    for (i=0;i<nGroups;i++) {
161706f2543Smrg	KeySym *syms;
162706f2543Smrg	syms= &xkb_syms_rtrn[XKB_OFFSET(i,0)];
163706f2543Smrg	if ((nSyms[i]>1)&&(syms[1]==NoSymbol)&&(syms[0]!=NoSymbol)) {
164706f2543Smrg	    KeySym upper,lower;
165706f2543Smrg	    XkbConvertCase(syms[0],&lower,&upper);
166706f2543Smrg	    if (upper!=lower) {
167706f2543Smrg		xkb_syms_rtrn[XKB_OFFSET(i,0)]= lower;
168706f2543Smrg		xkb_syms_rtrn[XKB_OFFSET(i,1)]= upper;
169706f2543Smrg		if ((protected&(1<<i))==0)
170706f2543Smrg		    types_inout[i]= XkbAlphabeticIndex;
171706f2543Smrg	    }
172706f2543Smrg	    else if ((protected&(1<<i))==0) {
173706f2543Smrg		types_inout[i]= XkbOneLevelIndex;
174706f2543Smrg	/*	nSyms[i]=	1;*/
175706f2543Smrg	    }
176706f2543Smrg	}
177706f2543Smrg	if (((protected&(1<<i))==0)&&(types_inout[i]==XkbTwoLevelIndex)) {
178706f2543Smrg	    if (XkbKSIsKeypad(syms[0])||XkbKSIsKeypad(syms[1]))
179706f2543Smrg		types_inout[i]= XkbKeypadIndex;
180706f2543Smrg	    else {
181706f2543Smrg		KeySym upper,lower;
182706f2543Smrg		XkbConvertCase(syms[0],&lower,&upper);
183706f2543Smrg		if ((syms[0]==lower)&&(syms[1]==upper))
184706f2543Smrg		    types_inout[i]= XkbAlphabeticIndex;
185706f2543Smrg	    }
186706f2543Smrg	}
187706f2543Smrg	if (syms[0]==NoSymbol) {
188706f2543Smrg	    register int n;
189706f2543Smrg	    Bool	found;
190706f2543Smrg	    for (n=1,found=FALSE;(!found)&&(n<nSyms[i]);n++) {
191706f2543Smrg		found= (syms[n]!=NoSymbol);
192706f2543Smrg	    }
193706f2543Smrg	    if (!found)
194706f2543Smrg		empty|= (1<<i);
195706f2543Smrg	}
196706f2543Smrg    }
197706f2543Smrg    /* step 5: squoosh out empty groups */
198706f2543Smrg    if (empty) {
199706f2543Smrg	for (i=nGroups-1;i>=0;i--) {
200706f2543Smrg	    if (((empty&(1<<i))==0)||(protected&(1<<i)))
201706f2543Smrg		break;
202706f2543Smrg	    nGroups--;
203706f2543Smrg	}
204706f2543Smrg    }
205706f2543Smrg    if (nGroups<1)
206706f2543Smrg	return 0;
207706f2543Smrg
208706f2543Smrg    /* step 6: replicate group 1 into group two, if necessary */
209706f2543Smrg    if ((nGroups>1)&&((empty&(XkbGroup1Mask|XkbGroup2Mask))==XkbGroup2Mask)) {
210706f2543Smrg	if ((protected&(XkbExplicitKeyType1Mask|XkbExplicitKeyType2Mask))==0) {
211706f2543Smrg	    nSyms[XkbGroup2Index]= nSyms[XkbGroup1Index];
212706f2543Smrg	    types_inout[XkbGroup2Index]= types_inout[XkbGroup1Index];
213706f2543Smrg	    memcpy((char *)&xkb_syms_rtrn[2],(char *)xkb_syms_rtrn,
214706f2543Smrg							2*sizeof(KeySym));
215706f2543Smrg	}
216706f2543Smrg	else if (types_inout[XkbGroup1Index]==types_inout[XkbGroup2Index]) {
217706f2543Smrg	    memcpy((char *)&xkb_syms_rtrn[nSyms[XkbGroup1Index]],
218706f2543Smrg	    				(char *)xkb_syms_rtrn,
219706f2543Smrg					nSyms[XkbGroup1Index]*sizeof(KeySym));
220706f2543Smrg	}
221706f2543Smrg    }
222706f2543Smrg
223706f2543Smrg    /* step 7: check for all groups identical or all width 1
224706f2543Smrg     *
225706f2543Smrg     * Special feature: if group 1 has an explicit type and all other groups
226706f2543Smrg     * have canonical types with same symbols, we assume it's info lost from
227706f2543Smrg     * the core replication.
228706f2543Smrg     */
229706f2543Smrg    if (nGroups>1) {
230706f2543Smrg	Bool sameType,allOneLevel, canonical = TRUE;
231706f2543Smrg	allOneLevel= (xkb->map->types[types_inout[0]].num_levels==1);
232706f2543Smrg	for (i=1,sameType=TRUE;(allOneLevel||sameType)&&(i<nGroups);i++) {
233706f2543Smrg	    sameType=(sameType&&(types_inout[i]==types_inout[XkbGroup1Index]));
234706f2543Smrg	    if (allOneLevel)
235706f2543Smrg		allOneLevel= (xkb->map->types[types_inout[i]].num_levels==1);
236706f2543Smrg	    if (types_inout[i] > XkbLastRequiredType)
237706f2543Smrg		canonical = FALSE;
238706f2543Smrg	}
239706f2543Smrg	if (((sameType) || canonical)&&
240706f2543Smrg	    (!(protected&(XkbExplicitKeyTypesMask&~XkbExplicitKeyType1Mask)))){
241706f2543Smrg	    register int s;
242706f2543Smrg	    Bool	identical;
243706f2543Smrg	    for (i=1,identical=TRUE;identical&&(i<nGroups);i++) {
244706f2543Smrg		KeySym *syms;
245706f2543Smrg                if (nSyms[i] != nSyms[XkbGroup1Index])
246706f2543Smrg                    identical = FALSE;
247706f2543Smrg		syms= &xkb_syms_rtrn[XKB_OFFSET(i,0)];
248706f2543Smrg		for (s=0;identical&&(s<nSyms[i]);s++) {
249706f2543Smrg		    if (syms[s]!=xkb_syms_rtrn[s])
250706f2543Smrg			identical= FALSE;
251706f2543Smrg		}
252706f2543Smrg	    }
253706f2543Smrg	    if (identical)
254706f2543Smrg		nGroups= 1;
255706f2543Smrg	}
256706f2543Smrg	if (allOneLevel && (nGroups>1)) {
257706f2543Smrg	    KeySym *syms;
258706f2543Smrg	    syms= &xkb_syms_rtrn[nSyms[XkbGroup1Index]];
259706f2543Smrg	    nSyms[XkbGroup1Index]= 1;
260706f2543Smrg	    for (i=1;i<nGroups;i++) {
261706f2543Smrg		xkb_syms_rtrn[i]= syms[0];
262706f2543Smrg		syms+= nSyms[i];
263706f2543Smrg		nSyms[i]= 1;
264706f2543Smrg	    }
265706f2543Smrg	}
266706f2543Smrg    }
267706f2543Smrg    return nGroups;
268706f2543Smrg}
269706f2543Smrg
270706f2543Smrgstatic XkbSymInterpretPtr
271706f2543Smrg_XkbFindMatchingInterp(	XkbDescPtr	xkb,
272706f2543Smrg			KeySym 		sym,
273706f2543Smrg			unsigned int	real_mods,
274706f2543Smrg			unsigned int	level)
275706f2543Smrg{
276706f2543Smrgregister unsigned	 i;
277706f2543SmrgXkbSymInterpretPtr	 interp,rtrn;
278706f2543SmrgCARD8			 mods;
279706f2543Smrg
280706f2543Smrg    rtrn= NULL;
281706f2543Smrg    interp= xkb->compat->sym_interpret;
282706f2543Smrg    for (i=0;i<xkb->compat->num_si;i++,interp++) {
283706f2543Smrg	if ((interp->sym==NoSymbol)||(sym==interp->sym)) {
284706f2543Smrg	    int match;
285706f2543Smrg	    if ((level==0)||((interp->match&XkbSI_LevelOneOnly)==0))
286706f2543Smrg		 mods= real_mods;
287706f2543Smrg	    else mods= 0;
288706f2543Smrg	    switch (interp->match&XkbSI_OpMask) {
289706f2543Smrg		case XkbSI_NoneOf:
290706f2543Smrg		    match= ((interp->mods&mods)==0);
291706f2543Smrg		    break;
292706f2543Smrg		case XkbSI_AnyOfOrNone:
293706f2543Smrg		    match= ((mods==0)||((interp->mods&mods)!=0));
294706f2543Smrg		    break;
295706f2543Smrg		case XkbSI_AnyOf:
296706f2543Smrg		    match= ((interp->mods&mods)!=0);
297706f2543Smrg		    break;
298706f2543Smrg		case XkbSI_AllOf:
299706f2543Smrg		    match= ((interp->mods&mods)==interp->mods);
300706f2543Smrg		    break;
301706f2543Smrg		case XkbSI_Exactly:
302706f2543Smrg		    match= (interp->mods==mods);
303706f2543Smrg		    break;
304706f2543Smrg		default:
305706f2543Smrg		    match= 0;
306706f2543Smrg		    break;
307706f2543Smrg	    }
308706f2543Smrg	    if (match) {
309706f2543Smrg		if (interp->sym!=NoSymbol) {
310706f2543Smrg		    return interp;
311706f2543Smrg		}
312706f2543Smrg		else if (rtrn==NULL) {
313706f2543Smrg		    rtrn= interp;
314706f2543Smrg		}
315706f2543Smrg	    }
316706f2543Smrg	}
317706f2543Smrg    }
318706f2543Smrg    return rtrn;
319706f2543Smrg}
320706f2543Smrg
321706f2543Smrgstatic void
322706f2543Smrg_XkbAddKeyChange(KeyCode *pFirst,unsigned char *pNum,KeyCode newKey)
323706f2543Smrg{
324706f2543SmrgKeyCode	last;
325706f2543Smrg
326706f2543Smrg    last= (*pFirst)+(*pNum);
327706f2543Smrg    if (newKey<*pFirst) {
328706f2543Smrg	*pFirst= newKey;
329706f2543Smrg	*pNum= (last-newKey)+1;
330706f2543Smrg    }
331706f2543Smrg    else if (newKey>last) {
332706f2543Smrg	*pNum= (last-*pFirst)+1;
333706f2543Smrg    }
334706f2543Smrg    return;
335706f2543Smrg}
336706f2543Smrg
337706f2543Smrgstatic void
338706f2543Smrg_XkbSetActionKeyMods(XkbDescPtr xkb,XkbAction *act,unsigned mods)
339706f2543Smrg{
340706f2543Smrgunsigned	tmp;
341706f2543Smrg
342706f2543Smrg    switch (act->type) {
343706f2543Smrg	case XkbSA_SetMods: case XkbSA_LatchMods: case XkbSA_LockMods:
344706f2543Smrg	    if (act->mods.flags&XkbSA_UseModMapMods)
345706f2543Smrg		act->mods.real_mods= act->mods.mask= mods;
346706f2543Smrg	    if ((tmp= XkbModActionVMods(&act->mods))!=0) {
347706f2543Smrg		XkbVirtualModsToReal(xkb,tmp,&tmp);
348706f2543Smrg		act->mods.mask|= tmp;
349706f2543Smrg	    }
350706f2543Smrg	    break;
351706f2543Smrg	case XkbSA_ISOLock:
352706f2543Smrg	    if (act->iso.flags&XkbSA_UseModMapMods)
353706f2543Smrg		act->iso.real_mods= act->iso.mask= mods;
354706f2543Smrg	    if ((tmp= XkbModActionVMods(&act->iso))!=0) {
355706f2543Smrg		XkbVirtualModsToReal(xkb,tmp,&tmp);
356706f2543Smrg		act->iso.mask|= tmp;
357706f2543Smrg	    }
358706f2543Smrg	    break;
359706f2543Smrg    }
360706f2543Smrg    return;
361706f2543Smrg}
362706f2543Smrg
363706f2543Smrg#define	IBUF_SIZE	8
364706f2543Smrg
365706f2543SmrgBool
366706f2543SmrgXkbApplyCompatMapToKey(XkbDescPtr xkb,KeyCode key,XkbChangesPtr changes)
367706f2543Smrg{
368706f2543SmrgKeySym *		syms;
369706f2543Smrgunsigned char 		explicit,mods;
370706f2543SmrgXkbSymInterpretPtr	*interps,ibuf[IBUF_SIZE];
371706f2543Smrgint			n,nSyms,found;
372706f2543Smrgunsigned		changed,tmp;
373706f2543Smrg
374706f2543Smrg    if ((!xkb)||(!xkb->map)||(!xkb->map->key_sym_map)||
375706f2543Smrg    			(!xkb->compat)||(!xkb->compat->sym_interpret)||
376706f2543Smrg			(key<xkb->min_key_code)||(key>xkb->max_key_code)) {
377706f2543Smrg	return FALSE;
378706f2543Smrg    }
379706f2543Smrg    if (((!xkb->server)||(!xkb->server->key_acts))&&
380706f2543Smrg		(XkbAllocServerMap(xkb,XkbAllServerInfoMask,0)!=Success)) {
381706f2543Smrg	return FALSE;
382706f2543Smrg    }
383706f2543Smrg    changed= 0;	/* keeps track of what has changed in _this_ call */
384706f2543Smrg    explicit= xkb->server->explicit[key];
385706f2543Smrg    if (explicit&XkbExplicitInterpretMask) /* nothing to do */
386706f2543Smrg	return TRUE;
387706f2543Smrg    mods= (xkb->map->modmap?xkb->map->modmap[key]:0);
388706f2543Smrg    nSyms= XkbKeyNumSyms(xkb,key);
389706f2543Smrg    syms= XkbKeySymsPtr(xkb,key);
390706f2543Smrg    if (nSyms>IBUF_SIZE) {
391706f2543Smrg	interps= calloc(nSyms, sizeof(XkbSymInterpretPtr));
392706f2543Smrg	if (interps==NULL) {
393706f2543Smrg	    interps= ibuf;
394706f2543Smrg	    nSyms= IBUF_SIZE;
395706f2543Smrg	}
396706f2543Smrg    }
397706f2543Smrg    else {
398706f2543Smrg	interps= ibuf;
399706f2543Smrg    }
400706f2543Smrg    found= 0;
401706f2543Smrg    for (n=0;n<nSyms;n++) {
402706f2543Smrg	unsigned level= (n%XkbKeyGroupsWidth(xkb,key));
403706f2543Smrg	interps[n]= NULL;
404706f2543Smrg	if (syms[n]!=NoSymbol) {
405706f2543Smrg	    interps[n]= _XkbFindMatchingInterp(xkb,syms[n],mods,level);
406706f2543Smrg	    if (interps[n]&&interps[n]->act.type!=XkbSA_NoAction)
407706f2543Smrg		found++;
408706f2543Smrg	    else interps[n]= NULL;
409706f2543Smrg	}
410706f2543Smrg    }
411706f2543Smrg    /* 1/28/96 (ef) -- XXX! WORKING HERE */
412706f2543Smrg    if (!found) {
413706f2543Smrg	if (xkb->server->key_acts[key]!=0) {
414706f2543Smrg	    xkb->server->key_acts[key]= 0;
415706f2543Smrg	    changed|= XkbKeyActionsMask;
416706f2543Smrg	}
417706f2543Smrg    }
418706f2543Smrg    else {
419706f2543Smrg	XkbAction *pActs;
420706f2543Smrg	unsigned int	new_vmodmask;
421706f2543Smrg	changed|= XkbKeyActionsMask;
422706f2543Smrg	pActs= XkbResizeKeyActions(xkb,key,nSyms);
423706f2543Smrg	if (!pActs) {
424706f2543Smrg            if (nSyms > IBUF_SIZE)
425706f2543Smrg                free(interps);
426706f2543Smrg	    return FALSE;
427706f2543Smrg        }
428706f2543Smrg	new_vmodmask= 0;
429706f2543Smrg	for (n=0;n<nSyms;n++) {
430706f2543Smrg	    if (interps[n]) {
431706f2543Smrg		unsigned effMods;
432706f2543Smrg
433706f2543Smrg		pActs[n]= *((XkbAction *)&interps[n]->act);
434706f2543Smrg		if ((n==0)||((interps[n]->match&XkbSI_LevelOneOnly)==0)) {
435706f2543Smrg		     effMods= mods;
436706f2543Smrg		     if (interps[n]->virtual_mod!=XkbNoModifier)
437706f2543Smrg			new_vmodmask|= (1<<interps[n]->virtual_mod);
438706f2543Smrg		}
439706f2543Smrg		else effMods= 0;
440706f2543Smrg		_XkbSetActionKeyMods(xkb,&pActs[n],effMods);
441706f2543Smrg	    }
442706f2543Smrg	    else pActs[n].type= XkbSA_NoAction;
443706f2543Smrg	}
444706f2543Smrg	if (((explicit&XkbExplicitVModMapMask)==0)&&
445706f2543Smrg				(xkb->server->vmodmap[key]!=new_vmodmask)) {
446706f2543Smrg	    changed|= XkbVirtualModMapMask;
447706f2543Smrg	    xkb->server->vmodmap[key]= new_vmodmask;
448706f2543Smrg	}
449706f2543Smrg	if (interps[0]) {
450706f2543Smrg	    if ((interps[0]->flags&XkbSI_LockingKey)&&
451706f2543Smrg				((explicit&XkbExplicitBehaviorMask)==0)) {
452706f2543Smrg		xkb->server->behaviors[key].type= XkbKB_Lock;
453706f2543Smrg		changed|= XkbKeyBehaviorsMask;
454706f2543Smrg	    }
455706f2543Smrg	    if (((explicit&XkbExplicitAutoRepeatMask)==0)&&(xkb->ctrls)) {
456706f2543Smrg		CARD8 old;
457706f2543Smrg		old= BitIsOn(xkb->ctrls->per_key_repeat, key);
458706f2543Smrg		if (interps[0]->flags&XkbSI_AutoRepeat)
459706f2543Smrg		    SetBit(xkb->ctrls->per_key_repeat, key);
460706f2543Smrg		else
461706f2543Smrg		    ClearBit(xkb->ctrls->per_key_repeat, key);
462706f2543Smrg		if (changes && old != BitIsOn(xkb->ctrls->per_key_repeat, key))
463706f2543Smrg		    changes->ctrls.changed_ctrls|= XkbPerKeyRepeatMask;
464706f2543Smrg	    }
465706f2543Smrg	}
466706f2543Smrg    }
467706f2543Smrg    if ((!found)||(interps[0]==NULL)) {
468706f2543Smrg	if (((explicit&XkbExplicitAutoRepeatMask)==0)&&(xkb->ctrls)) {
469706f2543Smrg	    CARD8 old;
470706f2543Smrg	    old = BitIsOn(xkb->ctrls->per_key_repeat, key);
471706f2543Smrg	    SetBit(xkb->ctrls->per_key_repeat, key);
472706f2543Smrg	    if (changes && (old != BitIsOn(xkb->ctrls->per_key_repeat, key)))
473706f2543Smrg		changes->ctrls.changed_ctrls|= XkbPerKeyRepeatMask;
474706f2543Smrg	}
475706f2543Smrg	if (((explicit&XkbExplicitBehaviorMask)==0)&&
476706f2543Smrg		(xkb->server->behaviors[key].type==XkbKB_Lock)) {
477706f2543Smrg		xkb->server->behaviors[key].type= XkbKB_Default;
478706f2543Smrg		changed|= XkbKeyBehaviorsMask;
479706f2543Smrg	}
480706f2543Smrg    }
481706f2543Smrg    if (changes) {
482706f2543Smrg	XkbMapChangesPtr	mc;
483706f2543Smrg	mc= &changes->map;
484706f2543Smrg	tmp= (changed&mc->changed);
485706f2543Smrg	if (tmp&XkbKeyActionsMask)
486706f2543Smrg	    _XkbAddKeyChange(&mc->first_key_act,&mc->num_key_acts,key);
487706f2543Smrg	else if (changed&XkbKeyActionsMask) {
488706f2543Smrg	    mc->changed|= XkbKeyActionsMask;
489706f2543Smrg	    mc->first_key_act= key;
490706f2543Smrg	    mc->num_key_acts= 1;
491706f2543Smrg	}
492706f2543Smrg	if (tmp&XkbKeyBehaviorsMask) {
493706f2543Smrg	    _XkbAddKeyChange(&mc->first_key_behavior,&mc->num_key_behaviors,
494706f2543Smrg	    								key);
495706f2543Smrg	}
496706f2543Smrg	else if (changed&XkbKeyBehaviorsMask) {
497706f2543Smrg	    mc->changed|= XkbKeyBehaviorsMask;
498706f2543Smrg	    mc->first_key_behavior= key;
499706f2543Smrg	    mc->num_key_behaviors= 1;
500706f2543Smrg	}
501706f2543Smrg	if (tmp&XkbVirtualModMapMask)
502706f2543Smrg	    _XkbAddKeyChange(&mc->first_vmodmap_key,&mc->num_vmodmap_keys,key);
503706f2543Smrg	else if (changed&XkbVirtualModMapMask) {
504706f2543Smrg	    mc->changed|= XkbVirtualModMapMask;
505706f2543Smrg	    mc->first_vmodmap_key= key;
506706f2543Smrg	    mc->num_vmodmap_keys= 1;
507706f2543Smrg	}
508706f2543Smrg	mc->changed|= changed;
509706f2543Smrg    }
510706f2543Smrg    if (interps!=ibuf)
511706f2543Smrg	free(interps);
512706f2543Smrg    return TRUE;
513706f2543Smrg}
514706f2543Smrg
515706f2543SmrgStatus
516706f2543SmrgXkbChangeTypesOfKey(	XkbDescPtr		 xkb,
517706f2543Smrg			int		 	 key,
518706f2543Smrg			int			 nGroups,
519706f2543Smrg			unsigned	 	 groups,
520706f2543Smrg			int	* 	 	 newTypesIn,
521706f2543Smrg			XkbMapChangesPtr	 changes)
522706f2543Smrg{
523706f2543SmrgXkbKeyTypePtr	pOldType,pNewType;
524706f2543Smrgregister int	i;
525706f2543Smrgint		width,nOldGroups,oldWidth,newTypes[XkbNumKbdGroups];
526706f2543Smrg
527706f2543Smrg    if ((!xkb) || (!XkbKeycodeInRange(xkb,key)) || (!xkb->map) ||
528706f2543Smrg	(!xkb->map->types)||(!newTypesIn)||((groups&XkbAllGroupsMask)==0)||
529706f2543Smrg	(nGroups>XkbNumKbdGroups)) {
530706f2543Smrg	return BadMatch;
531706f2543Smrg    }
532706f2543Smrg    if (nGroups==0) {
533706f2543Smrg	for (i=0;i<XkbNumKbdGroups;i++) {
534706f2543Smrg	    xkb->map->key_sym_map[key].kt_index[i]= XkbOneLevelIndex;
535706f2543Smrg	}
536706f2543Smrg	i= xkb->map->key_sym_map[key].group_info;
537706f2543Smrg	i= XkbSetNumGroups(i,0);
538706f2543Smrg	xkb->map->key_sym_map[key].group_info= i;
539706f2543Smrg	XkbResizeKeySyms(xkb,key,0);
540706f2543Smrg	return Success;
541706f2543Smrg    }
542706f2543Smrg
543706f2543Smrg    nOldGroups= XkbKeyNumGroups(xkb,key);
544706f2543Smrg    oldWidth= XkbKeyGroupsWidth(xkb,key);
545706f2543Smrg    for (width=i=0;i<nGroups;i++) {
546706f2543Smrg	if (groups&(1<<i))
547706f2543Smrg	     newTypes[i]=  newTypesIn[i];
548706f2543Smrg	else if (i<nOldGroups)
549706f2543Smrg	     newTypes[i]= XkbKeyKeyTypeIndex(xkb,key,i);
550706f2543Smrg	else if (nOldGroups>0)
551706f2543Smrg	     newTypes[i]= XkbKeyKeyTypeIndex(xkb,key,XkbGroup1Index);
552706f2543Smrg	else newTypes[i]= XkbTwoLevelIndex;
553706f2543Smrg	if (newTypes[i]>xkb->map->num_types)
554706f2543Smrg	    return BadMatch;
555706f2543Smrg	pNewType= &xkb->map->types[newTypes[i]];
556706f2543Smrg	if (pNewType->num_levels>width)
557706f2543Smrg	    width= pNewType->num_levels;
558706f2543Smrg    }
559706f2543Smrg    if ((xkb->ctrls)&&(nGroups>xkb->ctrls->num_groups))
560706f2543Smrg	xkb->ctrls->num_groups= nGroups;
561706f2543Smrg    if ((width!=oldWidth)||(nGroups!=nOldGroups)) {
562706f2543Smrg	KeySym		oldSyms[XkbMaxSymsPerKey],*pSyms;
563706f2543Smrg	int		nCopy;
564706f2543Smrg
565706f2543Smrg	if (nOldGroups==0) {
566706f2543Smrg	    pSyms= XkbResizeKeySyms(xkb,key,width*nGroups);
567706f2543Smrg	    if (pSyms!=NULL) {
568706f2543Smrg		i= xkb->map->key_sym_map[key].group_info;
569706f2543Smrg		i= XkbSetNumGroups(i,nGroups);
570706f2543Smrg		xkb->map->key_sym_map[key].group_info= i;
571706f2543Smrg		xkb->map->key_sym_map[key].width= width;
572706f2543Smrg		for (i=0;i<nGroups;i++) {
573706f2543Smrg		    xkb->map->key_sym_map[key].kt_index[i]= newTypes[i];
574706f2543Smrg		}
575706f2543Smrg		return Success;
576706f2543Smrg	    }
577706f2543Smrg	    return BadAlloc;
578706f2543Smrg	}
579706f2543Smrg	pSyms= XkbKeySymsPtr(xkb,key);
580706f2543Smrg	memcpy(oldSyms,pSyms,XkbKeyNumSyms(xkb,key)*sizeof(KeySym));
581706f2543Smrg	pSyms= XkbResizeKeySyms(xkb,key,width*nGroups);
582706f2543Smrg	if (pSyms==NULL)
583706f2543Smrg	    return BadAlloc;
584706f2543Smrg	memset(pSyms, 0, width*nGroups*sizeof(KeySym));
585706f2543Smrg	for (i=0;(i<nGroups)&&(i<nOldGroups);i++) {
586706f2543Smrg	    pOldType= XkbKeyKeyType(xkb,key,i);
587706f2543Smrg	    pNewType= &xkb->map->types[newTypes[i]];
588706f2543Smrg	    if (pNewType->num_levels>pOldType->num_levels)
589706f2543Smrg		 nCopy= pOldType->num_levels;
590706f2543Smrg	    else nCopy= pNewType->num_levels;
591706f2543Smrg	    memcpy(&pSyms[i*width],&oldSyms[i*oldWidth],nCopy*sizeof(KeySym));
592706f2543Smrg	}
593706f2543Smrg	if (XkbKeyHasActions(xkb,key)) {
594706f2543Smrg	    XkbAction	oldActs[XkbMaxSymsPerKey],*pActs;
595706f2543Smrg	    pActs= XkbKeyActionsPtr(xkb,key);
596706f2543Smrg	    memcpy(oldActs,pActs,XkbKeyNumSyms(xkb,key)*sizeof(XkbAction));
597706f2543Smrg	    pActs= XkbResizeKeyActions(xkb,key,width*nGroups);
598706f2543Smrg	    if (pActs==NULL)
599706f2543Smrg		return BadAlloc;
600706f2543Smrg	    memset(pActs, 0, width*nGroups*sizeof(XkbAction));
601706f2543Smrg	    for (i=0;(i<nGroups)&&(i<nOldGroups);i++) {
602706f2543Smrg		pOldType= XkbKeyKeyType(xkb,key,i);
603706f2543Smrg		pNewType= &xkb->map->types[newTypes[i]];
604706f2543Smrg		if (pNewType->num_levels>pOldType->num_levels)
605706f2543Smrg		     nCopy= pOldType->num_levels;
606706f2543Smrg		else nCopy= pNewType->num_levels;
607706f2543Smrg		memcpy(&pActs[i*width],&oldActs[i*oldWidth],
608706f2543Smrg						nCopy*sizeof(XkbAction));
609706f2543Smrg	    }
610706f2543Smrg	}
611706f2543Smrg	i= xkb->map->key_sym_map[key].group_info;
612706f2543Smrg	i= XkbSetNumGroups(i,nGroups);
613706f2543Smrg	xkb->map->key_sym_map[key].group_info= i;
614706f2543Smrg	xkb->map->key_sym_map[key].width= width;
615706f2543Smrg    }
616706f2543Smrg    width= 0;
617706f2543Smrg    for (i=0;i<nGroups;i++) {
618706f2543Smrg	xkb->map->key_sym_map[key].kt_index[i]= newTypes[i];
619706f2543Smrg	if (xkb->map->types[newTypes[i]].num_levels>width)
620706f2543Smrg	    width= xkb->map->types[newTypes[i]].num_levels;
621706f2543Smrg    }
622706f2543Smrg    xkb->map->key_sym_map[key].width= width;
623706f2543Smrg    if (changes!=NULL) {
624706f2543Smrg	if (changes->changed&XkbKeySymsMask) {
625706f2543Smrg	    _XkbAddKeyChange(&changes->first_key_sym,&changes->num_key_syms,
626706f2543Smrg	    								key);
627706f2543Smrg	}
628706f2543Smrg	else {
629706f2543Smrg	    changes->changed|= XkbKeySymsMask;
630706f2543Smrg	    changes->first_key_sym= key;
631706f2543Smrg	    changes->num_key_syms= 1;
632706f2543Smrg	}
633706f2543Smrg    }
634706f2543Smrg    return Success;
635706f2543Smrg}
636706f2543Smrg
637706f2543Smrg/***====================================================================***/
638706f2543Smrg
639706f2543SmrgBool
640706f2543SmrgXkbVirtualModsToReal(XkbDescPtr xkb,unsigned virtual_mask,unsigned *mask_rtrn)
641706f2543Smrg{
642706f2543Smrgregister int i,bit;
643706f2543Smrgregister unsigned mask;
644706f2543Smrg
645706f2543Smrg    if (xkb==NULL)
646706f2543Smrg	return FALSE;
647706f2543Smrg    if (virtual_mask==0) {
648706f2543Smrg	*mask_rtrn= 0;
649706f2543Smrg	return TRUE;
650706f2543Smrg    }
651706f2543Smrg    if (xkb->server==NULL)
652706f2543Smrg	return FALSE;
653706f2543Smrg    for (i=mask=0,bit=1;i<XkbNumVirtualMods;i++,bit<<=1) {
654706f2543Smrg	if (virtual_mask&bit)
655706f2543Smrg	    mask|= xkb->server->vmods[i];
656706f2543Smrg    }
657706f2543Smrg    *mask_rtrn= mask;
658706f2543Smrg    return TRUE;
659706f2543Smrg}
660706f2543Smrg
661706f2543Smrg/***====================================================================***/
662706f2543Smrg
663706f2543Smrgstatic Bool
664706f2543SmrgXkbUpdateActionVirtualMods(XkbDescPtr xkb,XkbAction *act,unsigned changed)
665706f2543Smrg{
666706f2543Smrgunsigned int	tmp;
667706f2543Smrg
668706f2543Smrg    switch (act->type) {
669706f2543Smrg	case XkbSA_SetMods: case XkbSA_LatchMods: case XkbSA_LockMods:
670706f2543Smrg	    if (((tmp= XkbModActionVMods(&act->mods))&changed)!=0) {
671706f2543Smrg		XkbVirtualModsToReal(xkb,tmp,&tmp);
672706f2543Smrg		act->mods.mask= act->mods.real_mods;
673706f2543Smrg		act->mods.mask|= tmp;
674706f2543Smrg		return TRUE;
675706f2543Smrg	    }
676706f2543Smrg	    break;
677706f2543Smrg	case XkbSA_ISOLock:
678706f2543Smrg	    if ((((tmp= XkbModActionVMods(&act->iso))!=0)&changed)!=0) {
679706f2543Smrg		XkbVirtualModsToReal(xkb,tmp,&tmp);
680706f2543Smrg		act->iso.mask= act->iso.real_mods;
681706f2543Smrg		act->iso.mask|= tmp;
682706f2543Smrg		return TRUE;
683706f2543Smrg	    }
684706f2543Smrg	    break;
685706f2543Smrg    }
686706f2543Smrg    return FALSE;
687706f2543Smrg}
688706f2543Smrg
689706f2543Smrgstatic void
690706f2543SmrgXkbUpdateKeyTypeVirtualMods(	XkbDescPtr	xkb,
691706f2543Smrg				XkbKeyTypePtr	type,
692706f2543Smrg				unsigned int	changed,
693706f2543Smrg				XkbChangesPtr 	changes)
694706f2543Smrg{
695706f2543Smrgregister unsigned int	i;
696706f2543Smrgunsigned int		mask;
697706f2543Smrg
698706f2543Smrg    XkbVirtualModsToReal(xkb,type->mods.vmods,&mask);
699706f2543Smrg    type->mods.mask= type->mods.real_mods|mask;
700706f2543Smrg    if ((type->map_count>0)&&(type->mods.vmods!=0)) {
701706f2543Smrg	XkbKTMapEntryPtr entry;
702706f2543Smrg	for (i=0,entry=type->map;i<type->map_count;i++,entry++) {
703706f2543Smrg	    if (entry->mods.vmods!=0) {
704706f2543Smrg		XkbVirtualModsToReal(xkb,entry->mods.vmods,&mask);
705706f2543Smrg		entry->mods.mask=entry->mods.real_mods|mask;
706706f2543Smrg		/* entry is active if vmods are bound*/
707706f2543Smrg	    	entry->active= (mask!=0);
708706f2543Smrg	    }
709706f2543Smrg	    else entry->active= 1;
710706f2543Smrg	}
711706f2543Smrg    }
712706f2543Smrg    if (changes) {
713706f2543Smrg	int	type_ndx;
714706f2543Smrg	type_ndx= type-xkb->map->types;
715706f2543Smrg	if ((type_ndx<0)||(type_ndx>xkb->map->num_types))
716706f2543Smrg	    return;
717706f2543Smrg	if (changes->map.changed&XkbKeyTypesMask) {
718706f2543Smrg	    int last;
719706f2543Smrg	    last= changes->map.first_type+changes->map.num_types-1;
720706f2543Smrg	    if (type_ndx<changes->map.first_type) {
721706f2543Smrg		changes->map.first_type= type_ndx;
722706f2543Smrg		changes->map.num_types= (last-type_ndx)+1;
723706f2543Smrg	    }
724706f2543Smrg	    else if (type_ndx>last) {
725706f2543Smrg		changes->map.num_types= (type_ndx-changes->map.first_type)+1;
726706f2543Smrg	    }
727706f2543Smrg	}
728706f2543Smrg	else {
729706f2543Smrg	    changes->map.changed|= XkbKeyTypesMask;
730706f2543Smrg	    changes->map.first_type= type_ndx;
731706f2543Smrg	    changes->map.num_types= 1;
732706f2543Smrg	}
733706f2543Smrg    }
734706f2543Smrg    return;
735706f2543Smrg}
736706f2543Smrg
737706f2543SmrgBool
738706f2543SmrgXkbApplyVirtualModChanges(XkbDescPtr xkb,unsigned changed,XkbChangesPtr changes)
739706f2543Smrg{
740706f2543Smrgregister int	i;
741706f2543Smrgunsigned int	checkState = 0;
742706f2543Smrg
743706f2543Smrg    if ((!xkb) || (!xkb->map) || (changed==0))
744706f2543Smrg	return FALSE;
745706f2543Smrg    for (i=0;i<xkb->map->num_types;i++) {
746706f2543Smrg	if (xkb->map->types[i].mods.vmods & changed)
747706f2543Smrg	XkbUpdateKeyTypeVirtualMods(xkb,&xkb->map->types[i],changed,changes);
748706f2543Smrg    }
749706f2543Smrg    if (changed&xkb->ctrls->internal.vmods) {
750706f2543Smrg	unsigned int	newMask;
751706f2543Smrg	XkbVirtualModsToReal(xkb,xkb->ctrls->internal.vmods,&newMask);
752706f2543Smrg	newMask|= xkb->ctrls->internal.real_mods;
753706f2543Smrg	if (xkb->ctrls->internal.mask!=newMask) {
754706f2543Smrg	    xkb->ctrls->internal.mask= newMask;
755706f2543Smrg	    if (changes) {
756706f2543Smrg		changes->ctrls.changed_ctrls|= XkbInternalModsMask;
757706f2543Smrg		checkState= TRUE;
758706f2543Smrg	    }
759706f2543Smrg	}
760706f2543Smrg    }
761706f2543Smrg    if (changed&xkb->ctrls->ignore_lock.vmods) {
762706f2543Smrg	unsigned int	newMask;
763706f2543Smrg	XkbVirtualModsToReal(xkb,xkb->ctrls->ignore_lock.vmods,&newMask);
764706f2543Smrg	newMask|= xkb->ctrls->ignore_lock.real_mods;
765706f2543Smrg	if (xkb->ctrls->ignore_lock.mask!=newMask) {
766706f2543Smrg	    xkb->ctrls->ignore_lock.mask= newMask;
767706f2543Smrg	    if (changes) {
768706f2543Smrg		changes->ctrls.changed_ctrls|= XkbIgnoreLockModsMask;
769706f2543Smrg		checkState= TRUE;
770706f2543Smrg	    }
771706f2543Smrg	}
772706f2543Smrg    }
773706f2543Smrg    if (xkb->indicators!=NULL) {
774706f2543Smrg	XkbIndicatorMapPtr map;
775706f2543Smrg	map= &xkb->indicators->maps[0];
776706f2543Smrg	for (i=0;i<XkbNumIndicators;i++,map++) {
777706f2543Smrg	    if (map->mods.vmods&changed) {
778706f2543Smrg		unsigned int newMask;
779706f2543Smrg		XkbVirtualModsToReal(xkb,map->mods.vmods,&newMask);
780706f2543Smrg		newMask|= map->mods.real_mods;
781706f2543Smrg		if (newMask!=map->mods.mask) {
782706f2543Smrg		    map->mods.mask= newMask;
783706f2543Smrg		    if (changes) {
784706f2543Smrg			changes->indicators.map_changes|= (1<<i);
785706f2543Smrg			checkState= TRUE;
786706f2543Smrg		    }
787706f2543Smrg		}
788706f2543Smrg	    }
789706f2543Smrg	}
790706f2543Smrg    }
791706f2543Smrg    if (xkb->compat!=NULL) {
792706f2543Smrg	XkbCompatMapPtr	compat;
793706f2543Smrg	compat= xkb->compat;
794706f2543Smrg	for (i=0;i<XkbNumKbdGroups;i++) {
795706f2543Smrg	    unsigned int newMask;
796706f2543Smrg	    XkbVirtualModsToReal(xkb,compat->groups[i].vmods,&newMask);
797706f2543Smrg	    newMask|= compat->groups[i].real_mods;
798706f2543Smrg	    if (compat->groups[i].mask!=newMask) {
799706f2543Smrg		compat->groups[i].mask= newMask;
800706f2543Smrg		if (changes) {
801706f2543Smrg		    changes->compat.changed_groups|= (1<<i);
802706f2543Smrg		    checkState= TRUE;
803706f2543Smrg		}
804706f2543Smrg	    }
805706f2543Smrg	}
806706f2543Smrg    }
807706f2543Smrg    if (xkb->map && xkb->server) {
808706f2543Smrg	int highChange = 0, lowChange = -1;
809706f2543Smrg	for (i=xkb->min_key_code;i<=xkb->max_key_code;i++) {
810706f2543Smrg	    if (XkbKeyHasActions(xkb,i)) {
811706f2543Smrg		register XkbAction *pAct;
812706f2543Smrg		register int n;
813706f2543Smrg
814706f2543Smrg		pAct= XkbKeyActionsPtr(xkb,i);
815706f2543Smrg		for (n=XkbKeyNumActions(xkb,i);n>0;n--,pAct++) {
816706f2543Smrg		    if ((pAct->type!=XkbSA_NoAction)&&
817706f2543Smrg			XkbUpdateActionVirtualMods(xkb,pAct,changed)) {
818706f2543Smrg			if (lowChange<0)
819706f2543Smrg			    lowChange= i;
820706f2543Smrg			highChange= i;
821706f2543Smrg		    }
822706f2543Smrg		}
823706f2543Smrg	    }
824706f2543Smrg	}
825706f2543Smrg	if (changes && (lowChange>0)) { /* something changed */
826706f2543Smrg	    if (changes->map.changed&XkbKeyActionsMask) {
827706f2543Smrg		int last;
828706f2543Smrg		if (changes->map.first_key_act<lowChange)
829706f2543Smrg		    lowChange= changes->map.first_key_act;
830706f2543Smrg		last= changes->map.first_key_act+changes->map.num_key_acts-1;
831706f2543Smrg		if (last>highChange)
832706f2543Smrg		    highChange= last;
833706f2543Smrg	    }
834706f2543Smrg	    changes->map.changed|= 	XkbKeyActionsMask;
835706f2543Smrg	    changes->map.first_key_act=	lowChange;
836706f2543Smrg	    changes->map.num_key_acts=	(highChange-lowChange)+1;
837706f2543Smrg	}
838706f2543Smrg    }
839706f2543Smrg    return checkState;
840706f2543Smrg}
841