diff options
-rw-r--r-- | dix/devices.c | 10 | ||||
-rw-r--r-- | dix/events.c | 10 | ||||
-rw-r--r-- | xkb/xkb.h | 4 | ||||
-rw-r--r-- | xkb/xkbUtils.c | 404 |
4 files changed, 410 insertions, 18 deletions
diff --git a/dix/devices.c b/dix/devices.c index 773b301dc..60c2d297b 100644 --- a/dix/devices.c +++ b/dix/devices.c @@ -260,16 +260,6 @@ CoreKeyboardProc(DeviceIntPtr pDev, int what) break; case DEVICE_CLOSE: - /* This, uh, probably requires some explanation. - * Press a key on another keyboard. - * Watch its xkbInfo get pivoted into core's. - * Kill the server. - * Watch the first device's xkbInfo get freed. - * Try to free ours, which points to same. - * - * ... yeah. - */ - pDev->key->xkbInfo = NULL; pDev->devPrivates[CoreDevicePrivatesIndex].ptr = NULL; break; diff --git a/dix/events.c b/dix/events.c index 47c52d1fe..808694d79 100644 --- a/dix/events.c +++ b/dix/events.c @@ -4802,14 +4802,8 @@ int GetKeyboardValuatorEvents(xEvent *events, DeviceIntPtr pDev, int type, mn.virtualMods = ~0; /* ??? */ mn.changed = XkbAllMapComponentsMask; - /* If this is still the map we set at DEVICE_INIT, free it so - * it doesn't just get lost. (Shameful hack, sorry.) */ - if (!inputInfo.keyboard->devPrivates[CoreDevicePrivatesIndex].ptr && - ckeyc->xkbInfo) - XkbFreeInfo(ckeyc->xkbInfo); - /* FIXME we really need a map copy here. */ - ckeyc->xkbInfo = pDev->key->xkbInfo; - XkbSendMapNotify(inputInfo.keyboard, &mn); + if (!XkbCopyKeymap(pDev->key->xkbInfo, ckeyc->xkbInfo)) + FatalError("Couldn't pivot keymap from device to core!\n"); } #endif SendMappingNotify(MappingKeyboard, ckeyc->curKeySyms.minKeyCode, @@ -72,3 +72,7 @@ extern Bool XkbDDXCompileKeymapByNames( unsigned need, char * nameRtrn, int nameRtrnLen); + +extern Bool XkbCopyKeymap( + XkbDescPtr src, + XkbDescPtr dst); diff --git a/xkb/xkbUtils.c b/xkb/xkbUtils.c index ee0abbeae..4fa536968 100644 --- a/xkb/xkbUtils.c +++ b/xkb/xkbUtils.c @@ -969,3 +969,407 @@ XkbConvertCase(register KeySym sym, KeySym *lower, KeySym *upper) break; } } + + +/** + * Copy an XKB map from src to dst, reallocating when necessary: if some + * map components are present in one, but not in the other, the destination + * components will be allocated or freed as necessary. + * + * Basic map consistency is assumed on both sides, so maps with random + * uninitialised data (e.g. names->radio_grous == NULL, names->num_rg == 19) + * _will_ cause failures. You've been warned. + * + * Returns TRUE on success, or FALSE on failure. If this function fails, + * dst may be in an inconsistent state: all its pointers are guaranteed + * to remain valid, but part of the map may be from src and part from dst. + */ +Bool +XkbCopyKeymap(XkbDescPtr src, XkbDescPtr dst) +{ + int i = 0, j = 0; + void *tmp = NULL; + XkbKeyTypePtr stype = NULL, dtype = NULL; + + if (!src || !dst) + return FALSE; + + /* client map */ + if (src->map) { + if (!dst->map) { + tmp = xcalloc(1, sizeof(XkbClientMapRec)); + if (!tmp) + return FALSE; + dst->map = tmp; + } + + if (src->map->syms) { + if (src->map->size_syms != dst->map->size_syms) { + if (dst->map->syms) + tmp = xrealloc(dst->map->syms, src->map->size_syms); + else + tmp = xalloc(src->map->size_syms); + if (!tmp) + return FALSE; + dst->map->syms = tmp; + } + memcpy(dst->map->syms, src->map->syms, src->map->size_syms); + memcpy(dst->map->key_sym_map, src->map->key_sym_map, + src->map->size_syms); + } + else { + if (dst->map->syms) { + xfree(dst->map->syms); + dst->map->syms = NULL; + } + } + dst->map->num_syms = src->map->num_syms; + dst->map->size_syms = src->map->size_syms; + + if (src->map->types) { + if (src->map->size_types != dst->map->size_types) { + XkbKeyTypePtr stype = src->map->types; + + if (dst->map->types) + tmp = xrealloc(dst->map->types, src->map->size_types); + else + tmp = xalloc(src->map->size_types); + if (!tmp) + return FALSE; + dst->map->types = tmp; + } + memcpy(dst->map->types, src->map->types, src->map->size_types); + + stype = src->map->types; + dtype = dst->map->types; + for (i = 0; i < dst->map->num_types; i++, dtype++, stype++) { + dtype->level_names = xalloc(dtype->num_levels * sizeof(Atom)); + if (!dtype->level_names) + continue; /* don't return FALSE here, try to whack all the + pointers we can, so we don't double-free when + going down. */ + memcpy(dtype->level_names, stype->level_names, + dtype->num_levels * sizeof(Atom)); + } + } + else { + if (dst->map->types) { + for (i = 0, dtype = dst->map->types; i < dst->map->num_types; + i++, dtype++) + xfree(dtype->level_names); + xfree(dst->map->types); + dst->map->types = NULL; + } + } + dst->map->size_types = src->map->size_types; + dst->map->num_types = src->map->num_types; + + if (src->map->modmap) { + if (src->max_key_code != dst->max_key_code) { + if (dst->map->modmap) + tmp = xrealloc(dst->map->modmap, src->max_key_code + 1); + else + tmp = xalloc(src->max_key_code + 1); + if (!tmp) + return FALSE; + dst->map->syms = tmp; + } + memcpy(dst->map->modmap, src->map->modmap, src->max_key_code + 1); + } + else { + if (dst->map->modmap) { + xfree(dst->map->modmap); + dst->map->modmap = NULL; + } + } + } + else { + if (dst->map) + XkbFreeClientMap(dst, XkbAllClientInfoMask, True); + } + + /* server map */ + if (src->server) { + if (!dst->server) { + tmp = xcalloc(1, sizeof(XkbServerMapRec)); + if (!tmp) + return FALSE; + dst->server = tmp; + } + + if (src->server->explicit) { + if (src->max_key_code != dst->max_key_code) { + if (dst->server->explicit) + tmp = xrealloc(dst->server->explicit, src->max_key_code + 1); + else + tmp = xalloc(src->max_key_code + 1); + if (!tmp) + return FALSE; + dst->server->explicit = tmp; + } + memcpy(dst->server->explicit, src->server->explicit, + src->max_key_code + 1); + } + else { + if (dst->server->explicit) { + xfree(dst->server->explicit); + dst->server->explicit = NULL; + } + } + + if (src->server->acts) { + if (src->server->size_acts != dst->server->size_acts) { + if (dst->server->acts) + tmp = xrealloc(dst->server->acts, src->server->size_acts); + else + tmp = xalloc(src->server->size_acts); + if (!tmp) + return FALSE; + dst->server->acts = tmp; + } + memcpy(dst->server->acts, src->server->acts, + src->server->size_acts); + } + else { + if (dst->server->acts) { + xfree(dst->server->acts); + dst->server->acts = NULL; + } + } + dst->server->size_acts = src->server->size_acts; + + if (src->server->key_acts) { + if (src->max_key_code != dst->max_key_code) { + if (dst->server->key_acts) + tmp = xrealloc(dst->server->key_acts, src->max_key_code + 1); + else + tmp = xalloc(src->max_key_code + 1); + if (!tmp) + return FALSE; + dst->server->key_acts = tmp; + } + memcpy(dst->server->key_acts, src->server->key_acts, + src->max_key_code + 1); + } + else { + if (dst->server->key_acts) { + xfree(dst->server->key_acts); + dst->server->key_acts = NULL; + } + } + + if (src->server->behaviors) { + if (src->max_key_code != dst->max_key_code) { + if (dst->server->behaviors) + tmp = xrealloc(dst->server->behaviors, + (src->max_key_code + 1) * + sizeof(XkbBehavior)); + else + tmp = xalloc((src->max_key_code + 1) * + sizeof(XkbBehavior)); + if (!tmp) + return FALSE; + dst->server->behaviors = tmp; + } + memcpy(dst->server->behaviors, src->server->behaviors, + (src->max_key_code + 1) * sizeof(XkbBehavior)); + } + else { + if (dst->server->behaviors) { + xfree(dst->server->behaviors); + dst->server->behaviors = NULL; + } + } + + if (src->server->vmodmap) { + if (src->max_key_code != dst->max_key_code) { + if (dst->server->vmodmap) + tmp = xrealloc(dst->server->vmodmap, + src->max_key_code + 1); + else + tmp = xalloc(src->max_key_code + 1); + if (!tmp) + return FALSE; + dst->server->vmodmap = tmp; + } + memcpy(dst->server->vmodmap, src->server->vmodmap, + src->max_key_code + 1); + } + else { + if (dst->server->vmodmap) { + xfree(dst->server->vmodmap); + dst->server->vmodmap = NULL; + } + } + } + else { + if (dst->server) + XkbFreeServerMap(dst, XkbAllServerInfoMask, True); + } + + /* indicators */ + if (src->indicators) { + if (!dst->indicators) { + dst->indicators = xalloc(sizeof(XkbIndicatorRec)); + if (!dst->indicators) + return FALSE; + } + memcpy(dst->indicators, src->indicators, sizeof(XkbIndicatorRec)); + } + else { + if (dst->indicators) { + xfree(dst->indicators); + dst->indicators = NULL; + } + } + + /* controls */ + if (src->ctrls) { + if (!dst->ctrls) { + dst->ctrls = xalloc(sizeof(XkbControlsRec)); + if (!dst->ctrls) + return FALSE; + } + memcpy(dst->ctrls, src->ctrls, sizeof(XkbControlsRec)); + } + else { + if (dst->ctrls) { + xfree(dst->ctrls); + dst->ctrls = NULL; + } + } + + /* names */ + if (src->names) { + if (!dst->names) { + dst->names = xcalloc(1, sizeof(XkbNamesRec)); + if (!dst->names) + return FALSE; + } + + if (src->names->keys) { + if (src->max_key_code != dst->max_key_code) { + if (dst->names->keys) + tmp = xrealloc(dst->names->keys, (src->max_key_code + 1) * + sizeof(XkbKeyNameRec)); + else + tmp = xalloc((src->max_key_code + 1) * + sizeof(XkbKeyNameRec)); + if (!tmp) + return FALSE; + dst->names->keys = tmp; + } + memcpy(dst->names->keys, src->names->keys, + (src->max_key_code + 1) * sizeof(XkbKeyNameRec)); + } + else { + if (dst->names->keys) { + xfree(dst->names->keys); + dst->names->keys = NULL; + } + } + + if (src->names->num_key_aliases) { + if (src->names->num_key_aliases != dst->names->num_key_aliases) { + if (dst->names->key_aliases) + tmp = xrealloc(dst->names->key_aliases, + src->names->num_key_aliases * + sizeof(XkbKeyAliasRec)); + else + tmp = xalloc(src->names->num_key_aliases * + sizeof(XkbKeyAliasRec)); + if (!tmp) + return FALSE; + dst->names->key_aliases = tmp; + } + memcpy(dst->names->key_aliases, src->names->key_aliases, + src->names->num_key_aliases * sizeof(XkbKeyAliasRec)); + } + else { + if (dst->names->key_aliases) { + xfree(dst->names->key_aliases); + dst->names->key_aliases = NULL; + } + } + dst->names->num_key_aliases = src->names->num_key_aliases; + + if (src->names->num_rg) { + if (src->names->num_rg != dst->names->num_rg) { + if (dst->names->radio_groups) + tmp = xrealloc(dst->names->radio_groups, + src->names->num_rg * sizeof(Atom)); + else + tmp = xalloc(src->names->num_rg * sizeof(Atom)); + if (!tmp) + return FALSE; + dst->names->radio_groups = tmp; + } + memcpy(dst->names->radio_groups, src->names->radio_groups, + src->names->num_rg * sizeof(Atom)); + } + else { + if (dst->names->radio_groups) + xfree(dst->names->radio_groups); + } + dst->names->num_rg = src->names->num_rg; + } + else { + if (dst->names) + XkbFreeNames(dst, XkbAllNamesMask, True); + } + + /* compat */ + if (src->compat) { + if (src->compat->sym_interpret) { + if (src->compat->num_si != dst->compat->num_si) { + if (dst->compat->sym_interpret) + tmp = xrealloc(dst->compat->sym_interpret, + src->compat->num_si * + sizeof(XkbSymInterpretRec)); + else + tmp = xalloc(src->compat->num_si * + sizeof(XkbSymInterpretRec)); + if (!tmp) + return FALSE; + dst->compat->sym_interpret = tmp; + } + memcpy(dst->compat->sym_interpret, src->compat->sym_interpret, + src->compat->num_si * sizeof(XkbSymInterpretRec)); + } + else { + if (dst->compat->sym_interpret) { + xfree(dst->compat->sym_interpret); + dst->compat->sym_interpret = NULL; + } + } + dst->compat->num_si = src->compat->num_si; + + memcpy(dst->compat->groups, src->compat->groups, + XkbNumKbdGroups * sizeof(XkbModsRec)); + } + else { + if (dst->compat) + XkbFreeCompatMap(dst, XkbAllCompatMask, True); + } + + /* geometry */ + if (src->geom) { + /* properties */ + /* colors */ + /* shapes */ + /* sections */ + /* doodads */ + /* key aliases */ + /* font?!? */ + } + else { + if (dst->geom) { + XkbFreeGeometry(dst->geom, XkbGeomAllMask, True); + } + } + + dst->min_key_code = src->min_key_code; + dst->max_key_code = src->max_key_code; + + return TRUE; +} |