Add keycode min/max and iteration API
[platform/upstream/libxkbcommon.git] / src / keymap.c
index bc2632f..8205bab 100644 (file)
@@ -53,7 +53,7 @@
 #include "keymap.h"
 #include "text.h"
 
-struct xkb_keymap *
+static struct xkb_keymap *
 xkb_keymap_new(struct xkb_context *ctx,
                enum xkb_keymap_format format,
                enum xkb_keymap_compile_flags flags)
@@ -89,24 +89,28 @@ xkb_keymap_unref(struct xkb_keymap *keymap)
     if (!keymap || --keymap->refcnt > 0)
         return;
 
-    darray_foreach(key, keymap->keys) {
-        for (i = 0; i < key->num_groups; i++) {
-            for (j = 0; j < XkbKeyGroupWidth(key, i); j++)
-                if (key->groups[i].levels[j].num_syms > 1)
-                    free(key->groups[i].levels[j].u.syms);
-            free(key->groups[i].levels);
+    if (keymap->keys) {
+        xkb_foreach_key(key, keymap) {
+            for (i = 0; i < key->num_groups; i++) {
+                for (j = 0; j < XkbKeyGroupWidth(key, i); j++)
+                    if (key->groups[i].levels[j].num_syms > 1)
+                        free(key->groups[i].levels[j].u.syms);
+                free(key->groups[i].levels);
+            }
+            free(key->groups);
         }
-        free(key->groups);
+        free(keymap->keys);
     }
-    darray_free(keymap->keys);
     for (i = 0; i < keymap->num_types; i++) {
-        free(keymap->types[i].map);
+        free(keymap->types[i].entries);
         free(keymap->types[i].level_names);
     }
     free(keymap->types);
-    darray_free(keymap->sym_interpret);
-    darray_free(keymap->key_aliases);
-    darray_free(keymap->group_names);
+    darray_free(keymap->sym_interprets);
+    free(keymap->key_aliases);
+    free(keymap->group_names);
+    darray_free(keymap->mods);
+    darray_free(keymap->leds);
     free(keymap->keycodes_section_name);
     free(keymap->symbols_section_name);
     free(keymap->types_section_name);
@@ -115,21 +119,179 @@ xkb_keymap_unref(struct xkb_keymap *keymap)
     free(keymap);
 }
 
+static const struct xkb_keymap_format_ops *
+get_keymap_format_ops(enum xkb_keymap_format format)
+{
+    static const struct xkb_keymap_format_ops *keymap_format_ops[] = {
+        [XKB_KEYMAP_FORMAT_TEXT_V1] = &text_v1_keymap_format_ops,
+    };
+
+    if ((int) format < 0 || (int) format >= ARRAY_SIZE(keymap_format_ops))
+        return NULL;
+
+    return keymap_format_ops[format];
+}
+
+XKB_EXPORT struct xkb_keymap *
+xkb_keymap_new_from_names(struct xkb_context *ctx,
+                          const struct xkb_rule_names *rmlvo_in,
+                          enum xkb_keymap_compile_flags flags)
+{
+    struct xkb_keymap *keymap;
+    struct xkb_rule_names rmlvo;
+    const enum xkb_keymap_format format = XKB_KEYMAP_FORMAT_TEXT_V1;
+    const struct xkb_keymap_format_ops *ops;
+
+    ops = get_keymap_format_ops(format);
+    if (!ops || !ops->keymap_new_from_names) {
+        log_err_func(ctx, "unsupported keymap format: %d\n", format);
+        return NULL;
+    }
+
+    if (flags & ~(XKB_MAP_COMPILE_PLACEHOLDER)) {
+        log_err_func(ctx, "unrecognized flags: %#x\n", flags);
+        return NULL;
+    }
+
+    if (rmlvo_in)
+        rmlvo = *rmlvo_in;
+    else
+        memset(&rmlvo, 0, sizeof(rmlvo));
+
+    if (isempty(rmlvo.rules))
+        rmlvo.rules = xkb_context_get_default_rules(ctx);
+    if (isempty(rmlvo.model))
+        rmlvo.model = xkb_context_get_default_model(ctx);
+    /* Layout and variant are tied together, so don't try to use one from
+     * the caller and one from the environment. */
+    if (isempty(rmlvo.layout)) {
+        rmlvo.layout = xkb_context_get_default_layout(ctx);
+        rmlvo.variant = xkb_context_get_default_variant(ctx);
+    }
+    /* Options can be empty, so respect that if passed in. */
+    if (rmlvo.options == NULL)
+        rmlvo.options = xkb_context_get_default_options(ctx);
+
+    keymap = xkb_keymap_new(ctx, format, flags);
+    if (!keymap)
+        return NULL;
+
+    if (!ops->keymap_new_from_names(keymap, &rmlvo)) {
+        xkb_keymap_unref(keymap);
+        return NULL;
+    }
+
+    return keymap;
+}
+
+XKB_EXPORT struct xkb_keymap *
+xkb_keymap_new_from_string(struct xkb_context *ctx,
+                           const char *string,
+                           enum xkb_keymap_format format,
+                           enum xkb_keymap_compile_flags flags)
+{
+    return xkb_keymap_new_from_buffer(ctx, string, SIZE_MAX, format, flags);
+}
+
+XKB_EXPORT struct xkb_keymap *
+xkb_keymap_new_from_buffer(struct xkb_context *ctx,
+                           const char *buffer, size_t length,
+                           enum xkb_keymap_format format,
+                           enum xkb_keymap_compile_flags flags)
+{
+    struct xkb_keymap *keymap;
+    const struct xkb_keymap_format_ops *ops;
+
+    ops = get_keymap_format_ops(format);
+    if (!ops || !ops->keymap_new_from_string) {
+        log_err_func(ctx, "unsupported keymap format: %d\n", format);
+        return NULL;
+    }
+
+    if (flags & ~(XKB_MAP_COMPILE_PLACEHOLDER)) {
+        log_err_func(ctx, "unrecognized flags: %#x\n", flags);
+        return NULL;
+    }
+
+    if (!buffer) {
+        log_err_func1(ctx, "no buffer specified\n");
+        return NULL;
+    }
+
+    keymap = xkb_keymap_new(ctx, format, flags);
+    if (!keymap)
+        return NULL;
+
+    if (!ops->keymap_new_from_string(keymap, buffer, length)) {
+        xkb_keymap_unref(keymap);
+        return NULL;
+    }
+
+    return keymap;
+}
+
+XKB_EXPORT struct xkb_keymap *
+xkb_keymap_new_from_file(struct xkb_context *ctx,
+                         FILE *file,
+                         enum xkb_keymap_format format,
+                         enum xkb_keymap_compile_flags flags)
+{
+    struct xkb_keymap *keymap;
+    const struct xkb_keymap_format_ops *ops;
+
+    ops = get_keymap_format_ops(format);
+    if (!ops || !ops->keymap_new_from_file) {
+        log_err_func(ctx, "unsupported keymap format: %d\n", format);
+        return NULL;
+    }
+
+    if (flags & ~(XKB_MAP_COMPILE_PLACEHOLDER)) {
+        log_err_func(ctx, "unrecognized flags: %#x\n", flags);
+        return NULL;
+    }
+
+    if (!file) {
+        log_err_func1(ctx, "no file specified\n");
+        return NULL;
+    }
+
+    keymap = xkb_keymap_new(ctx, format, flags);
+    if (!keymap)
+        return NULL;
+
+    if (!ops->keymap_new_from_file(keymap, file)) {
+        xkb_keymap_unref(keymap);
+        return NULL;
+    }
+
+    return keymap;
+}
+
+XKB_EXPORT char *
+xkb_keymap_get_as_string(struct xkb_keymap *keymap,
+                         enum xkb_keymap_format format)
+{
+    const struct xkb_keymap_format_ops *ops;
+
+    if (format == XKB_KEYMAP_USE_ORIGINAL_FORMAT)
+        format = keymap->format;
+
+    ops = get_keymap_format_ops(format);
+    if (!ops || !ops->keymap_get_as_string) {
+        log_err_func(keymap->ctx, "unsupported keymap format: %d\n", format);
+        return NULL;
+    }
+
+    return ops->keymap_get_as_string(keymap);
+}
+
 /**
  * Returns the total number of modifiers active in the keymap.
  */
 XKB_EXPORT xkb_mod_index_t
 xkb_keymap_num_mods(struct xkb_keymap *keymap)
 {
-    xkb_mod_index_t i;
-
-    for (i = 0; i < XKB_NUM_VIRTUAL_MODS; i++)
-        if (!keymap->vmod_names[i])
-            break;
-
-    /* We always have all the core modifiers (for now), plus any virtual
-     * modifiers we may have defined. */
-    return i + XKB_NUM_CORE_MODS;
+    return darray_size(keymap->mods);
 }
 
 /**
@@ -138,19 +300,10 @@ xkb_keymap_num_mods(struct xkb_keymap *keymap)
 XKB_EXPORT const char *
 xkb_keymap_mod_get_name(struct xkb_keymap *keymap, xkb_mod_index_t idx)
 {
-    const char *name;
-
-    if (idx >= xkb_keymap_num_mods(keymap))
+    if (idx >= darray_size(keymap->mods))
         return NULL;
 
-    /* First try to find a legacy modifier name.  If that fails, try to
-     * find a virtual mod name. */
-    name = ModIndexToName(idx);
-    if (!name)
-        name = xkb_atom_text(keymap->ctx,
-                             keymap->vmod_names[idx - XKB_NUM_CORE_MODS]);
-
-    return name;
+    return xkb_atom_text(keymap->ctx, darray_item(keymap->mods, idx).name);
 }
 
 /**
@@ -161,21 +314,15 @@ xkb_keymap_mod_get_index(struct xkb_keymap *keymap, const char *name)
 {
     xkb_mod_index_t i;
     xkb_atom_t atom;
-
-    i = ModNameToIndex(name);
-    if (i != XKB_MOD_INVALID)
-        return i;
+    const struct xkb_mod *mod;
 
     atom = xkb_atom_lookup(keymap->ctx, name);
     if (atom == XKB_ATOM_NONE)
         return XKB_MOD_INVALID;
 
-    for (i = 0; i < XKB_NUM_VIRTUAL_MODS; i++) {
-        if (keymap->vmod_names[i] == XKB_ATOM_NONE)
-            break;
-        if (keymap->vmod_names[i] == atom)
-            return i + XKB_NUM_CORE_MODS;
-    }
+    darray_enumerate(i, mod, keymap->mods)
+        if (mod->name == atom)
+            return i;
 
     return XKB_MOD_INVALID;
 }
@@ -195,11 +342,10 @@ xkb_keymap_num_layouts(struct xkb_keymap *keymap)
 XKB_EXPORT const char *
 xkb_keymap_layout_get_name(struct xkb_keymap *keymap, xkb_layout_index_t idx)
 {
-    if (idx >= xkb_keymap_num_layouts(keymap) ||
-        idx >= darray_size(keymap->group_names))
+    if (idx >= keymap->num_group_names)
         return NULL;
 
-    return xkb_atom_text(keymap->ctx, darray_item(keymap->group_names, idx));
+    return xkb_atom_text(keymap->ctx, keymap->group_names[idx]);
 }
 
 /**
@@ -209,14 +355,13 @@ XKB_EXPORT xkb_layout_index_t
 xkb_keymap_layout_get_index(struct xkb_keymap *keymap, const char *name)
 {
     xkb_atom_t atom = xkb_atom_lookup(keymap->ctx, name);
-    xkb_atom_t *group_name;
     xkb_layout_index_t i;
 
     if (atom == XKB_ATOM_NONE)
         return XKB_LAYOUT_INVALID;
 
-    darray_enumerate(i, group_name, keymap->group_names)
-        if (*group_name == atom)
+    for (i = 0; i < keymap->num_group_names; i++)
+        if (keymap->group_names[i] == atom)
             return i;
 
     return XKB_LAYOUT_INVALID;
@@ -248,54 +393,51 @@ xkb_keymap_num_levels_for_key(struct xkb_keymap *keymap, xkb_keycode_t kc,
     if (!key)
         return 0;
 
+    layout = wrap_group_into_range(layout, key->num_groups,
+                                   key->out_of_range_group_action,
+                                   key->out_of_range_group_number);
+    if (layout == XKB_LAYOUT_INVALID)
+        return 0;
+
     return XkbKeyGroupWidth(key, layout);
 }
 
 /**
- * Return the total number of active LEDs in the keymap.
+ * Return the total number of LEDs in the keymap.
  */
 XKB_EXPORT xkb_led_index_t
 xkb_keymap_num_leds(struct xkb_keymap *keymap)
 {
-    xkb_led_index_t ret = 0;
-    xkb_led_index_t i;
-
-    for (i = 0; i < XKB_NUM_INDICATORS; i++)
-        if (keymap->indicators[i].which_groups ||
-            keymap->indicators[i].which_mods ||
-            keymap->indicators[i].ctrls)
-            ret++;
-
-    return ret;
+    return darray_size(keymap->leds);
 }
 
 /**
- * Returns the name for a given group.
+ * Returns the name for a given LED.
  */
 XKB_EXPORT const char *
 xkb_keymap_led_get_name(struct xkb_keymap *keymap, xkb_led_index_t idx)
 {
-    if (idx >= xkb_keymap_num_leds(keymap))
+    if (idx >= darray_size(keymap->leds))
         return NULL;
 
-    return xkb_atom_text(keymap->ctx, keymap->indicators[idx].name);
+    return xkb_atom_text(keymap->ctx, darray_item(keymap->leds, idx).name);
 }
 
 /**
- * Returns the index for a named group.
+ * Returns the index for a named LED.
  */
