xkbUtils.c revision c82838c1
1/************************************************************
2Copyright (c) 1993 by Silicon Graphics Computer Systems, Inc.
3
4Permission to use, copy, modify, and distribute this
5software and its documentation for any purpose and without
6fee is hereby granted, provided that the above copyright
7notice appear in all copies and that both that copyright
8notice and this permission notice appear in supporting
9documentation, and that the name of Silicon Graphics not be
10used in advertising or publicity pertaining to distribution
11of the software without specific prior written permission.
12Silicon Graphics makes no representation about the suitability
13of this software for any purpose. It is provided "as is"
14without any express or implied warranty.
15
16SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
17SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
18AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON
19GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
20DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
21DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
22OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION  WITH
23THE USE OR PERFORMANCE OF THIS SOFTWARE.
24
25********************************************************/
26/*
27
28Copyright © 2008 Red Hat Inc.
29
30Permission is hereby granted, free of charge, to any person obtaining a
31copy of this software and associated documentation files (the "Software"),
32to deal in the Software without restriction, including without limitation
33the rights to use, copy, modify, merge, publish, distribute, sublicense,
34and/or sell copies of the Software, and to permit persons to whom the
35Software is furnished to do so, subject to the following conditions:
36
37The above copyright notice and this permission notice (including the next
38paragraph) shall be included in all copies or substantial portions of the
39Software.
40
41THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
42IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
43FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
44THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
45LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
46FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
47DEALINGS IN THE SOFTWARE.
48
49*/
50
51#ifdef HAVE_DIX_CONFIG_H
52#include <dix-config.h>
53#endif
54
55#include "os.h"
56#include <stdio.h>
57#include <ctype.h>
58#include <math.h>
59#include <X11/X.h>
60#include <X11/Xproto.h>
61#define	XK_CYRILLIC
62#include <X11/keysym.h>
63#include "misc.h"
64#include "inputstr.h"
65#include "eventstr.h"
66
67#define	XKBSRV_NEED_FILE_FUNCS
68#include <xkbsrv.h>
69#include "xkbgeom.h"
70#include "xkb.h"
71
72/***====================================================================***/
73
74int
75_XkbLookupAnyDevice(DeviceIntPtr *pDev, int id, ClientPtr client,
76                    Mask access_mode, int *xkb_err)
77{
78    int rc = XkbKeyboardErrorCode;
79
80    if (id == XkbUseCoreKbd)
81        id = PickKeyboard(client)->id;
82    else if (id == XkbUseCorePtr)
83        id = PickPointer(client)->id;
84
85    rc = dixLookupDevice(pDev, id, client, access_mode);
86    if (rc != Success)
87        *xkb_err = XkbErr_BadDevice;
88
89    return rc;
90}
91
92int
93_XkbLookupKeyboard(DeviceIntPtr *pDev, int id, ClientPtr client,
94                   Mask access_mode, int *xkb_err)
95{
96    DeviceIntPtr dev;
97    int rc;
98
99    if (id == XkbDfltXIId)
100        id = XkbUseCoreKbd;
101
102    rc = _XkbLookupAnyDevice(pDev, id, client, access_mode, xkb_err);
103    if (rc != Success)
104        return rc;
105
106    dev = *pDev;
107    if (!dev->key || !dev->key->xkbInfo) {
108        *pDev = NULL;
109        *xkb_err = XkbErr_BadClass;
110        return XkbKeyboardErrorCode;
111    }
112    return Success;
113}
114
115int
116_XkbLookupBellDevice(DeviceIntPtr *pDev, int id, ClientPtr client,
117                     Mask access_mode, int *xkb_err)
118{
119    DeviceIntPtr dev;
120    int rc;
121
122    rc = _XkbLookupAnyDevice(pDev, id, client, access_mode, xkb_err);
123    if (rc != Success)
124        return rc;
125
126    dev = *pDev;
127    if (!dev->kbdfeed && !dev->bell) {
128        *pDev = NULL;
129        *xkb_err = XkbErr_BadClass;
130        return XkbKeyboardErrorCode;
131    }
132    return Success;
133}
134
135int
136_XkbLookupLedDevice(DeviceIntPtr *pDev, int id, ClientPtr client,
137                    Mask access_mode, int *xkb_err)
138{
139    DeviceIntPtr dev;
140    int rc;
141
142    if (id == XkbDfltXIId)
143        id = XkbUseCorePtr;
144
145    rc = _XkbLookupAnyDevice(pDev, id, client, access_mode, xkb_err);
146    if (rc != Success)
147        return rc;
148
149    dev = *pDev;
150    if (!dev->kbdfeed && !dev->leds) {
151        *pDev = NULL;
152        *xkb_err = XkbErr_BadClass;
153        return XkbKeyboardErrorCode;
154    }
155    return Success;
156}
157
158int
159_XkbLookupButtonDevice(DeviceIntPtr *pDev, int id, ClientPtr client,
160                       Mask access_mode, int *xkb_err)
161{
162    DeviceIntPtr dev;
163    int rc;
164
165    rc = _XkbLookupAnyDevice(pDev, id, client, access_mode, xkb_err);
166    if (rc != Success)
167        return rc;
168
169    dev = *pDev;
170    if (!dev->button) {
171        *pDev = NULL;
172        *xkb_err = XkbErr_BadClass;
173        return XkbKeyboardErrorCode;
174    }
175    return Success;
176}
177
178void
179XkbSetActionKeyMods(XkbDescPtr xkb, XkbAction *act, unsigned mods)
180{
181    register unsigned tmp;
182
183    switch (act->type) {
184    case XkbSA_SetMods:
185    case XkbSA_LatchMods:
186    case XkbSA_LockMods:
187        if (act->mods.flags & XkbSA_UseModMapMods)
188            act->mods.real_mods = act->mods.mask = mods;
189        if ((tmp = XkbModActionVMods(&act->mods)) != 0)
190            act->mods.mask |= XkbMaskForVMask(xkb, tmp);
191        break;
192    case XkbSA_ISOLock:
193        if (act->iso.flags & XkbSA_UseModMapMods)
194            act->iso.real_mods = act->iso.mask = mods;
195        if ((tmp = XkbModActionVMods(&act->iso)) != 0)
196            act->iso.mask |= XkbMaskForVMask(xkb, tmp);
197        break;
198    }
199    return;
200}
201
202unsigned
203XkbMaskForVMask(XkbDescPtr xkb, unsigned vmask)
204{
205    register int i, bit;
206    register unsigned mask;
207
208    for (mask = i = 0, bit = 1; i < XkbNumVirtualMods; i++, bit <<= 1) {
209        if (vmask & bit)
210            mask |= xkb->server->vmods[i];
211    }
212    return mask;
213}
214
215/***====================================================================***/
216
217void
218XkbUpdateKeyTypesFromCore(DeviceIntPtr pXDev,
219                          KeySymsPtr pCore,
220                          KeyCode first, CARD8 num, XkbChangesPtr changes)
221{
222    XkbDescPtr xkb;
223    unsigned key, nG, explicit;
224    int types[XkbNumKbdGroups];
225    KeySym tsyms[XkbMaxSymsPerKey] = {NoSymbol}, *syms;
226    XkbMapChangesPtr mc;
227
228    xkb = pXDev->key->xkbInfo->desc;
229    if (first + num - 1 > xkb->max_key_code) {
230        /* 1/12/95 (ef) -- XXX! should allow XKB structures to grow */
231        num = xkb->max_key_code - first + 1;
232    }
233
234    mc = (changes ? (&changes->map) : NULL);
235
236    syms = &pCore->map[(first - pCore->minKeyCode) * pCore->mapWidth];
237    for (key = first; key < (first + num); key++, syms += pCore->mapWidth) {
238        explicit = xkb->server->explicit[key] & XkbExplicitKeyTypesMask;
239        types[XkbGroup1Index] = XkbKeyKeyTypeIndex(xkb, key, XkbGroup1Index);
240        types[XkbGroup2Index] = XkbKeyKeyTypeIndex(xkb, key, XkbGroup2Index);
241        types[XkbGroup3Index] = XkbKeyKeyTypeIndex(xkb, key, XkbGroup3Index);
242        types[XkbGroup4Index] = XkbKeyKeyTypeIndex(xkb, key, XkbGroup4Index);
243        nG = XkbKeyTypesForCoreSymbols(xkb, pCore->mapWidth, syms, explicit,
244                                       types, tsyms);
245        XkbChangeTypesOfKey(xkb, key, nG, XkbAllGroupsMask, types, mc);
246        memcpy((char *) XkbKeySymsPtr(xkb, key), (char *) tsyms,
247               XkbKeyNumSyms(xkb, key) * sizeof(KeySym));
248    }
249    if (changes->map.changed & XkbKeySymsMask) {
250        CARD8 oldLast, newLast;
251
252        oldLast = changes->map.first_key_sym + changes->map.num_key_syms - 1;
253        newLast = first + num - 1;
254
255        if (first < changes->map.first_key_sym)
256            changes->map.first_key_sym = first;
257        if (oldLast > newLast)
258            newLast = oldLast;
259        changes->map.num_key_syms = newLast - changes->map.first_key_sym + 1;
260    }
261    else {
262        changes->map.changed |= XkbKeySymsMask;
263        changes->map.first_key_sym = first;
264        changes->map.num_key_syms = num;
265    }
266    return;
267}
268
269void
270XkbUpdateDescActions(XkbDescPtr xkb,
271                     KeyCode first, CARD8 num, XkbChangesPtr changes)
272{
273    register unsigned key;
274
275    for (key = first; key < (first + num); key++) {
276        XkbApplyCompatMapToKey(xkb, key, changes);
277    }
278
279    if (changes->map.changed & (XkbVirtualModMapMask | XkbModifierMapMask)) {
280        unsigned char newVMods[XkbNumVirtualMods];
281        register unsigned bit, i;
282        unsigned present;
283
284        memset(newVMods, 0, XkbNumVirtualMods);
285        present = 0;
286        for (key = xkb->min_key_code; key <= xkb->max_key_code; key++) {
287            if (xkb->server->vmodmap[key] == 0)
288                continue;
289            for (i = 0, bit = 1; i < XkbNumVirtualMods; i++, bit <<= 1) {
290                if (bit & xkb->server->vmodmap[key]) {
291                    present |= bit;
292                    newVMods[i] |= xkb->map->modmap[key];
293                }
294            }
295        }
296        for (i = 0, bit = 1; i < XkbNumVirtualMods; i++, bit <<= 1) {
297            if ((bit & present) && (newVMods[i] != xkb->server->vmods[i])) {
298                changes->map.changed |= XkbVirtualModsMask;
299                changes->map.vmods |= bit;
300                xkb->server->vmods[i] = newVMods[i];
301            }
302        }
303    }
304    if (changes->map.changed & XkbVirtualModsMask)
305        XkbApplyVirtualModChanges(xkb, changes->map.vmods, changes);
306
307    if (changes->map.changed & XkbKeyActionsMask) {
308        CARD8 oldLast, newLast;
309
310        oldLast = changes->map.first_key_act + changes->map.num_key_acts - 1;
311        newLast = first + num - 1;
312
313        if (first < changes->map.first_key_act)
314            changes->map.first_key_act = first;
315        if (newLast > oldLast)
316            newLast = oldLast;
317        changes->map.num_key_acts = newLast - changes->map.first_key_act + 1;
318    }
319    else {
320        changes->map.changed |= XkbKeyActionsMask;
321        changes->map.first_key_act = first;
322        changes->map.num_key_acts = num;
323    }
324    return;
325}
326
327void
328XkbUpdateActions(DeviceIntPtr pXDev,
329                 KeyCode first,
330                 CARD8 num,
331                 XkbChangesPtr changes,
332                 unsigned *needChecksRtrn, XkbEventCausePtr cause)
333{
334    XkbSrvInfoPtr xkbi;
335    XkbDescPtr xkb;
336    CARD8 *repeat;
337
338    if (needChecksRtrn)
339        *needChecksRtrn = 0;
340    xkbi = pXDev->key->xkbInfo;
341    xkb = xkbi->desc;
342    repeat = xkb->ctrls->per_key_repeat;
343
344    /* before letting XKB do any changes, copy the current core values */
345    if (pXDev->kbdfeed)
346        memcpy(repeat, pXDev->kbdfeed->ctrl.autoRepeats, XkbPerKeyBitArraySize);
347
348    XkbUpdateDescActions(xkb, first, num, changes);
349
350    if ((pXDev->kbdfeed) &&
351        (changes->ctrls.changed_ctrls & XkbPerKeyRepeatMask)) {
352        /* now copy the modified changes back to core */
353        memcpy(pXDev->kbdfeed->ctrl.autoRepeats, repeat, XkbPerKeyBitArraySize);
354        if (pXDev->kbdfeed->CtrlProc)
355            (*pXDev->kbdfeed->CtrlProc) (pXDev, &pXDev->kbdfeed->ctrl);
356    }
357    return;
358}
359
360KeySymsPtr
361XkbGetCoreMap(DeviceIntPtr keybd)
362{
363    register int key, tmp;
364    int maxSymsPerKey, maxGroup1Width;
365    XkbDescPtr xkb;
366    KeySymsPtr syms;
367    int maxNumberOfGroups;
368
369    if (!keybd || !keybd->key || !keybd->key->xkbInfo)
370        return NULL;
371
372    xkb = keybd->key->xkbInfo->desc;
373    maxSymsPerKey = maxGroup1Width = 0;
374    maxNumberOfGroups = 0;
375
376    /* determine sizes */
377    for (key = xkb->min_key_code; key <= xkb->max_key_code; key++) {
378        if (XkbKeycodeInRange(xkb, key)) {
379            int nGroups;
380            int w;
381
382            nGroups = XkbKeyNumGroups(xkb, key);
383            tmp = 0;
384            if (nGroups > 0) {
385                if ((w = XkbKeyGroupWidth(xkb, key, XkbGroup1Index)) <= 2)
386                    tmp += 2;
387                else
388                    tmp += w + 2;
389                /* remember highest G1 width */
390                if (w > maxGroup1Width)
391                    maxGroup1Width = w;
392            }
393            if (nGroups > 1) {
394                if (tmp <= 2) {
395                    if ((w = XkbKeyGroupWidth(xkb, key, XkbGroup2Index)) < 2)
396                        tmp += 2;
397                    else
398                        tmp += w;
399                }
400                else {
401                    if ((w = XkbKeyGroupWidth(xkb, key, XkbGroup2Index)) > 2)
402                        tmp += w - 2;
403                }
404            }
405            if (nGroups > 2)
406                tmp += XkbKeyGroupWidth(xkb, key, XkbGroup3Index);
407            if (nGroups > 3)
408                tmp += XkbKeyGroupWidth(xkb, key, XkbGroup4Index);
409            if (tmp > maxSymsPerKey)
410                maxSymsPerKey = tmp;
411            if (nGroups > maxNumberOfGroups)
412                maxNumberOfGroups = nGroups;
413        }
414    }
415
416    if (maxSymsPerKey <= 0)
417        return NULL;
418
419    syms = calloc(1, sizeof(*syms));
420    if (!syms)
421        return NULL;
422
423    /* See Section 12.4 of the XKB Protocol spec. Because of the
424     * single-group distribution for multi-group keyboards, we have to
425     * have enough symbols for the largest group 1 to replicate across the
426     * number of groups on the keyboard. e.g. a single-group key with 4
427     * symbols on a keyboard that has 3 groups -> 12 syms per key */
428    if (maxSymsPerKey < maxNumberOfGroups * maxGroup1Width)
429        maxSymsPerKey = maxNumberOfGroups * maxGroup1Width;
430
431    syms->mapWidth = maxSymsPerKey;
432    syms->minKeyCode = xkb->min_key_code;
433    syms->maxKeyCode = xkb->max_key_code;
434
435    tmp = syms->mapWidth * (xkb->max_key_code - xkb->min_key_code + 1);
436    syms->map = calloc(tmp, sizeof(*syms->map));
437    if (!syms->map) {
438        free(syms);
439        return NULL;
440    }
441
442    for (key = xkb->min_key_code; key <= xkb->max_key_code; key++) {
443        KeySym *pCore, *pXKB;
444        unsigned nGroups, groupWidth, n, nOut;
445
446        nGroups = XkbKeyNumGroups(xkb, key);
447        n = (key - xkb->min_key_code) * syms->mapWidth;
448        pCore = &syms->map[n];
449        pXKB = XkbKeySymsPtr(xkb, key);
450        nOut = 2;
451        if (nGroups > 0) {
452            groupWidth = XkbKeyGroupWidth(xkb, key, XkbGroup1Index);
453            if (groupWidth > 0)
454                pCore[0] = pXKB[0];
455            if (groupWidth > 1)
456                pCore[1] = pXKB[1];
457            for (n = 2; n < groupWidth; n++)
458                pCore[2 + n] = pXKB[n];
459            if (groupWidth > 2)
460                nOut = groupWidth;
461        }
462
463        /* See XKB Protocol Sec, Section 12.4.
464           A 1-group key with ABCDE on a 2 group keyboard must be
465           duplicated across all groups as ABABCDECDE.
466         */
467        if (nGroups == 1) {
468            int idx, j;
469
470            groupWidth = XkbKeyGroupWidth(xkb, key, XkbGroup1Index);
471
472            /* AB..CDE... -> ABABCDE... */
473            if (groupWidth > 0 && syms->mapWidth >= 3)
474                pCore[2] = pCore[0];
475            if (groupWidth > 1 && syms->mapWidth >= 4)
476                pCore[3] = pCore[1];
477
478            /* ABABCDE... -> ABABCDECDE */
479            idx = 2 + groupWidth;
480            while (groupWidth > 2 && idx < syms->mapWidth &&
481                   idx < groupWidth * 2) {
482                pCore[idx] = pCore[idx - groupWidth + 2];
483                idx++;
484            }
485            idx = 2 * groupWidth;
486            if (idx < 4)
487                idx = 4;
488            /* 3 or more groups: ABABCDECDEABCDEABCDE */
489            for (j = 3; j <= maxNumberOfGroups; j++)
490                for (n = 0; n < groupWidth && idx < maxSymsPerKey; n++)
491                    pCore[idx++] = pXKB[n];
492        }
493
494        pXKB += XkbKeyGroupsWidth(xkb, key);
495        nOut += 2;
496        if (nGroups > 1) {
497            groupWidth = XkbKeyGroupWidth(xkb, key, XkbGroup2Index);
498            if (groupWidth > 0)
499                pCore[2] = pXKB[0];
500            if (groupWidth > 1)
501                pCore[3] = pXKB[1];
502            for (n = 2; n < groupWidth; n++) {
503                pCore[nOut + (n - 2)] = pXKB[n];
504            }
505            if (groupWidth > 2)
506                nOut += (groupWidth - 2);
507        }
508        pXKB += XkbKeyGroupsWidth(xkb, key);
509        for (n = XkbGroup3Index; n < nGroups; n++) {
510            register int s;
511
512            groupWidth = XkbKeyGroupWidth(xkb, key, n);
513            for (s = 0; s < groupWidth; s++) {
514                pCore[nOut++] = pXKB[s];
515            }
516            pXKB += XkbKeyGroupsWidth(xkb, key);
517        }
518    }
519
520    return syms;
521}
522
523void
524XkbSetRepeatKeys(DeviceIntPtr pXDev, int key, int onoff)
525{
526    if (pXDev && pXDev->key && pXDev->key->xkbInfo) {
527        xkbControlsNotify cn;
528        XkbControlsPtr ctrls = pXDev->key->xkbInfo->desc->ctrls;
529        XkbControlsRec old;
530
531        old = *ctrls;
532
533        if (key == -1) {        /* global autorepeat setting changed */
534            if (onoff)
535                ctrls->enabled_ctrls |= XkbRepeatKeysMask;
536            else
537                ctrls->enabled_ctrls &= ~XkbRepeatKeysMask;
538        }
539        else if (pXDev->kbdfeed) {
540            ctrls->per_key_repeat[key / 8] =
541                pXDev->kbdfeed->ctrl.autoRepeats[key / 8];
542        }
543
544        if (XkbComputeControlsNotify(pXDev, &old, ctrls, &cn, TRUE))
545            XkbSendControlsNotify(pXDev, &cn);
546    }
547    return;
548}
549
550/* Applies a change to a single device, does not traverse the device tree. */
551void
552XkbApplyMappingChange(DeviceIntPtr kbd, KeySymsPtr map, KeyCode first_key,
553                      CARD8 num_keys, CARD8 *modmap, ClientPtr client)
554{
555    XkbDescPtr xkb = kbd->key->xkbInfo->desc;
556    XkbEventCauseRec cause;
557    XkbChangesRec changes;
558    unsigned int check;
559
560    memset(&changes, 0, sizeof(changes));
561    memset(&cause, 0, sizeof(cause));
562
563    if (map && first_key && num_keys) {
564        check = 0;
565        XkbSetCauseCoreReq(&cause, X_ChangeKeyboardMapping, client);
566
567        XkbUpdateKeyTypesFromCore(kbd, map, first_key, num_keys, &changes);
568        XkbUpdateActions(kbd, first_key, num_keys, &changes, &check, &cause);
569
570        if (check)
571            XkbCheckSecondaryEffects(kbd->key->xkbInfo, 1, &changes, &cause);
572    }
573
574    if (modmap) {
575        /* A keymap change can imply a modmap change, se we prefer the
576         * former. */
577        if (!cause.mjr)
578            XkbSetCauseCoreReq(&cause, X_SetModifierMapping, client);
579
580        check = 0;
581        num_keys = xkb->max_key_code - xkb->min_key_code + 1;
582        changes.map.changed |= XkbModifierMapMask;
583        changes.map.first_modmap_key = xkb->min_key_code;
584        changes.map.num_modmap_keys = num_keys;
585        memcpy(kbd->key->xkbInfo->desc->map->modmap, modmap, MAP_LENGTH);
586        XkbUpdateActions(kbd, xkb->min_key_code, num_keys, &changes, &check,
587                         &cause);
588
589        if (check)
590            XkbCheckSecondaryEffects(kbd->key->xkbInfo, 1, &changes, &cause);
591    }
592
593    XkbSendNotification(kbd, &changes, &cause);
594}
595
596void
597XkbDisableComputedAutoRepeats(DeviceIntPtr dev, unsigned key)
598{
599    XkbSrvInfoPtr xkbi = dev->key->xkbInfo;
600    xkbMapNotify mn;
601
602    xkbi->desc->server->explicit[key] |= XkbExplicitAutoRepeatMask;
603    memset(&mn, 0, sizeof(mn));
604    mn.changed = XkbExplicitComponentsMask;
605    mn.firstKeyExplicit = key;
606    mn.nKeyExplicit = 1;
607    XkbSendMapNotify(dev, &mn);
608    return;
609}
610
611unsigned
612XkbStateChangedFlags(XkbStatePtr old, XkbStatePtr new)
613{
614    int changed;
615
616    changed = (old->group != new->group ? XkbGroupStateMask : 0);
617    changed |= (old->base_group != new->base_group ? XkbGroupBaseMask : 0);
618    changed |=
619        (old->latched_group != new->latched_group ? XkbGroupLatchMask : 0);
620    changed |= (old->locked_group != new->locked_group ? XkbGroupLockMask : 0);
621    changed |= (old->mods != new->mods ? XkbModifierStateMask : 0);
622    changed |= (old->base_mods != new->base_mods ? XkbModifierBaseMask : 0);
623    changed |=
624        (old->latched_mods != new->latched_mods ? XkbModifierLatchMask : 0);
625    changed |= (old->locked_mods != new->locked_mods ? XkbModifierLockMask : 0);
626    changed |=
627        (old->compat_state != new->compat_state ? XkbCompatStateMask : 0);
628    changed |= (old->grab_mods != new->grab_mods ? XkbGrabModsMask : 0);
629    if (old->compat_grab_mods != new->compat_grab_mods)
630        changed |= XkbCompatGrabModsMask;
631    changed |= (old->lookup_mods != new->lookup_mods ? XkbLookupModsMask : 0);
632    if (old->compat_lookup_mods != new->compat_lookup_mods)
633        changed |= XkbCompatLookupModsMask;
634    changed |=
635        (old->ptr_buttons != new->ptr_buttons ? XkbPointerButtonMask : 0);
636    return changed;
637}
638
639static void
640XkbComputeCompatState(XkbSrvInfoPtr xkbi)
641{
642    CARD16 grp_mask;
643    XkbStatePtr state = &xkbi->state;
644    XkbCompatMapPtr map;
645    XkbControlsPtr ctrls;
646
647    if (!state || !xkbi->desc || !xkbi->desc->ctrls || !xkbi->desc->compat)
648        return;
649
650    map = xkbi->desc->compat;
651    grp_mask = map->groups[state->group].mask;
652    state->compat_state = state->mods | grp_mask;
653    state->compat_lookup_mods = state->lookup_mods | grp_mask;
654    ctrls= xkbi->desc->ctrls;
655
656    if (ctrls->enabled_ctrls & XkbIgnoreGroupLockMask) {
657	unsigned char grp = state->base_group+state->latched_group;
658	if (grp >= ctrls->num_groups)
659	    grp = XkbAdjustGroup(XkbCharToInt(grp), ctrls);
660        grp_mask = map->groups[grp].mask;
661    }
662    state->compat_grab_mods = state->grab_mods | grp_mask;
663    return;
664}
665
666unsigned
667XkbAdjustGroup(int group, XkbControlsPtr ctrls)
668{
669    unsigned act;
670
671    act = XkbOutOfRangeGroupAction(ctrls->groups_wrap);
672    if (group < 0) {
673        while (group < 0) {
674            if (act == XkbClampIntoRange) {
675                group = XkbGroup1Index;
676            }
677            else if (act == XkbRedirectIntoRange) {
678                int newGroup;
679
680                newGroup = XkbOutOfRangeGroupNumber(ctrls->groups_wrap);
681                if (newGroup >= ctrls->num_groups)
682                    group = XkbGroup1Index;
683                else
684                    group = newGroup;
685            }
686            else {
687                group += ctrls->num_groups;
688            }
689        }
690    }
691    else if (group >= ctrls->num_groups) {
692        if (act == XkbClampIntoRange) {
693            group = ctrls->num_groups - 1;
694        }
695        else if (act == XkbRedirectIntoRange) {
696            int newGroup;
697
698            newGroup = XkbOutOfRangeGroupNumber(ctrls->groups_wrap);
699            if (newGroup >= ctrls->num_groups)
700                group = XkbGroup1Index;
701            else
702                group = newGroup;
703        }
704        else {
705            group %= ctrls->num_groups;
706        }
707    }
708    return group;
709}
710
711void
712XkbComputeDerivedState(XkbSrvInfoPtr xkbi)
713{
714    XkbStatePtr state = &xkbi->state;
715    XkbControlsPtr ctrls = xkbi->desc->ctrls;
716    unsigned char grp;
717
718    if (!state || !ctrls)
719        return;
720
721    state->mods = (state->base_mods | state->latched_mods | state->locked_mods);
722    state->lookup_mods = state->mods & (~ctrls->internal.mask);
723    state->grab_mods = state->lookup_mods & (~ctrls->ignore_lock.mask);
724    state->grab_mods |=
725        ((state->base_mods | state->latched_mods) & ctrls->ignore_lock.mask);
726
727    grp = state->locked_group;
728    if (grp >= ctrls->num_groups)
729        state->locked_group = XkbAdjustGroup(XkbCharToInt(grp), ctrls);
730
731    grp = state->locked_group + state->base_group + state->latched_group;
732    if (grp >= ctrls->num_groups)
733        state->group = XkbAdjustGroup(XkbCharToInt(grp), ctrls);
734    else
735        state->group = grp;
736    XkbComputeCompatState(xkbi);
737    return;
738}
739
740/***====================================================================***/
741
742void
743XkbCheckSecondaryEffects(XkbSrvInfoPtr xkbi,
744                         unsigned which,
745                         XkbChangesPtr changes, XkbEventCausePtr cause)
746{
747    if (which & XkbStateNotifyMask) {
748        XkbStateRec old;
749
750        old = xkbi->state;
751        changes->state_changes |= XkbStateChangedFlags(&old, &xkbi->state);
752        XkbComputeDerivedState(xkbi);
753    }
754    if (which & XkbIndicatorStateNotifyMask)
755        XkbUpdateIndicators(xkbi->device, XkbAllIndicatorsMask, TRUE, changes,
756                            cause);
757    return;
758}
759
760/***====================================================================***/
761
762Bool
763XkbEnableDisableControls(XkbSrvInfoPtr xkbi,
764                         unsigned long change,
765                         unsigned long newValues,
766                         XkbChangesPtr changes, XkbEventCausePtr cause)
767{
768    XkbControlsPtr ctrls;
769    unsigned old;
770    XkbSrvLedInfoPtr sli;
771
772    ctrls = xkbi->desc->ctrls;
773    old = ctrls->enabled_ctrls;
774    ctrls->enabled_ctrls &= ~change;
775    ctrls->enabled_ctrls |= (change & newValues);
776    if (old == ctrls->enabled_ctrls)
777        return FALSE;
778    if (cause != NULL) {
779        xkbControlsNotify cn;
780
781        cn.numGroups = ctrls->num_groups;
782        cn.changedControls = XkbControlsEnabledMask;
783        cn.enabledControls = ctrls->enabled_ctrls;
784        cn.enabledControlChanges = (ctrls->enabled_ctrls ^ old);
785        cn.keycode = cause->kc;
786        cn.eventType = cause->event;
787        cn.requestMajor = cause->mjr;
788        cn.requestMinor = cause->mnr;
789        XkbSendControlsNotify(xkbi->device, &cn);
790    }
791    else {
792        /* Yes, this really should be an XOR.  If ctrls->enabled_ctrls_changes */
793        /* is non-zero, the controls in question changed already in "this" */
794        /* request and this change merely undoes the previous one.  By the */
795        /* same token, we have to figure out whether or not ControlsEnabled */
796        /* should be set or not in the changes structure */
797        changes->ctrls.enabled_ctrls_changes ^= (ctrls->enabled_ctrls ^ old);
798        if (changes->ctrls.enabled_ctrls_changes)
799            changes->ctrls.changed_ctrls |= XkbControlsEnabledMask;
800        else
801            changes->ctrls.changed_ctrls &= ~XkbControlsEnabledMask;
802    }
803    sli = XkbFindSrvLedInfo(xkbi->device, XkbDfltXIClass, XkbDfltXIId, 0);
804    XkbUpdateIndicators(xkbi->device, sli->usesControls, TRUE, changes, cause);
805    return TRUE;
806}
807
808/***====================================================================***/
809
810#define	MAX_TOC	16
811
812XkbGeometryPtr
813XkbLookupNamedGeometry(DeviceIntPtr dev, Atom name, Bool *shouldFree)
814{
815    XkbSrvInfoPtr xkbi = dev->key->xkbInfo;
816    XkbDescPtr xkb = xkbi->desc;
817
818    *shouldFree = 0;
819    if (name == None) {
820        if (xkb->geom != NULL)
821            return xkb->geom;
822        name = xkb->names->geometry;
823    }
824    if ((xkb->geom != NULL) && (xkb->geom->name == name))
825        return xkb->geom;
826    *shouldFree = 1;
827    return NULL;
828}
829
830void
831XkbConvertCase(register KeySym sym, KeySym * lower, KeySym * upper)
832{
833    *lower = sym;
834    *upper = sym;
835    switch (sym >> 8) {
836    case 0:                    /* Latin 1 */
837        if ((sym >= XK_A) && (sym <= XK_Z))
838            *lower += (XK_a - XK_A);
839        else if ((sym >= XK_a) && (sym <= XK_z))
840            *upper -= (XK_a - XK_A);
841        else if ((sym >= XK_Agrave) && (sym <= XK_Odiaeresis))
842            *lower += (XK_agrave - XK_Agrave);
843        else if ((sym >= XK_agrave) && (sym <= XK_odiaeresis))
844            *upper -= (XK_agrave - XK_Agrave);
845        else if ((sym >= XK_Ooblique) && (sym <= XK_Thorn))
846            *lower += (XK_oslash - XK_Ooblique);
847        else if ((sym >= XK_oslash) && (sym <= XK_thorn))
848            *upper -= (XK_oslash - XK_Ooblique);
849        break;
850    case 1:                    /* Latin 2 */
851        /* Assume the KeySym is a legal value (ignore discontinuities) */
852        if (sym == XK_Aogonek)
853            *lower = XK_aogonek;
854        else if (sym >= XK_Lstroke && sym <= XK_Sacute)
855            *lower += (XK_lstroke - XK_Lstroke);
856        else if (sym >= XK_Scaron && sym <= XK_Zacute)
857            *lower += (XK_scaron - XK_Scaron);
858        else if (sym >= XK_Zcaron && sym <= XK_Zabovedot)
859            *lower += (XK_zcaron - XK_Zcaron);
860        else if (sym == XK_aogonek)
861            *upper = XK_Aogonek;
862        else if (sym >= XK_lstroke && sym <= XK_sacute)
863            *upper -= (XK_lstroke - XK_Lstroke);
864        else if (sym >= XK_scaron && sym <= XK_zacute)
865            *upper -= (XK_scaron - XK_Scaron);
866        else if (sym >= XK_zcaron && sym <= XK_zabovedot)
867            *upper -= (XK_zcaron - XK_Zcaron);
868        else if (sym >= XK_Racute && sym <= XK_Tcedilla)
869            *lower += (XK_racute - XK_Racute);
870        else if (sym >= XK_racute && sym <= XK_tcedilla)
871            *upper -= (XK_racute - XK_Racute);
872        break;
873    case 2:                    /* Latin 3 */
874        /* Assume the KeySym is a legal value (ignore discontinuities) */
875        if (sym >= XK_Hstroke && sym <= XK_Hcircumflex)
876            *lower += (XK_hstroke - XK_Hstroke);
877        else if (sym >= XK_Gbreve && sym <= XK_Jcircumflex)
878            *lower += (XK_gbreve - XK_Gbreve);
879        else if (sym >= XK_hstroke && sym <= XK_hcircumflex)
880            *upper -= (XK_hstroke - XK_Hstroke);
881        else if (sym >= XK_gbreve && sym <= XK_jcircumflex)
882            *upper -= (XK_gbreve - XK_Gbreve);
883        else if (sym >= XK_Cabovedot && sym <= XK_Scircumflex)
884            *lower += (XK_cabovedot - XK_Cabovedot);
885        else if (sym >= XK_cabovedot && sym <= XK_scircumflex)
886            *upper -= (XK_cabovedot - XK_Cabovedot);
887        break;
888    case 3:                    /* Latin 4 */
889        /* Assume the KeySym is a legal value (ignore discontinuities) */
890        if (sym >= XK_Rcedilla && sym <= XK_Tslash)
891            *lower += (XK_rcedilla - XK_Rcedilla);
892        else if (sym >= XK_rcedilla && sym <= XK_tslash)
893            *upper -= (XK_rcedilla - XK_Rcedilla);
894        else if (sym == XK_ENG)
895            *lower = XK_eng;
896        else if (sym == XK_eng)
897            *upper = XK_ENG;
898        else if (sym >= XK_Amacron && sym <= XK_Umacron)
899            *lower += (XK_amacron - XK_Amacron);
900        else if (sym >= XK_amacron && sym <= XK_umacron)
901            *upper -= (XK_amacron - XK_Amacron);
902        break;
903    case 6:                    /* Cyrillic */
904        /* Assume the KeySym is a legal value (ignore discontinuities) */
905        if (sym >= XK_Serbian_DJE && sym <= XK_Serbian_DZE)
906            *lower -= (XK_Serbian_DJE - XK_Serbian_dje);
907        else if (sym >= XK_Serbian_dje && sym <= XK_Serbian_dze)
908            *upper += (XK_Serbian_DJE - XK_Serbian_dje);
909        else if (sym >= XK_Cyrillic_YU && sym <= XK_Cyrillic_HARDSIGN)
910            *lower -= (XK_Cyrillic_YU - XK_Cyrillic_yu);
911        else if (sym >= XK_Cyrillic_yu && sym <= XK_Cyrillic_hardsign)
912            *upper += (XK_Cyrillic_YU - XK_Cyrillic_yu);
913        break;
914    case 7:                    /* Greek */
915        /* Assume the KeySym is a legal value (ignore discontinuities) */
916        if (sym >= XK_Greek_ALPHAaccent && sym <= XK_Greek_OMEGAaccent)
917            *lower += (XK_Greek_alphaaccent - XK_Greek_ALPHAaccent);
918        else if (sym >= XK_Greek_alphaaccent && sym <= XK_Greek_omegaaccent &&
919                 sym != XK_Greek_iotaaccentdieresis &&
920                 sym != XK_Greek_upsilonaccentdieresis)
921            *upper -= (XK_Greek_alphaaccent - XK_Greek_ALPHAaccent);
922        else if (sym >= XK_Greek_ALPHA && sym <= XK_Greek_OMEGA)
923            *lower += (XK_Greek_alpha - XK_Greek_ALPHA);
924        else if (sym >= XK_Greek_alpha && sym <= XK_Greek_omega &&
925                 sym != XK_Greek_finalsmallsigma)
926            *upper -= (XK_Greek_alpha - XK_Greek_ALPHA);
927        break;
928    }
929}
930
931static Bool
932_XkbCopyClientMap(XkbDescPtr src, XkbDescPtr dst)
933{
934    void *tmp = NULL;
935    int i;
936    XkbKeyTypePtr stype = NULL, dtype = NULL;
937
938    /* client map */
939    if (src->map) {
940        if (!dst->map) {
941            tmp = calloc(1, sizeof(XkbClientMapRec));
942            if (!tmp)
943                return FALSE;
944            dst->map = tmp;
945        }
946
947        if (src->map->syms) {
948            if (src->map->size_syms != dst->map->size_syms) {
949                tmp = reallocarray(dst->map->syms,
950                                   src->map->size_syms, sizeof(KeySym));
951                if (!tmp)
952                    return FALSE;
953                dst->map->syms = tmp;
954
955            }
956            memcpy(dst->map->syms, src->map->syms,
957                   src->map->size_syms * sizeof(KeySym));
958        }
959        else {
960            free(dst->map->syms);
961            dst->map->syms = NULL;
962        }
963        dst->map->num_syms = src->map->num_syms;
964        dst->map->size_syms = src->map->size_syms;
965
966        if (src->map->key_sym_map) {
967            if (src->max_key_code != dst->max_key_code) {
968                tmp = reallocarray(dst->map->key_sym_map,
969                                   src->max_key_code + 1, sizeof(XkbSymMapRec));
970                if (!tmp)
971                    return FALSE;
972                dst->map->key_sym_map = tmp;
973            }
974            memcpy(dst->map->key_sym_map, src->map->key_sym_map,
975                   (src->max_key_code + 1) * sizeof(XkbSymMapRec));
976        }
977        else {
978            free(dst->map->key_sym_map);
979            dst->map->key_sym_map = NULL;
980        }
981
982        if (src->map->types && src->map->num_types) {
983            if (src->map->num_types > dst->map->size_types ||
984                !dst->map->types || !dst->map->size_types) {
985                if (dst->map->types && dst->map->size_types) {
986                    tmp = reallocarray(dst->map->types, src->map->num_types,
987                                       sizeof(XkbKeyTypeRec));
988                    if (!tmp)
989                        return FALSE;
990                    dst->map->types = tmp;
991                    memset(dst->map->types + dst->map->num_types, 0,
992                           (src->map->num_types - dst->map->num_types) *
993                           sizeof(XkbKeyTypeRec));
994                }
995                else {
996                    tmp = calloc(src->map->num_types, sizeof(XkbKeyTypeRec));
997                    if (!tmp)
998                        return FALSE;
999                    dst->map->types = tmp;
1000                }
1001            }
1002            else if (src->map->num_types < dst->map->num_types &&
1003                     dst->map->types) {
1004                for (i = src->map->num_types, dtype = (dst->map->types + i);
1005                     i < dst->map->num_types; i++, dtype++) {
1006                    free(dtype->level_names);
1007                    dtype->level_names = NULL;
1008                    dtype->num_levels = 0;
1009                    if (dtype->map_count) {
1010                        free(dtype->map);
1011                        free(dtype->preserve);
1012                    }
1013                }
1014            }
1015
1016            stype = src->map->types;
1017            dtype = dst->map->types;
1018            for (i = 0; i < src->map->num_types; i++, dtype++, stype++) {
1019                if (stype->num_levels && stype->level_names) {
1020                    if (stype->num_levels != dtype->num_levels &&
1021                        dtype->num_levels && dtype->level_names &&
1022                        i < dst->map->num_types) {
1023                        tmp = reallocarray(dtype->level_names,
1024                                           stype->num_levels, sizeof(Atom));
1025                        if (!tmp)
1026                            continue;
1027                        dtype->level_names = tmp;
1028                    }
1029                    else if (!dtype->num_levels || !dtype->level_names ||
1030                             i >= dst->map->num_types) {
1031                        tmp = malloc(stype->num_levels * sizeof(Atom));
1032                        if (!tmp)
1033                            continue;
1034                        dtype->level_names = tmp;
1035                    }
1036                    dtype->num_levels = stype->num_levels;
1037                    memcpy(dtype->level_names, stype->level_names,
1038                           stype->num_levels * sizeof(Atom));
1039                }
1040                else {
1041                    if (dtype->num_levels && dtype->level_names &&
1042                        i < dst->map->num_types)
1043                        free(dtype->level_names);
1044                    dtype->num_levels = 0;
1045                    dtype->level_names = NULL;
1046                }
1047
1048                dtype->name = stype->name;
1049                memcpy(&dtype->mods, &stype->mods, sizeof(XkbModsRec));
1050
1051                if (stype->map_count) {
1052                    if (stype->map) {
1053                        if (stype->map_count != dtype->map_count &&
1054                            dtype->map_count && dtype->map &&
1055                            i < dst->map->num_types) {
1056                            tmp = reallocarray(dtype->map,
1057                                               stype->map_count,
1058                                               sizeof(XkbKTMapEntryRec));
1059                            if (!tmp)
1060                                return FALSE;
1061                            dtype->map = tmp;
1062                        }
1063                        else if (!dtype->map_count || !dtype->map ||
1064                                 i >= dst->map->num_types) {
1065                            tmp = xallocarray(stype->map_count,
1066                                              sizeof(XkbKTMapEntryRec));
1067                            if (!tmp)
1068                                return FALSE;
1069                            dtype->map = tmp;
1070                        }
1071
1072                        memcpy(dtype->map, stype->map,
1073                               stype->map_count * sizeof(XkbKTMapEntryRec));
1074                    }
1075                    else {
1076                        if (dtype->map && i < dst->map->num_types)
1077                            free(dtype->map);
1078                        dtype->map = NULL;
1079                    }
1080
1081                    if (stype->preserve) {
1082                        if (stype->map_count != dtype->map_count &&
1083                            dtype->map_count && dtype->preserve &&
1084                            i < dst->map->num_types) {
1085                            tmp = reallocarray(dtype->preserve,
1086                                               stype->map_count,
1087                                               sizeof(XkbModsRec));
1088                            if (!tmp)
1089                                return FALSE;
1090                            dtype->preserve = tmp;
1091                        }
1092                        else if (!dtype->preserve || !dtype->map_count ||
1093                                 i >= dst->map->num_types) {
1094                            tmp = xallocarray(stype->map_count,
1095                                              sizeof(XkbModsRec));
1096                            if (!tmp)
1097                                return FALSE;
1098                            dtype->preserve = tmp;
1099                        }
1100
1101                        memcpy(dtype->preserve, stype->preserve,
1102                               stype->map_count * sizeof(XkbModsRec));
1103                    }
1104                    else {
1105                        if (dtype->preserve && i < dst->map->num_types)
1106                            free(dtype->preserve);
1107                        dtype->preserve = NULL;
1108                    }
1109
1110                    dtype->map_count = stype->map_count;
1111                }
1112                else {
1113                    if (dtype->map_count && i < dst->map->num_types) {
1114                        free(dtype->map);
1115                        free(dtype->preserve);
1116                    }
1117                    dtype->map_count = 0;
1118                    dtype->map = NULL;
1119                    dtype->preserve = NULL;
1120                }
1121            }
1122
1123            dst->map->size_types = src->map->num_types;
1124            dst->map->num_types = src->map->num_types;
1125        }
1126        else {
1127            if (dst->map->types) {
1128                for (i = 0, dtype = dst->map->types; i < dst->map->num_types;
1129                     i++, dtype++) {
1130                    free(dtype->level_names);
1131                    if (dtype->map && dtype->map_count)
1132                        free(dtype->map);
1133                    if (dtype->preserve && dtype->map_count)
1134                        free(dtype->preserve);
1135                }
1136            }
1137            free(dst->map->types);
1138            dst->map->types = NULL;
1139            dst->map->num_types = 0;
1140            dst->map->size_types = 0;
1141        }
1142
1143        if (src->map->modmap) {
1144            if (src->max_key_code != dst->max_key_code) {
1145                tmp = realloc(dst->map->modmap, src->max_key_code + 1);
1146                if (!tmp)
1147                    return FALSE;
1148                dst->map->modmap = tmp;
1149            }
1150            memcpy(dst->map->modmap, src->map->modmap, src->max_key_code + 1);
1151        }
1152        else {
1153            free(dst->map->modmap);
1154            dst->map->modmap = NULL;
1155        }
1156    }
1157    else {
1158        if (dst->map)
1159            XkbFreeClientMap(dst, XkbAllClientInfoMask, TRUE);
1160    }
1161
1162    return TRUE;
1163}
1164
1165static Bool
1166_XkbCopyServerMap(XkbDescPtr src, XkbDescPtr dst)
1167{
1168    void *tmp = NULL;
1169
1170    /* server map */
1171    if (src->server) {
1172        if (!dst->server) {
1173            tmp = calloc(1, sizeof(XkbServerMapRec));
1174            if (!tmp)
1175                return FALSE;
1176            dst->server = tmp;
1177        }
1178
1179        if (src->server->explicit) {
1180            if (src->max_key_code != dst->max_key_code) {
1181                tmp = realloc(dst->server->explicit, src->max_key_code + 1);
1182                if (!tmp)
1183                    return FALSE;
1184                dst->server->explicit = tmp;
1185            }
1186            memcpy(dst->server->explicit, src->server->explicit,
1187                   src->max_key_code + 1);
1188        }
1189        else {
1190            free(dst->server->explicit);
1191            dst->server->explicit = NULL;
1192        }
1193
1194        if (src->server->acts) {
1195            if (src->server->size_acts != dst->server->size_acts) {
1196                tmp = reallocarray(dst->server->acts,
1197                                   src->server->size_acts, sizeof(XkbAction));
1198                if (!tmp)
1199                    return FALSE;
1200                dst->server->acts = tmp;
1201            }
1202            memcpy(dst->server->acts, src->server->acts,
1203                   src->server->size_acts * sizeof(XkbAction));
1204        }
1205        else {
1206            free(dst->server->acts);
1207            dst->server->acts = NULL;
1208        }
1209        dst->server->size_acts = src->server->size_acts;
1210        dst->server->num_acts = src->server->num_acts;
1211
1212        if (src->server->key_acts) {
1213            if (src->max_key_code != dst->max_key_code) {
1214                tmp = reallocarray(dst->server->key_acts,
1215                                   src->max_key_code + 1, sizeof(unsigned short));
1216                if (!tmp)
1217                    return FALSE;
1218                dst->server->key_acts = tmp;
1219            }
1220            memcpy(dst->server->key_acts, src->server->key_acts,
1221                   (src->max_key_code + 1) * sizeof(unsigned short));
1222        }
1223        else {
1224            free(dst->server->key_acts);
1225            dst->server->key_acts = NULL;
1226        }
1227
1228        if (src->server->behaviors) {
1229            if (src->max_key_code != dst->max_key_code) {
1230                tmp = reallocarray(dst->server->behaviors,
1231                                   src->max_key_code + 1, sizeof(XkbBehavior));
1232                if (!tmp)
1233                    return FALSE;
1234                dst->server->behaviors = tmp;
1235            }
1236            memcpy(dst->server->behaviors, src->server->behaviors,
1237                   (src->max_key_code + 1) * sizeof(XkbBehavior));
1238        }
1239        else {
1240            free(dst->server->behaviors);
1241            dst->server->behaviors = NULL;
1242        }
1243
1244        memcpy(dst->server->vmods, src->server->vmods, XkbNumVirtualMods);
1245
1246        if (src->server->vmodmap) {
1247            if (src->max_key_code != dst->max_key_code) {
1248                tmp = reallocarray(dst->server->vmodmap,
1249                                   src->max_key_code + 1, sizeof(unsigned short));
1250                if (!tmp)
1251                    return FALSE;
1252                dst->server->vmodmap = tmp;
1253            }
1254            memcpy(dst->server->vmodmap, src->server->vmodmap,
1255                   (src->max_key_code + 1) * sizeof(unsigned short));
1256        }
1257        else {
1258            free(dst->server->vmodmap);
1259            dst->server->vmodmap = NULL;
1260        }
1261    }
1262    else {
1263        if (dst->server)
1264            XkbFreeServerMap(dst, XkbAllServerInfoMask, TRUE);
1265    }
1266
1267    return TRUE;
1268}
1269
1270static Bool
1271_XkbCopyNames(XkbDescPtr src, XkbDescPtr dst)
1272{
1273    void *tmp = NULL;
1274
1275    /* names */
1276    if (src->names) {
1277        if (!dst->names) {
1278            dst->names = calloc(1, sizeof(XkbNamesRec));
1279            if (!dst->names)
1280                return FALSE;
1281        }
1282
1283        if (src->names->keys) {
1284            if (src->max_key_code != dst->max_key_code) {
1285                tmp = reallocarray(dst->names->keys, src->max_key_code + 1,
1286                                   sizeof(XkbKeyNameRec));
1287                if (!tmp)
1288                    return FALSE;
1289                dst->names->keys = tmp;
1290            }
1291            memcpy(dst->names->keys, src->names->keys,
1292                   (src->max_key_code + 1) * sizeof(XkbKeyNameRec));
1293        }
1294        else {
1295            free(dst->names->keys);
1296            dst->names->keys = NULL;
1297        }
1298
1299        if (src->names->num_key_aliases) {
1300            if (src->names->num_key_aliases != dst->names->num_key_aliases) {
1301                tmp = reallocarray(dst->names->key_aliases,
1302                                   src->names->num_key_aliases,
1303                                   sizeof(XkbKeyAliasRec));
1304                if (!tmp)
1305                    return FALSE;
1306                dst->names->key_aliases = tmp;
1307            }
1308            memcpy(dst->names->key_aliases, src->names->key_aliases,
1309                   src->names->num_key_aliases * sizeof(XkbKeyAliasRec));
1310        }
1311        else {
1312            free(dst->names->key_aliases);
1313            dst->names->key_aliases = NULL;
1314        }
1315        dst->names->num_key_aliases = src->names->num_key_aliases;
1316
1317        if (src->names->num_rg) {
1318            if (src->names->num_rg != dst->names->num_rg) {
1319                tmp = reallocarray(dst->names->radio_groups,
1320                                   src->names->num_rg, sizeof(Atom));
1321                if (!tmp)
1322                    return FALSE;
1323                dst->names->radio_groups = tmp;
1324            }
1325            memcpy(dst->names->radio_groups, src->names->radio_groups,
1326                   src->names->num_rg * sizeof(Atom));
1327        }
1328        else {
1329            free(dst->names->radio_groups);
1330            dst->names->radio_groups = NULL;
1331        }
1332        dst->names->num_rg = src->names->num_rg;
1333
1334        dst->names->keycodes = src->names->keycodes;
1335        dst->names->geometry = src->names->geometry;
1336        dst->names->symbols = src->names->symbols;
1337        dst->names->types = src->names->types;
1338        dst->names->compat = src->names->compat;
1339        dst->names->phys_symbols = src->names->phys_symbols;
1340
1341        memcpy(dst->names->vmods, src->names->vmods,
1342               XkbNumVirtualMods * sizeof(Atom));
1343        memcpy(dst->names->indicators, src->names->indicators,
1344               XkbNumIndicators * sizeof(Atom));
1345        memcpy(dst->names->groups, src->names->groups,
1346               XkbNumKbdGroups * sizeof(Atom));
1347    }
1348    else {
1349        if (dst->names)
1350            XkbFreeNames(dst, XkbAllNamesMask, TRUE);
1351    }
1352
1353    return TRUE;
1354}
1355
1356static Bool
1357_XkbCopyCompat(XkbDescPtr src, XkbDescPtr dst)
1358{
1359    void *tmp = NULL;
1360
1361    /* compat */
1362    if (src->compat) {
1363        if (!dst->compat) {
1364            dst->compat = calloc(1, sizeof(XkbCompatMapRec));
1365            if (!dst->compat)
1366                return FALSE;
1367        }
1368
1369        if (src->compat->sym_interpret && src->compat->num_si) {
1370            if (src->compat->num_si != dst->compat->size_si) {
1371                tmp = reallocarray(dst->compat->sym_interpret,
1372                                   src->compat->num_si,
1373                                   sizeof(XkbSymInterpretRec));
1374                if (!tmp)
1375                    return FALSE;
1376                dst->compat->sym_interpret = tmp;
1377            }
1378            memcpy(dst->compat->sym_interpret, src->compat->sym_interpret,
1379                   src->compat->num_si * sizeof(XkbSymInterpretRec));
1380
1381            dst->compat->num_si = src->compat->num_si;
1382            dst->compat->size_si = src->compat->num_si;
1383        }
1384        else {
1385            if (dst->compat->sym_interpret && dst->compat->size_si)
1386                free(dst->compat->sym_interpret);
1387
1388            dst->compat->sym_interpret = NULL;
1389            dst->compat->num_si = 0;
1390            dst->compat->size_si = 0;
1391        }
1392
1393        memcpy(dst->compat->groups, src->compat->groups,
1394               XkbNumKbdGroups * sizeof(XkbModsRec));
1395    }
1396    else {
1397        if (dst->compat)
1398            XkbFreeCompatMap(dst, XkbAllCompatMask, TRUE);
1399    }
1400
1401    return TRUE;
1402}
1403
1404static Bool
1405_XkbCopyGeom(XkbDescPtr src, XkbDescPtr dst)
1406{
1407    void *tmp = NULL;
1408    int i = 0, j = 0, k = 0;
1409    XkbColorPtr scolor = NULL, dcolor = NULL;
1410    XkbDoodadPtr sdoodad = NULL, ddoodad = NULL;
1411    XkbOutlinePtr soutline = NULL, doutline = NULL;
1412    XkbPropertyPtr sprop = NULL, dprop = NULL;
1413    XkbRowPtr srow = NULL, drow = NULL;
1414    XkbSectionPtr ssection = NULL, dsection = NULL;
1415    XkbShapePtr sshape = NULL, dshape = NULL;
1416
1417    /* geometry */
1418    if (src->geom) {
1419        if (!dst->geom) {
1420            dst->geom = calloc(sizeof(XkbGeometryRec), 1);
1421            if (!dst->geom)
1422                return FALSE;
1423        }
1424
1425        /* properties */
1426        if (src->geom->num_properties) {
1427            /* If we've got more properties in the destination than
1428             * the source, run through and free all the excess ones
1429             * first. */
1430            if (src->geom->num_properties < dst->geom->sz_properties) {
1431                for (i = src->geom->num_properties, dprop =
1432                     dst->geom->properties + i; i < dst->geom->num_properties;
1433                     i++, dprop++) {
1434                    free(dprop->name);
1435                    free(dprop->value);
1436                }
1437            }
1438
1439            /* Reallocate and clear all new items if the buffer grows. */
1440            if (!XkbGeomRealloc
1441                ((void **) &dst->geom->properties, dst->geom->sz_properties,
1442                 src->geom->num_properties, sizeof(XkbPropertyRec),
1443                 XKB_GEOM_CLEAR_EXCESS))
1444                return FALSE;
1445            /* We don't set num_properties as we need it to try and avoid
1446             * too much reallocing. */
1447            dst->geom->sz_properties = src->geom->num_properties;
1448
1449            for (i = 0,
1450                 sprop = src->geom->properties,
1451                 dprop = dst->geom->properties;
1452                 i < src->geom->num_properties; i++, sprop++, dprop++) {
1453                if (i < dst->geom->num_properties) {
1454                    if (strlen(sprop->name) != strlen(dprop->name)) {
1455                        tmp = realloc(dprop->name, strlen(sprop->name) + 1);
1456                        if (!tmp)
1457                            return FALSE;
1458                        dprop->name = tmp;
1459                    }
1460                    if (strlen(sprop->value) != strlen(dprop->value)) {
1461                        tmp = realloc(dprop->value, strlen(sprop->value) + 1);
1462                        if (!tmp)
1463                            return FALSE;
1464                        dprop->value = tmp;
1465                    }
1466                    strcpy(dprop->name, sprop->name);
1467                    strcpy(dprop->value, sprop->value);
1468                }
1469                else {
1470                    dprop->name = xstrdup(sprop->name);
1471                    dprop->value = xstrdup(sprop->value);
1472                }
1473            }
1474
1475            /* ... which is already src->geom->num_properties. */
1476            dst->geom->num_properties = dst->geom->sz_properties;
1477        }
1478        else {
1479            if (dst->geom->sz_properties) {
1480                for (i = 0, dprop = dst->geom->properties;
1481                     i < dst->geom->num_properties; i++, dprop++) {
1482                    free(dprop->name);
1483                    free(dprop->value);
1484                }
1485                free(dst->geom->properties);
1486                dst->geom->properties = NULL;
1487            }
1488
1489            dst->geom->num_properties = 0;
1490            dst->geom->sz_properties = 0;
1491        }
1492
1493        /* colors */
1494        if (src->geom->num_colors) {
1495            if (src->geom->num_colors < dst->geom->sz_colors) {
1496                for (i = src->geom->num_colors, dcolor = dst->geom->colors + i;
1497                     i < dst->geom->num_colors; i++, dcolor++) {
1498                    free(dcolor->spec);
1499                }
1500            }
1501
1502            /* Reallocate and clear all new items if the buffer grows. */
1503            if (!XkbGeomRealloc
1504                ((void **) &dst->geom->colors, dst->geom->sz_colors,
1505                 src->geom->num_colors, sizeof(XkbColorRec),
1506                 XKB_GEOM_CLEAR_EXCESS))
1507                return FALSE;
1508            dst->geom->sz_colors = src->geom->num_colors;
1509
1510            for (i = 0,
1511                 scolor = src->geom->colors,
1512                 dcolor = dst->geom->colors;
1513                 i < src->geom->num_colors; i++, scolor++, dcolor++) {
1514                if (i < dst->geom->num_colors) {
1515                    if (strlen(scolor->spec) != strlen(dcolor->spec)) {
1516                        tmp = realloc(dcolor->spec, strlen(scolor->spec) + 1);
1517                        if (!tmp)
1518                            return FALSE;
1519                        dcolor->spec = tmp;
1520                    }
1521                    strcpy(dcolor->spec, scolor->spec);
1522                }
1523                else {
1524                    dcolor->spec = xstrdup(scolor->spec);
1525                }
1526                dcolor->pixel = scolor->pixel;
1527            }
1528
1529            dst->geom->num_colors = dst->geom->sz_colors;
1530        }
1531        else {
1532            if (dst->geom->sz_colors) {
1533                for (i = 0, dcolor = dst->geom->colors;
1534                     i < dst->geom->num_colors; i++, dcolor++) {
1535                    free(dcolor->spec);
1536                }
1537                free(dst->geom->colors);
1538                dst->geom->colors = NULL;
1539            }
1540
1541            dst->geom->num_colors = 0;
1542            dst->geom->sz_colors = 0;
1543        }
1544
1545        /* shapes */
1546        /* shapes break down into outlines, which break down into points. */
1547        if (dst->geom->num_shapes) {
1548            for (i = 0, dshape = dst->geom->shapes;
1549                 i < dst->geom->num_shapes; i++, dshape++) {
1550                for (j = 0, doutline = dshape->outlines;
1551                     j < dshape->num_outlines; j++, doutline++) {
1552                    if (doutline->sz_points)
1553                        free(doutline->points);
1554                }
1555
1556                if (dshape->sz_outlines) {
1557                    free(dshape->outlines);
1558                    dshape->outlines = NULL;
1559                }
1560
1561                dshape->num_outlines = 0;
1562                dshape->sz_outlines = 0;
1563            }
1564        }
1565
1566        if (src->geom->num_shapes) {
1567            /* Reallocate and clear all items. */
1568            if (!XkbGeomRealloc
1569                ((void **) &dst->geom->shapes, dst->geom->sz_shapes,
1570                 src->geom->num_shapes, sizeof(XkbShapeRec),
1571                 XKB_GEOM_CLEAR_ALL))
1572                return FALSE;
1573
1574            for (i = 0, sshape = src->geom->shapes, dshape = dst->geom->shapes;
1575                 i < src->geom->num_shapes; i++, sshape++, dshape++) {
1576                if (sshape->num_outlines) {
1577                    tmp = calloc(sshape->num_outlines, sizeof(XkbOutlineRec));
1578                    if (!tmp)
1579                        return FALSE;
1580                    dshape->outlines = tmp;
1581
1582                    for (j = 0,
1583                         soutline = sshape->outlines,
1584                         doutline = dshape->outlines;
1585                         j < sshape->num_outlines;
1586                         j++, soutline++, doutline++) {
1587                        if (soutline->num_points) {
1588                            tmp = xallocarray(soutline->num_points,
1589                                              sizeof(XkbPointRec));
1590                            if (!tmp)
1591                                return FALSE;
1592                            doutline->points = tmp;
1593
1594                            memcpy(doutline->points, soutline->points,
1595                                   soutline->num_points * sizeof(XkbPointRec));
1596
1597                            doutline->corner_radius = soutline->corner_radius;
1598                        }
1599
1600                        doutline->num_points = soutline->num_points;
1601                        doutline->sz_points = soutline->num_points;
1602                    }
1603                }
1604
1605                dshape->num_outlines = sshape->num_outlines;
1606                dshape->sz_outlines = sshape->num_outlines;
1607                dshape->name = sshape->name;
1608                dshape->bounds = sshape->bounds;
1609
1610                dshape->approx = NULL;
1611                if (sshape->approx && sshape->num_outlines > 0) {
1612
1613                    const ptrdiff_t approx_idx =
1614                        sshape->approx - sshape->outlines;
1615
1616                    if (approx_idx < dshape->num_outlines) {
1617                        dshape->approx = dshape->outlines + approx_idx;
1618                    }
1619                    else {
1620                        LogMessage(X_WARNING, "XKB: approx outline "
1621                                   "index is out of range\n");
1622                    }
1623                }
1624
1625                dshape->primary = NULL;
1626                if (sshape->primary && sshape->num_outlines > 0) {
1627
1628                    const ptrdiff_t primary_idx =
1629                        sshape->primary - sshape->outlines;
1630
1631                    if (primary_idx < dshape->num_outlines) {
1632                        dshape->primary = dshape->outlines + primary_idx;
1633                    }
1634                    else {
1635                        LogMessage(X_WARNING, "XKB: primary outline "
1636                                   "index is out of range\n");
1637                    }
1638                }
1639            }
1640
1641            dst->geom->num_shapes = src->geom->num_shapes;
1642            dst->geom->sz_shapes = src->geom->num_shapes;
1643        }
1644        else {
1645            if (dst->geom->sz_shapes) {
1646                free(dst->geom->shapes);
1647            }
1648            dst->geom->shapes = NULL;
1649            dst->geom->num_shapes = 0;
1650            dst->geom->sz_shapes = 0;
1651        }
1652
1653        /* sections */
1654        /* sections break down into doodads, and also into rows, which break
1655         * down into keys. */
1656        if (dst->geom->num_sections) {
1657            for (i = 0, dsection = dst->geom->sections;
1658                 i < dst->geom->num_sections; i++, dsection++) {
1659                for (j = 0, drow = dsection->rows;
1660                     j < dsection->num_rows; j++, drow++) {
1661                    if (drow->num_keys)
1662                        free(drow->keys);
1663                }
1664
1665                if (dsection->num_rows)
1666                    free(dsection->rows);
1667
1668                /* cut and waste from geom/doodad below. */
1669                for (j = 0, ddoodad = dsection->doodads;
1670                     j < dsection->num_doodads; j++, ddoodad++) {
1671                    if (ddoodad->any.type == XkbTextDoodad) {
1672                        free(ddoodad->text.text);
1673                        ddoodad->text.text = NULL;
1674                        free(ddoodad->text.font);
1675                        ddoodad->text.font = NULL;
1676                    }
1677                    else if (ddoodad->any.type == XkbLogoDoodad) {
1678                        free(ddoodad->logo.logo_name);
1679                        ddoodad->logo.logo_name = NULL;
1680                    }
1681                }
1682
1683                free(dsection->doodads);
1684            }
1685
1686            dst->geom->num_sections = 0;
1687        }
1688
1689        if (src->geom->num_sections) {
1690            /* Reallocate and clear all items. */
1691            if (!XkbGeomRealloc
1692                ((void **) &dst->geom->sections, dst->geom->sz_sections,
1693                 src->geom->num_sections, sizeof(XkbSectionRec),
1694                 XKB_GEOM_CLEAR_ALL))
1695                return FALSE;
1696            dst->geom->num_sections = src->geom->num_sections;
1697            dst->geom->sz_sections = src->geom->num_sections;
1698
1699            for (i = 0,
1700                 ssection = src->geom->sections,
1701                 dsection = dst->geom->sections;
1702                 i < src->geom->num_sections; i++, ssection++, dsection++) {
1703                *dsection = *ssection;
1704                if (ssection->num_rows) {
1705                    tmp = calloc(ssection->num_rows, sizeof(XkbRowRec));
1706                    if (!tmp)
1707                        return FALSE;
1708                    dsection->rows = tmp;
1709                }
1710                dsection->num_rows = ssection->num_rows;
1711                dsection->sz_rows = ssection->num_rows;
1712
1713                for (j = 0, srow = ssection->rows, drow = dsection->rows;
1714                     j < ssection->num_rows; j++, srow++, drow++) {
1715                    if (srow->num_keys) {
1716                        tmp = xallocarray(srow->num_keys, sizeof(XkbKeyRec));
1717                        if (!tmp)
1718                            return FALSE;
1719                        drow->keys = tmp;
1720                        memcpy(drow->keys, srow->keys,
1721                               srow->num_keys * sizeof(XkbKeyRec));
1722                    }
1723                    drow->num_keys = srow->num_keys;
1724                    drow->sz_keys = srow->num_keys;
1725                    drow->top = srow->top;
1726                    drow->left = srow->left;
1727                    drow->vertical = srow->vertical;
1728                    drow->bounds = srow->bounds;
1729                }
1730
1731                if (ssection->num_doodads) {
1732                    tmp = calloc(ssection->num_doodads, sizeof(XkbDoodadRec));
1733                    if (!tmp)
1734                        return FALSE;
1735                    dsection->doodads = tmp;
1736                }
1737                else {
1738                    dsection->doodads = NULL;
1739                }
1740
1741                dsection->sz_doodads = ssection->num_doodads;
1742                for (k = 0,
1743                     sdoodad = ssection->doodads,
1744                     ddoodad = dsection->doodads;
1745                     k < ssection->num_doodads; k++, sdoodad++, ddoodad++) {
1746                    memcpy(ddoodad, sdoodad, sizeof(XkbDoodadRec));
1747                    if (sdoodad->any.type == XkbTextDoodad) {
1748                        if (sdoodad->text.text)
1749                            ddoodad->text.text = strdup(sdoodad->text.text);
1750                        if (sdoodad->text.font)
1751                            ddoodad->text.font = strdup(sdoodad->text.font);
1752                    }
1753                    else if (sdoodad->any.type == XkbLogoDoodad) {
1754                        if (sdoodad->logo.logo_name)
1755                            ddoodad->logo.logo_name =
1756                                strdup(sdoodad->logo.logo_name);
1757                    }
1758                }
1759                dsection->overlays = NULL;
1760                dsection->sz_overlays = 0;
1761                dsection->num_overlays = 0;
1762            }
1763        }
1764        else {
1765            if (dst->geom->sz_sections) {
1766                free(dst->geom->sections);
1767            }
1768
1769            dst->geom->sections = NULL;
1770            dst->geom->num_sections = 0;
1771            dst->geom->sz_sections = 0;
1772        }
1773
1774        /* doodads */
1775        if (dst->geom->num_doodads) {
1776            for (i = src->geom->num_doodads,
1777                 ddoodad = dst->geom->doodads +
1778                 src->geom->num_doodads;
1779                 i < dst->geom->num_doodads; i++, ddoodad++) {
1780                if (ddoodad->any.type == XkbTextDoodad) {
1781                    free(ddoodad->text.text);
1782                    ddoodad->text.text = NULL;
1783                    free(ddoodad->text.font);
1784                    ddoodad->text.font = NULL;
1785                }
1786                else if (ddoodad->any.type == XkbLogoDoodad) {
1787                    free(ddoodad->logo.logo_name);
1788                    ddoodad->logo.logo_name = NULL;
1789                }
1790            }
1791            dst->geom->num_doodads = 0;
1792        }
1793
1794        if (src->geom->num_doodads) {
1795            /* Reallocate and clear all items. */
1796            if (!XkbGeomRealloc
1797                ((void **) &dst->geom->doodads, dst->geom->sz_doodads,
1798                 src->geom->num_doodads, sizeof(XkbDoodadRec),
1799                 XKB_GEOM_CLEAR_ALL))
1800                return FALSE;
1801
1802            dst->geom->sz_doodads = src->geom->num_doodads;
1803
1804            for (i = 0,
1805                 sdoodad = src->geom->doodads,
1806                 ddoodad = dst->geom->doodads;
1807                 i < src->geom->num_doodads; i++, sdoodad++, ddoodad++) {
1808                memcpy(ddoodad, sdoodad, sizeof(XkbDoodadRec));
1809                if (sdoodad->any.type == XkbTextDoodad) {
1810                    if (sdoodad->text.text)
1811                        ddoodad->text.text = strdup(sdoodad->text.text);
1812                    if (sdoodad->text.font)
1813                        ddoodad->text.font = strdup(sdoodad->text.font);
1814                }
1815                else if (sdoodad->any.type == XkbLogoDoodad) {
1816                    if (sdoodad->logo.logo_name)
1817                        ddoodad->logo.logo_name =
1818                            strdup(sdoodad->logo.logo_name);
1819                }
1820            }
1821
1822            dst->geom->num_doodads = dst->geom->sz_doodads;
1823        }
1824        else {
1825            if (dst->geom->sz_doodads) {
1826                free(dst->geom->doodads);
1827            }
1828
1829            dst->geom->doodads = NULL;
1830            dst->geom->num_doodads = 0;
1831            dst->geom->sz_doodads = 0;
1832        }
1833
1834        /* key aliases */
1835        if (src->geom->num_key_aliases) {
1836            /* Reallocate but don't clear any items. There is no need
1837             * to clear anything because data is immediately copied
1838             * over the whole memory area with memcpy. */
1839            if (!XkbGeomRealloc
1840                ((void **) &dst->geom->key_aliases, dst->geom->sz_key_aliases,
1841                 src->geom->num_key_aliases, 2 * XkbKeyNameLength,
1842                 XKB_GEOM_CLEAR_NONE))
1843                return FALSE;
1844
1845            dst->geom->sz_key_aliases = src->geom->num_key_aliases;
1846
1847            memcpy(dst->geom->key_aliases, src->geom->key_aliases,
1848                   src->geom->num_key_aliases * 2 * XkbKeyNameLength);
1849
1850            dst->geom->num_key_aliases = dst->geom->sz_key_aliases;
1851        }
1852        else {
1853            free(dst->geom->key_aliases);
1854            dst->geom->key_aliases = NULL;
1855            dst->geom->num_key_aliases = 0;
1856            dst->geom->sz_key_aliases = 0;
1857        }
1858
1859        /* font */
1860        if (src->geom->label_font) {
1861            if (!dst->geom->label_font) {
1862                tmp = malloc(strlen(src->geom->label_font) + 1);
1863                if (!tmp)
1864                    return FALSE;
1865                dst->geom->label_font = tmp;
1866            }
1867            else if (strlen(src->geom->label_font) !=
1868                     strlen(dst->geom->label_font)) {
1869                tmp = realloc(dst->geom->label_font,
1870                              strlen(src->geom->label_font) + 1);
1871                if (!tmp)
1872                    return FALSE;
1873                dst->geom->label_font = tmp;
1874            }
1875
1876            strcpy(dst->geom->label_font, src->geom->label_font);
1877            i = XkbGeomColorIndex(src->geom, src->geom->label_color);
1878            dst->geom->label_color = &(dst->geom->colors[i]);
1879            i = XkbGeomColorIndex(src->geom, src->geom->base_color);
1880            dst->geom->base_color = &(dst->geom->colors[i]);
1881        }
1882        else {
1883            free(dst->geom->label_font);
1884            dst->geom->label_font = NULL;
1885            dst->geom->label_color = NULL;
1886            dst->geom->base_color = NULL;
1887        }
1888
1889        dst->geom->name = src->geom->name;
1890        dst->geom->width_mm = src->geom->width_mm;
1891        dst->geom->height_mm = src->geom->height_mm;
1892    }
1893    else {
1894        if (dst->geom) {
1895            /* I LOVE THE DIFFERENT CALL SIGNATURE.  REALLY, I DO. */
1896            XkbFreeGeometry(dst->geom, XkbGeomAllMask, TRUE);
1897            dst->geom = NULL;
1898        }
1899    }
1900
1901    return TRUE;
1902}
1903
1904static Bool
1905_XkbCopyIndicators(XkbDescPtr src, XkbDescPtr dst)
1906{
1907    /* indicators */
1908    if (src->indicators) {
1909        if (!dst->indicators) {
1910            dst->indicators = malloc(sizeof(XkbIndicatorRec));
1911            if (!dst->indicators)
1912                return FALSE;
1913        }
1914        memcpy(dst->indicators, src->indicators, sizeof(XkbIndicatorRec));
1915    }
1916    else {
1917        free(dst->indicators);
1918        dst->indicators = NULL;
1919    }
1920    return TRUE;
1921}
1922
1923static Bool
1924_XkbCopyControls(XkbDescPtr src, XkbDescPtr dst)
1925{
1926    /* controls */
1927    if (src->ctrls) {
1928        if (!dst->ctrls) {
1929            dst->ctrls = malloc(sizeof(XkbControlsRec));
1930            if (!dst->ctrls)
1931                return FALSE;
1932        }
1933        memcpy(dst->ctrls, src->ctrls, sizeof(XkbControlsRec));
1934    }
1935    else {
1936        free(dst->ctrls);
1937        dst->ctrls = NULL;
1938    }
1939    return TRUE;
1940}
1941
1942/**
1943 * Copy an XKB map from src to dst, reallocating when necessary: if some
1944 * map components are present in one, but not in the other, the destination
1945 * components will be allocated or freed as necessary.
1946 *
1947 * Basic map consistency is assumed on both sides, so maps with random
1948 * uninitialised data (e.g. names->radio_grous == NULL, names->num_rg == 19)
1949 * _will_ cause failures.  You've been warned.
1950 *
1951 * Returns TRUE on success, or FALSE on failure.  If this function fails,
1952 * dst may be in an inconsistent state: all its pointers are guaranteed
1953 * to remain valid, but part of the map may be from src and part from dst.
1954 *
1955 */
1956
1957Bool
1958XkbCopyKeymap(XkbDescPtr dst, XkbDescPtr src)
1959{
1960
1961    if (!src || !dst) {
1962        DebugF("XkbCopyKeymap: src (%p) or dst (%p) is NULL\n", src, dst);
1963        return FALSE;
1964    }
1965
1966    if (src == dst)
1967        return TRUE;
1968
1969    if (!_XkbCopyClientMap(src, dst)) {
1970        DebugF("XkbCopyKeymap: failed to copy client map\n");
1971        return FALSE;
1972    }
1973    if (!_XkbCopyServerMap(src, dst)) {
1974        DebugF("XkbCopyKeymap: failed to copy server map\n");
1975        return FALSE;
1976    }
1977    if (!_XkbCopyIndicators(src, dst)) {
1978        DebugF("XkbCopyKeymap: failed to copy indicators\n");
1979        return FALSE;
1980    }
1981    if (!_XkbCopyControls(src, dst)) {
1982        DebugF("XkbCopyKeymap: failed to copy controls\n");
1983        return FALSE;
1984    }
1985    if (!_XkbCopyNames(src, dst)) {
1986        DebugF("XkbCopyKeymap: failed to copy names\n");
1987        return FALSE;
1988    }
1989    if (!_XkbCopyCompat(src, dst)) {
1990        DebugF("XkbCopyKeymap: failed to copy compat map\n");
1991        return FALSE;
1992    }
1993    if (!_XkbCopyGeom(src, dst)) {
1994        DebugF("XkbCopyKeymap: failed to copy geometry\n");
1995        return FALSE;
1996    }
1997
1998    dst->min_key_code = src->min_key_code;
1999    dst->max_key_code = src->max_key_code;
2000
2001    return TRUE;
2002}
2003
2004Bool
2005XkbDeviceApplyKeymap(DeviceIntPtr dst, XkbDescPtr desc)
2006{
2007    xkbNewKeyboardNotify nkn;
2008    Bool ret;
2009
2010    if (!dst->key || !desc)
2011        return FALSE;
2012
2013    memset(&nkn, 0, sizeof(xkbNewKeyboardNotify));
2014    nkn.oldMinKeyCode = dst->key->xkbInfo->desc->min_key_code;
2015    nkn.oldMaxKeyCode = dst->key->xkbInfo->desc->max_key_code;
2016    nkn.deviceID = dst->id;
2017    nkn.oldDeviceID = dst->id;
2018    nkn.minKeyCode = desc->min_key_code;
2019    nkn.maxKeyCode = desc->max_key_code;
2020    nkn.requestMajor = XkbReqCode;
2021    nkn.requestMinor = X_kbSetMap;      /* Near enough's good enough. */
2022    nkn.changed = XkbNKN_KeycodesMask;
2023    if (desc->geom)
2024        nkn.changed |= XkbNKN_GeometryMask;
2025
2026    ret = XkbCopyKeymap(dst->key->xkbInfo->desc, desc);
2027    if (ret)
2028        XkbSendNewKeyboardNotify(dst, &nkn);
2029
2030    return ret;
2031}
2032
2033Bool
2034XkbCopyDeviceKeymap(DeviceIntPtr dst, DeviceIntPtr src)
2035{
2036    return XkbDeviceApplyKeymap(dst, src->key->xkbInfo->desc);
2037}
2038
2039int
2040XkbGetEffectiveGroup(XkbSrvInfoPtr xkbi, XkbStatePtr xkbState, CARD8 keycode)
2041{
2042    XkbDescPtr xkb = xkbi->desc;
2043    int effectiveGroup = xkbState->group;
2044
2045    if (!XkbKeycodeInRange(xkb, keycode))
2046        return -1;
2047
2048    if (effectiveGroup == XkbGroup1Index)
2049        return effectiveGroup;
2050
2051    if (XkbKeyNumGroups(xkb, keycode) > 1U) {
2052        if (effectiveGroup >= XkbKeyNumGroups(xkb, keycode)) {
2053            unsigned int gi = XkbKeyGroupInfo(xkb, keycode);
2054
2055            switch (XkbOutOfRangeGroupAction(gi)) {
2056            default:
2057            case XkbWrapIntoRange:
2058                effectiveGroup %= XkbKeyNumGroups(xkb, keycode);
2059                break;
2060            case XkbClampIntoRange:
2061                effectiveGroup = XkbKeyNumGroups(xkb, keycode) - 1;
2062                break;
2063            case XkbRedirectIntoRange:
2064                effectiveGroup = XkbOutOfRangeGroupInfo(gi);
2065                if (effectiveGroup >= XkbKeyNumGroups(xkb, keycode))
2066                    effectiveGroup = 0;
2067                break;
2068            }
2069        }
2070    }
2071    else
2072        effectiveGroup = XkbGroup1Index;
2073
2074    return effectiveGroup;
2075}
2076
2077/* Merge the lockedPtrButtons from all attached SDs for the given master
2078 * device into the MD's state.
2079 */
2080void
2081XkbMergeLockedPtrBtns(DeviceIntPtr master)
2082{
2083    DeviceIntPtr d = inputInfo.devices;
2084    XkbSrvInfoPtr xkbi = NULL;
2085
2086    if (!IsMaster(master))
2087        return;
2088
2089    if (!master->key)
2090        return;
2091
2092    xkbi = master->key->xkbInfo;
2093    xkbi->lockedPtrButtons = 0;
2094
2095    for (; d; d = d->next) {
2096        if (IsMaster(d) || GetMaster(d, MASTER_KEYBOARD) != master || !d->key)
2097            continue;
2098
2099        xkbi->lockedPtrButtons |= d->key->xkbInfo->lockedPtrButtons;
2100    }
2101}
2102
2103void
2104XkbCopyControls(XkbDescPtr dst, XkbDescPtr src)
2105{
2106    int i, nG, nTG;
2107
2108    if (!dst || !src)
2109        return;
2110
2111    *dst->ctrls = *src->ctrls;
2112
2113    for (nG = nTG = 0, i = dst->min_key_code; i <= dst->max_key_code; i++) {
2114        nG = XkbKeyNumGroups(dst, i);
2115        if (nG >= XkbNumKbdGroups) {
2116            nTG = XkbNumKbdGroups;
2117            break;
2118        }
2119        if (nG > nTG) {
2120            nTG = nG;
2121        }
2122    }
2123    dst->ctrls->num_groups = nTG;
2124}
2125