Merge remote-tracking branch 'whot/for-keith'

This commit is contained in:
Keith Packard 2014-03-19 06:43:14 -07:00
commit 4fb31e4824
7 changed files with 275 additions and 91 deletions

View File

@ -230,7 +230,7 @@ CopyKeyClass(DeviceIntPtr device, DeviceIntPtr master)
mk->sourceid = device->id; mk->sourceid = device->id;
if (!XkbCopyDeviceKeymap(master, device)) if (!XkbDeviceApplyKeymap(master, device->key->xkbInfo->desc))
FatalError("Couldn't pivot keymap from device to core!\n"); FatalError("Couldn't pivot keymap from device to core!\n");
} }

View File

@ -385,6 +385,12 @@ extern _X_EXPORT Bool InitKeyboardDeviceStruct(DeviceIntPtr /*device */ ,
KbdCtrlProcPtr /*controlProc */ KbdCtrlProcPtr /*controlProc */
); );
extern _X_EXPORT Bool InitKeyboardDeviceStructFromString(DeviceIntPtr dev,
const char *keymap,
int keymap_length,
BellProcPtr bell_func,
KbdCtrlProcPtr ctrl_func);
extern int ApplyPointerMapping(DeviceIntPtr /* pDev */ , extern int ApplyPointerMapping(DeviceIntPtr /* pDev */ ,
CARD8 * /* map */ , CARD8 * /* map */ ,
int /* len */ , int /* len */ ,

View File

@ -824,8 +824,8 @@ extern _X_EXPORT void XkbSendNewKeyboardNotify(DeviceIntPtr /* kbd */ ,
extern Bool XkbCopyKeymap(XkbDescPtr /* dst */ , extern Bool XkbCopyKeymap(XkbDescPtr /* dst */ ,
XkbDescPtr /* src */ ); XkbDescPtr /* src */ );
extern _X_EXPORT Bool XkbCopyDeviceKeymap(DeviceIntPtr /* dst */ , extern _X_EXPORT Bool XkbDeviceApplyKeymap(DeviceIntPtr /* dst */ ,
DeviceIntPtr /* src */ ); XkbDescPtr /* src */ );
extern void XkbFilterEvents(ClientPtr /* pClient */ , extern void XkbFilterEvents(ClientPtr /* pClient */ ,
int /* nEvents */ , int /* nEvents */ ,
@ -841,6 +841,9 @@ extern void XkbFakeDeviceButton(DeviceIntPtr /* dev */ ,
int /* press */ , int /* press */ ,
int /* button */ ); int /* button */ );
extern _X_EXPORT void XkbCopyControls(XkbDescPtr /* dst */ ,
XkbDescPtr /* src */ );
#include "xkbfile.h" #include "xkbfile.h"
#include "xkbrules.h" #include "xkbrules.h"
@ -873,4 +876,8 @@ extern _X_EXPORT XkbDescPtr XkbCompileKeymap(DeviceIntPtr /* dev */ ,
XkbRMLVOSet * /* rmlvo */ XkbRMLVOSet * /* rmlvo */
); );
extern _X_EXPORT XkbDescPtr XkbCompileKeymapFromString(DeviceIntPtr dev,
const char *keymap,
int keymap_length);
#endif /* _XKBSRV_H_ */ #endif /* _XKBSRV_H_ */

View File

@ -68,6 +68,9 @@ THE USE OR PERFORMANCE OF THIS SOFTWARE.
#define PATHSEPARATOR "/" #define PATHSEPARATOR "/"
#endif #endif
static unsigned
LoadXKM(unsigned want, unsigned need, const char *keymap, XkbDescPtr *xkbRtrn);
static void static void
OutputDirectory(char *outdir, size_t size) OutputDirectory(char *outdir, size_t size)
{ {
@ -90,11 +93,17 @@ OutputDirectory(char *outdir, size_t size)
} }
} }
static Bool /**
XkbDDXCompileKeymapByNames(XkbDescPtr xkb, * Callback invoked by XkbRunXkbComp. Write to out to talk to xkbcomp.
XkbComponentNamesPtr names, */
unsigned want, typedef void (*xkbcomp_buffer_callback)(FILE *out, void *userdata);
unsigned need, char *nameRtrn, int nameRtrnLen)
/**
* Start xkbcomp, let the callback write into xkbcomp's stdin. When done,
* return a strdup'd copy of the file name we've written to.
*/
static char *
RunXkbComp(xkbcomp_buffer_callback callback, void *userdata)
{ {
FILE *out; FILE *out;
char *buf = NULL, keymap[PATH_MAX], xkm_output_dir[PATH_MAX]; char *buf = NULL, keymap[PATH_MAX], xkm_output_dir[PATH_MAX];
@ -155,7 +164,7 @@ XkbDDXCompileKeymapByNames(XkbDescPtr xkb,
if (!buf) { if (!buf) {
LogMessage(X_ERROR, LogMessage(X_ERROR,
"XKB: Could not invoke xkbcomp: not enough memory\n"); "XKB: Could not invoke xkbcomp: not enough memory\n");
return FALSE; return NULL;
} }
#ifndef WIN32 #ifndef WIN32
@ -165,13 +174,9 @@ XkbDDXCompileKeymapByNames(XkbDescPtr xkb,
#endif #endif
if (out != NULL) { if (out != NULL) {
#ifdef DEBUG /* Now write to xkbcomp */
if (xkbDebugFlags) { (*callback)(out, userdata);
ErrorF("[xkb] XkbDDXCompileKeymapByNames compiling keymap:\n");
XkbWriteXKBKeymapForNames(stderr, names, xkb, want, need);
}
#endif
XkbWriteXKBKeymapForNames(out, names, xkb, want, need);
#ifndef WIN32 #ifndef WIN32
if (Pclose(out) == 0) if (Pclose(out) == 0)
#else #else
@ -180,14 +185,11 @@ XkbDDXCompileKeymapByNames(XkbDescPtr xkb,
{ {
if (xkbDebugFlags) if (xkbDebugFlags)
DebugF("[xkb] xkb executes: %s\n", buf); DebugF("[xkb] xkb executes: %s\n", buf);
if (nameRtrn) {
strlcpy(nameRtrn, keymap, nameRtrnLen);
}
free(buf); free(buf);
#ifdef WIN32 #ifdef WIN32
unlink(tmpname); unlink(tmpname);
#endif #endif
return TRUE; return xnfstrdup(keymap);
} }
else else
LogMessage(X_ERROR, "Error compiling keymap (%s)\n", keymap); LogMessage(X_ERROR, "Error compiling keymap (%s)\n", keymap);
@ -203,14 +205,101 @@ XkbDDXCompileKeymapByNames(XkbDescPtr xkb,
LogMessage(X_ERROR, "Could not open file %s\n", tmpname); LogMessage(X_ERROR, "Could not open file %s\n", tmpname);
#endif #endif
} }
if (nameRtrn)
nameRtrn[0] = '\0';
free(buf); free(buf);
return FALSE; return NULL;
}
typedef struct {
XkbDescPtr xkb;
XkbComponentNamesPtr names;
unsigned int want;
unsigned int need;
} XkbKeymapNamesCtx;
static void
xkb_write_keymap_for_names_cb(FILE *out, void *userdata)
{
XkbKeymapNamesCtx *ctx = userdata;
#ifdef DEBUG
if (xkbDebugFlags) {
ErrorF("[xkb] XkbDDXCompileKeymapByNames compiling keymap:\n");
XkbWriteXKBKeymapForNames(stderr, ctx->names, ctx->xkb, ctx->want, ctx->need);
}
#endif
XkbWriteXKBKeymapForNames(out, ctx->names, ctx->xkb, ctx->want, ctx->need);
}
static Bool
XkbDDXCompileKeymapByNames(XkbDescPtr xkb,
XkbComponentNamesPtr names,
unsigned want,
unsigned need, char *nameRtrn, int nameRtrnLen)
{
char *keymap;
Bool rc = FALSE;
XkbKeymapNamesCtx ctx = {
.xkb = xkb,
.names = names,
.want = want,
.need = need
};
keymap = RunXkbComp(xkb_write_keymap_for_names_cb, &ctx);
if (keymap) {
if(nameRtrn)
strlcpy(nameRtrn, keymap, nameRtrnLen);
free(keymap);
rc = TRUE;
} else if (nameRtrn)
*nameRtrn = '\0';
return rc;
}
typedef struct {
const char *keymap;
size_t len;
} XkbKeymapString;
static void
xkb_write_keymap_string_cb(FILE *out, void *userdata)
{
XkbKeymapString *s = userdata;
fwrite(s->keymap, s->len, 1, out);
}
static unsigned int
XkbDDXLoadKeymapFromString(DeviceIntPtr keybd,
const char *keymap, int keymap_length,
unsigned int want,
unsigned int need,
XkbDescPtr *xkbRtrn)
{
unsigned int have;
char *map_name;
XkbKeymapString map = {
.keymap = keymap,
.len = keymap_length
};
*xkbRtrn = NULL;
map_name = RunXkbComp(xkb_write_keymap_string_cb, &map);
if (!map_name) {
LogMessage(X_ERROR, "XKB: Couldn't compile keymap\n");
return 0;
}
have = LoadXKM(want, need, map_name, xkbRtrn);
free(map_name);
return have;
} }
static FILE * static FILE *
XkbDDXOpenConfigFile(char *mapName, char *fileNameRtrn, int fileNameRtrnLen) XkbDDXOpenConfigFile(const char *mapName, char *fileNameRtrn, int fileNameRtrnLen)
{ {
char buf[PATH_MAX], xkm_output_dir[PATH_MAX]; char buf[PATH_MAX], xkm_output_dir[PATH_MAX];
FILE *file; FILE *file;
@ -245,37 +334,14 @@ XkbDDXOpenConfigFile(char *mapName, char *fileNameRtrn, int fileNameRtrnLen)
return file; return file;
} }
unsigned static unsigned
XkbDDXLoadKeymapByNames(DeviceIntPtr keybd, LoadXKM(unsigned want, unsigned need, const char *keymap, XkbDescPtr *xkbRtrn)
XkbComponentNamesPtr names,
unsigned want,
unsigned need,
XkbDescPtr *xkbRtrn, char *nameRtrn, int nameRtrnLen)
{ {
XkbDescPtr xkb;
FILE *file; FILE *file;
char fileName[PATH_MAX]; char fileName[PATH_MAX];
unsigned missing; unsigned missing;
*xkbRtrn = NULL; file = XkbDDXOpenConfigFile(keymap, fileName, PATH_MAX);
if ((keybd == NULL) || (keybd->key == NULL) ||
(keybd->key->xkbInfo == NULL))
xkb = NULL;
else
xkb = keybd->key->xkbInfo->desc;
if ((names->keycodes == NULL) && (names->types == NULL) &&
(names->compat == NULL) && (names->symbols == NULL) &&
(names->geometry == NULL)) {
LogMessage(X_ERROR, "XKB: No components provided for device %s\n",
keybd->name ? keybd->name : "(unnamed keyboard)");
return 0;
}
else if (!XkbDDXCompileKeymapByNames(xkb, names, want, need,
nameRtrn, nameRtrnLen)) {
LogMessage(X_ERROR, "XKB: Couldn't compile keymap\n");
return 0;
}
file = XkbDDXOpenConfigFile(nameRtrn, fileName, PATH_MAX);
if (file == NULL) { if (file == NULL) {
LogMessage(X_ERROR, "Couldn't open compiled keymap file %s\n", LogMessage(X_ERROR, "Couldn't open compiled keymap file %s\n",
fileName); fileName);
@ -297,6 +363,37 @@ XkbDDXLoadKeymapByNames(DeviceIntPtr keybd,
return (need | want) & (~missing); return (need | want) & (~missing);
} }
unsigned
XkbDDXLoadKeymapByNames(DeviceIntPtr keybd,
XkbComponentNamesPtr names,
unsigned want,
unsigned need,
XkbDescPtr *xkbRtrn, char *nameRtrn, int nameRtrnLen)
{
XkbDescPtr xkb;
*xkbRtrn = NULL;
if ((keybd == NULL) || (keybd->key == NULL) ||
(keybd->key->xkbInfo == NULL))
xkb = NULL;
else
xkb = keybd->key->xkbInfo->desc;
if ((names->keycodes == NULL) && (names->types == NULL) &&
(names->compat == NULL) && (names->symbols == NULL) &&
(names->geometry == NULL)) {
LogMessage(X_ERROR, "XKB: No components provided for device %s\n",
keybd->name ? keybd->name : "(unnamed keyboard)");
return 0;
}
else if (!XkbDDXCompileKeymapByNames(xkb, names, want, need,
nameRtrn, nameRtrnLen)) {
LogMessage(X_ERROR, "XKB: Couldn't compile keymap\n");
return 0;
}
return LoadXKM(want, need, nameRtrn, xkbRtrn);
}
Bool Bool
XkbDDXNamesFromRules(DeviceIntPtr keybd, XkbDDXNamesFromRules(DeviceIntPtr keybd,
const char *rules_name, const char *rules_name,
@ -390,6 +487,29 @@ XkbCompileKeymapForDevice(DeviceIntPtr dev, XkbRMLVOSet * rmlvo, int need)
return xkb; return xkb;
} }
static XkbDescPtr
KeymapOrDefaults(DeviceIntPtr dev, XkbDescPtr xkb)
{
XkbRMLVOSet dflts;
if (xkb)
return xkb;
/* we didn't get what we really needed. And that will likely leave
* us with a keyboard that doesn't work. Use the defaults instead */
LogMessage(X_ERROR, "XKB: Failed to load keymap. Loading default "
"keymap instead.\n");
XkbGetRulesDflts(&dflts);
xkb = XkbCompileKeymapForDevice(dev, &dflts, 0);
XkbFreeRMLVOSet(&dflts, FALSE);
return xkb;
}
XkbDescPtr XkbDescPtr
XkbCompileKeymap(DeviceIntPtr dev, XkbRMLVOSet * rmlvo) XkbCompileKeymap(DeviceIntPtr dev, XkbRMLVOSet * rmlvo)
{ {
@ -407,20 +527,34 @@ XkbCompileKeymap(DeviceIntPtr dev, XkbRMLVOSet * rmlvo)
xkb = XkbCompileKeymapForDevice(dev, rmlvo, need); xkb = XkbCompileKeymapForDevice(dev, rmlvo, need);
if (!xkb) { return KeymapOrDefaults(dev, xkb);
XkbRMLVOSet dflts; }
/* we didn't get what we really needed. And that will likely leave XkbDescPtr
* us with a keyboard that doesn't work. Use the defaults instead */ XkbCompileKeymapFromString(DeviceIntPtr dev,
LogMessage(X_ERROR, "XKB: Failed to load keymap. Loading default " const char *keymap, int keymap_length)
"keymap instead.\n"); {
XkbDescPtr xkb;
unsigned int need, provided;
XkbGetRulesDflts(&dflts); if (!dev || !keymap) {
LogMessage(X_ERROR, "XKB: No device or keymap specified\n");
xkb = XkbCompileKeymapForDevice(dev, &dflts, 0); return NULL;
XkbFreeRMLVOSet(&dflts, FALSE);
} }
return xkb; /* These are the components we really really need */
need = XkmSymbolsMask | XkmCompatMapMask | XkmTypesMask |
XkmKeyNamesMask | XkmVirtualModsMask;
provided =
XkbDDXLoadKeymapFromString(dev, keymap, keymap_length,
XkmAllIndicesMask, need, &xkb);
if ((need & provided) != need) {
if (xkb) {
XkbFreeKeyboard(xkb, 0, TRUE);
xkb = NULL;
}
}
return KeymapOrDefaults(dev, xkb);
} }

View File

@ -5950,25 +5950,13 @@ ProcXkbGetKbdByName(ClientPtr client)
if (rep.loaded) { if (rep.loaded) {
XkbDescPtr old_xkb; XkbDescPtr old_xkb;
xkbNewKeyboardNotify nkn; xkbNewKeyboardNotify nkn;
int i, nG, nTG;
old_xkb = xkb; old_xkb = xkb;
xkb = new; xkb = new;
dev->key->xkbInfo->desc = xkb; dev->key->xkbInfo->desc = xkb;
new = old_xkb; /* so it'll get freed automatically */ new = old_xkb; /* so it'll get freed automatically */
*xkb->ctrls = *old_xkb->ctrls; XkbCopyControls(xkb, old_xkb);
for (nG = nTG = 0, i = xkb->min_key_code; i <= xkb->max_key_code; i++) {
nG = XkbKeyNumGroups(xkb, i);
if (nG >= XkbNumKbdGroups) {
nTG = XkbNumKbdGroups;
break;
}
if (nG > nTG) {
nTG = nG;
}
}
xkb->ctrls->num_groups = nTG;
nkn.deviceID = nkn.oldDeviceID = dev->id; nkn.deviceID = nkn.oldDeviceID = dev->id;
nkn.minKeyCode = new->min_key_code; nkn.minKeyCode = new->min_key_code;
@ -5991,7 +5979,7 @@ ProcXkbGetKbdByName(ClientPtr client)
continue; continue;
if (tmpd != dev) if (tmpd != dev)
XkbCopyDeviceKeymap(tmpd, dev); XkbDeviceApplyKeymap(tmpd, xkb);
if (tmpd->kbdfeed && tmpd->kbdfeed->xkb_sli) { if (tmpd->kbdfeed && tmpd->kbdfeed->xkb_sli) {
old_sli = tmpd->kbdfeed->xkb_sli; old_sli = tmpd->kbdfeed->xkb_sli;

View File

@ -505,9 +505,10 @@ XkbInitControls(DeviceIntPtr pXDev, XkbSrvInfoPtr xkbi)
return Success; return Success;
} }
_X_EXPORT Bool static Bool
InitKeyboardDeviceStruct(DeviceIntPtr dev, XkbRMLVOSet * rmlvo, InitKeyboardDeviceStructInternal(DeviceIntPtr dev, XkbRMLVOSet * rmlvo,
BellProcPtr bell_func, KbdCtrlProcPtr ctrl_func) const char *keymap, int keymap_length,
BellProcPtr bell_func, KbdCtrlProcPtr ctrl_func)
{ {
int i; int i;
unsigned int check; unsigned int check;
@ -521,8 +522,9 @@ InitKeyboardDeviceStruct(DeviceIntPtr dev, XkbRMLVOSet * rmlvo,
BUG_RETURN_VAL(dev == NULL, FALSE); BUG_RETURN_VAL(dev == NULL, FALSE);
BUG_RETURN_VAL(dev->key != NULL, FALSE); BUG_RETURN_VAL(dev->key != NULL, FALSE);
BUG_RETURN_VAL(dev->kbdfeed != NULL, FALSE); BUG_RETURN_VAL(dev->kbdfeed != NULL, FALSE);
BUG_RETURN_VAL(rmlvo && keymap, FALSE);
if (!rmlvo) { if (!rmlvo && !keymap) {
rmlvo = &rmlvo_dflts; rmlvo = &rmlvo_dflts;
XkbGetRulesDflts(rmlvo); XkbGetRulesDflts(rmlvo);
} }
@ -550,7 +552,7 @@ InitKeyboardDeviceStruct(DeviceIntPtr dev, XkbRMLVOSet * rmlvo,
} }
dev->key->xkbInfo = xkbi; dev->key->xkbInfo = xkbi;
if (xkb_cached_map && !XkbCompareUsedRMLVO(rmlvo)) { if (xkb_cached_map && (keymap || (rmlvo && !XkbCompareUsedRMLVO(rmlvo)))) {
XkbFreeKeyboard(xkb_cached_map, XkbAllComponentsMask, TRUE); XkbFreeKeyboard(xkb_cached_map, XkbAllComponentsMask, TRUE);
xkb_cached_map = NULL; xkb_cached_map = NULL;
} }
@ -558,7 +560,11 @@ InitKeyboardDeviceStruct(DeviceIntPtr dev, XkbRMLVOSet * rmlvo,
if (xkb_cached_map) if (xkb_cached_map)
LogMessageVerb(X_INFO, 4, "XKB: Reusing cached keymap\n"); LogMessageVerb(X_INFO, 4, "XKB: Reusing cached keymap\n");
else { else {
xkb_cached_map = XkbCompileKeymap(dev, rmlvo); if (rmlvo)
xkb_cached_map = XkbCompileKeymap(dev, rmlvo);
else
xkb_cached_map = XkbCompileKeymapFromString(dev, keymap, keymap_length);
if (!xkb_cached_map) { if (!xkb_cached_map) {
ErrorF("XKB: Failed to compile keymap\n"); ErrorF("XKB: Failed to compile keymap\n");
goto unwind_info; goto unwind_info;
@ -627,8 +633,10 @@ InitKeyboardDeviceStruct(DeviceIntPtr dev, XkbRMLVOSet * rmlvo,
dev->kbdfeed->CtrlProc(dev, &dev->kbdfeed->ctrl); dev->kbdfeed->CtrlProc(dev, &dev->kbdfeed->ctrl);
XkbSetRulesDflts(rmlvo); if (rmlvo) {
XkbSetRulesUsed(rmlvo); XkbSetRulesDflts(rmlvo);
XkbSetRulesUsed(rmlvo);
}
XkbFreeRMLVOSet(&rmlvo_dflts, FALSE); XkbFreeRMLVOSet(&rmlvo_dflts, FALSE);
return TRUE; return TRUE;
@ -647,6 +655,24 @@ InitKeyboardDeviceStruct(DeviceIntPtr dev, XkbRMLVOSet * rmlvo,
return FALSE; return FALSE;
} }
_X_EXPORT Bool
InitKeyboardDeviceStruct(DeviceIntPtr dev, XkbRMLVOSet * rmlvo,
BellProcPtr bell_func, KbdCtrlProcPtr ctrl_func)
{
return InitKeyboardDeviceStructInternal(dev, rmlvo,
NULL, 0, bell_func, ctrl_func);
}
_X_EXPORT Bool
InitKeyboardDeviceStructFromString(DeviceIntPtr dev,
const char *keymap, int keymap_length,
BellProcPtr bell_func, KbdCtrlProcPtr ctrl_func)
{
return InitKeyboardDeviceStructInternal(dev, NULL,
keymap, keymap_length,
bell_func, ctrl_func);
}
/***====================================================================***/ /***====================================================================***/
/* /*

View File

@ -1999,28 +1999,28 @@ XkbCopyKeymap(XkbDescPtr dst, XkbDescPtr src)
} }
Bool Bool
XkbCopyDeviceKeymap(DeviceIntPtr dst, DeviceIntPtr src) XkbDeviceApplyKeymap(DeviceIntPtr dst, XkbDescPtr desc)
{ {
xkbNewKeyboardNotify nkn; xkbNewKeyboardNotify nkn;
Bool ret; Bool ret;
if (!dst->key || !src->key) if (!dst->key || !desc)
return FALSE; return FALSE;
memset(&nkn, 0, sizeof(xkbNewKeyboardNotify)); memset(&nkn, 0, sizeof(xkbNewKeyboardNotify));
nkn.oldMinKeyCode = dst->key->xkbInfo->desc->min_key_code; nkn.oldMinKeyCode = dst->key->xkbInfo->desc->min_key_code;
nkn.oldMaxKeyCode = dst->key->xkbInfo->desc->max_key_code; nkn.oldMaxKeyCode = dst->key->xkbInfo->desc->max_key_code;
nkn.deviceID = dst->id; nkn.deviceID = dst->id;
nkn.oldDeviceID = dst->id; /* maybe src->id? */ nkn.oldDeviceID = dst->id;
nkn.minKeyCode = src->key->xkbInfo->desc->min_key_code; nkn.minKeyCode = desc->min_key_code;
nkn.maxKeyCode = src->key->xkbInfo->desc->max_key_code; nkn.maxKeyCode = desc->max_key_code;
nkn.requestMajor = XkbReqCode; nkn.requestMajor = XkbReqCode;
nkn.requestMinor = X_kbSetMap; /* Near enough's good enough. */ nkn.requestMinor = X_kbSetMap; /* Near enough's good enough. */
nkn.changed = XkbNKN_KeycodesMask; nkn.changed = XkbNKN_KeycodesMask;
if (src->key->xkbInfo->desc->geom) if (desc->geom)
nkn.changed |= XkbNKN_GeometryMask; nkn.changed |= XkbNKN_GeometryMask;
ret = XkbCopyKeymap(dst->key->xkbInfo->desc, src->key->xkbInfo->desc); ret = XkbCopyKeymap(dst->key->xkbInfo->desc, desc);
if (ret) if (ret)
XkbSendNewKeyboardNotify(dst, &nkn); XkbSendNewKeyboardNotify(dst, &nkn);
@ -2090,3 +2090,26 @@ XkbMergeLockedPtrBtns(DeviceIntPtr master)
xkbi->lockedPtrButtons |= d->key->xkbInfo->lockedPtrButtons; xkbi->lockedPtrButtons |= d->key->xkbInfo->lockedPtrButtons;
} }
} }
void
XkbCopyControls(XkbDescPtr dst, XkbDescPtr src)
{
int i, nG, nTG;
if (!dst || !src)
return;
*dst->ctrls = *src->ctrls;
for (nG = nTG = 0, i = dst->min_key_code; i <= dst->max_key_code; i++) {
nG = XkbKeyNumGroups(dst, i);
if (nG >= XkbNumKbdGroups) {
nTG = XkbNumKbdGroups;
break;
}
if (nG > nTG) {
nTG = nG;
}
}
dst->ctrls->num_groups = nTG;
}