summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--dix/devices.c10
-rw-r--r--dix/events.c10
-rw-r--r--xkb/xkb.h4
-rw-r--r--xkb/xkbUtils.c404
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,
diff --git a/xkb/xkb.h b/xkb/xkb.h
index 156608ef0..a85df8e6f 100644
--- a/xkb/xkb.h
+++ b/xkb/xkb.h
@@ -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;
+}