-XKB_EXPORT xkb_layout_index_t
+XKB_EXPORT xkb_led_index_t
 xkb_keymap_led_get_index(struct xkb_keymap *keymap, const char *name)
 {
-    xkb_led_index_t num_leds = xkb_keymap_num_leds(keymap);
     xkb_atom_t atom = xkb_atom_lookup(keymap->ctx, name);
     xkb_led_index_t i;
+    const struct xkb_led *led;
 
     if (atom == XKB_ATOM_NONE)
         return XKB_LED_INVALID;
 
-    for (i = 0; i < num_leds; i++)
-        if (keymap->indicators[i].name == atom)
+    darray_enumerate(i, led, keymap->leds)
+        if (led->name == atom)
             return i;
 
     return XKB_LED_INVALID;
@@ -316,8 +458,13 @@ xkb_keymap_key_get_syms_by_level(struct xkb_keymap *keymap,
 
     if (!key)
         goto err;
-    if (layout >= key->num_groups)
+
+    layout = wrap_group_into_range(layout, key->num_groups,
+                                   key->out_of_range_group_action,
+                                   key->out_of_range_group_number);
+    if (layout == XKB_LAYOUT_INVALID)
         goto err;
+
     if (level >= XkbKeyGroupWidth(key, layout))
         goto err;
 
@@ -337,6 +484,28 @@ err:
     return 0;
 }
 
+XKB_EXPORT xkb_keycode_t
+xkb_keymap_min_keycode(struct xkb_keymap *keymap)
+{
+    return keymap->min_key_code;
+}
+
+XKB_EXPORT xkb_keycode_t
+xkb_keymap_max_keycode(struct xkb_keymap *keymap)
+{
+    return keymap->max_key_code;
+}
+
+XKB_EXPORT void
+xkb_keymap_key_for_each(struct xkb_keymap *keymap, xkb_keymap_key_iter_t iter,
+                        void *data)
+{
+    struct xkb_key *key;
+
+    xkb_foreach_key(key, keymap)
+        iter(keymap, key->keycode, data);
+}
+
 /**
  * Simple boolean specifying whether or not the key should repeat.
  */
@@ -350,3 +519,31 @@ xkb_keymap_key_repeats(struct xkb_keymap *keymap, xkb_keycode_t kc)
 
     return key->repeats;
 }
+
+struct xkb_key *
+XkbKeyByName(struct xkb_keymap *keymap, xkb_atom_t name, bool use_aliases)
+{
+    struct xkb_key *key;
+
+    xkb_foreach_key(key, keymap)
+        if (key->name == name)
+            return key;
+
+    if (use_aliases) {
+        xkb_atom_t new_name = XkbResolveKeyAlias(keymap, name);
+        if (new_name != XKB_ATOM_NONE)
+            return XkbKeyByName(keymap, new_name, false);
+    }
+
+    return NULL;
+}
+
+xkb_atom_t
+XkbResolveKeyAlias(struct xkb_keymap *keymap, xkb_atom_t name)
+{
+    for (unsigned i = 0; i < keymap->num_key_aliases; i++)
+        if (keymap->key_aliases[i].alias == name)
+            return keymap->key_aliases[i].real;
+
+    return XKB_ATOM_NONE;
+}