From 32f3e18614ccef0264be44e77898466b61fea59f Mon Sep 17 00:00:00 2001 From: Pierre Le Marre Date: Sat, 16 Sep 2023 16:12:47 +0200 Subject: [PATCH] Use an iterator API - Also perform some minor renaming. - Add some tests - Add option --file - Add benchmark for compose traversal --- bench/compose-traversal.c | 88 ++++++++++++++ include/xkbcommon/xkbcommon-compose.h | 106 ++++++++++++----- meson.build | 5 + src/compose/table.c | 164 ++++++++++++++++++++------ src/compose/table.h | 4 +- src/darray.h | 5 + test/compose.c | 105 +++++++++++++++++ tools/compose.c | 50 ++++++-- xkbcommon.map | 12 +- 9 files changed, 457 insertions(+), 82 deletions(-) create mode 100644 bench/compose-traversal.c diff --git a/bench/compose-traversal.c b/bench/compose-traversal.c new file mode 100644 index 000000000..be24beba7 --- /dev/null +++ b/bench/compose-traversal.c @@ -0,0 +1,88 @@ +/* + * Copyright © 2023 Pierre Le Marre + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "config.h" + +#include + +#include "xkbcommon/xkbcommon-compose.h" + +#include "../test/test.h" +#include "bench.h" + +#define BENCHMARK_ITERATIONS 1000 + +int +main(void) +{ + struct xkb_context *ctx; + char *path; + FILE *file; + struct xkb_compose_table *table; + struct xkb_compose_table_iterator *iter; + struct xkb_compose_table_entry *entry; + struct bench bench; + char *elapsed; + + ctx = test_get_context(CONTEXT_NO_FLAG); + assert(ctx); + + path = test_get_path("locale/en_US.UTF-8/Compose"); + file = fopen(path, "rb"); + if (file == NULL) { + perror(path); + free(path); + xkb_context_unref(ctx); + return -1; + } + free(path); + + xkb_context_set_log_level(ctx, XKB_LOG_LEVEL_CRITICAL); + xkb_context_set_log_verbosity(ctx, 0); + + table = xkb_compose_table_new_from_file(ctx, file, "", + XKB_COMPOSE_FORMAT_TEXT_V1, + XKB_COMPOSE_COMPILE_NO_FLAGS); + fclose(file); + assert(table); + + bench_start(&bench); + for (int i = 0; i < BENCHMARK_ITERATIONS; i++) { + iter = xkb_compose_table_iterator_new(table); + while ((entry = xkb_compose_table_iterator_next(iter))) { + assert (entry); + } + xkb_compose_table_iterator_free(iter); + } + bench_stop(&bench); + + xkb_compose_table_unref(table); + + elapsed = bench_elapsed_str(&bench); + fprintf(stderr, "traversed %d compose tables in %ss\n", + BENCHMARK_ITERATIONS, elapsed); + free(elapsed); + + xkb_context_unref(ctx); + return 0; +} diff --git a/include/xkbcommon/xkbcommon-compose.h b/include/xkbcommon/xkbcommon-compose.h index 607cae89c..36a3d7039 100644 --- a/include/xkbcommon/xkbcommon-compose.h +++ b/include/xkbcommon/xkbcommon-compose.h @@ -306,40 +306,52 @@ xkb_compose_table_unref(struct xkb_compose_table *table); * Represents a single entry in a Compose file in the iteration API. * It is immutable. * - * @sa xkb_compose_table_for_each - * @since 1.5.0 + * @sa xkb_compose_table_iterator_new + * @since 1.6.0 */ struct xkb_compose_table_entry; /** - * Get the left-hand side of a Compose table entry. + * Get the left-hand sequence of a Compose table entry. * - * For example, given the following entry - * \ \ : "~" asciitilde # TILDE - * returns `{XKB_KEY_dead_tilde, XKB_KEY_space}`. + * For example, given the following entry: + * + * ``` + * : "~" asciitilde # TILDE + * ``` + * + * it will return `{XKB_KEY_dead_tilde, XKB_KEY_space}`. + * + * @param[in] entry The compose table entry object to process. + * + * @param[out] sequence_length Number of keysyms in the sequence. * * @returns The array of left-hand side keysyms. The number of keysyms - * is returned in the nsyms out-parameter. + * is returned in the @p sequence_length out-parameter. * * @memberof xkb_compose_table_entry - * @since 1.5.0 + * @since 1.6.0 */ const xkb_keysym_t * -xkb_compose_table_entry_lhs(struct xkb_compose_table_entry *entry, - size_t *nsyms); +xkb_compose_table_entry_sequence(struct xkb_compose_table_entry *entry, + size_t *sequence_length); /** * Get the right-hand result keysym of a Compose table entry. * - * For example, given the following entry - * \ \ : "~" asciitilde # TILDE + * For example, given the following entry: + * + * ``` + * : "~" asciitilde # TILDE + * ``` + * * it will return `XKB_KEY_asciitilde`. * * The keysym is optional; if the entry does not specify a keysym, * returns `XKB_KEY_NoSymbol`. * * @memberof xkb_compose_table_entry - * @since 1.5.0 + * @since 1.6.0 */ xkb_keysym_t xkb_compose_table_entry_keysym(struct xkb_compose_table_entry *entry); @@ -349,44 +361,78 @@ xkb_compose_table_entry_keysym(struct xkb_compose_table_entry *entry); * * The string is UTF-8 encoded and `\0`-terminated. * - * For example, given the following entry - * \ \ : "~" asciitilde # TILDE + * For example, given the following entry: + * + * ``` + * : "~" asciitilde # TILDE + * ``` + * * it will return `"~"`. * * The string is optional; if the entry does not specify a string, * returns the empty string. * * @memberof xkb_compose_table_entry - * @since 1.5.0 + * @since 1.6.0 */ const char * xkb_compose_table_entry_utf8(struct xkb_compose_table_entry *entry); /** - * The iterator function type used by xkb_compose_table_for_each(). + * @struct xkb_compose_table_iterator + * Iterator over a compose table’s entries. * - * @sa xkb_compose_table_for_each - * @memberof xkb_compose - * @since 1.5.0 + * @sa xkb_compose_table_iterator_new() + * @since 1.6.0 */ -typedef void -(*xkb_compose_table_iter_t)(struct xkb_compose_table_entry *entry, - void *data); +struct xkb_compose_table_iterator; /** - * Run a specified function for every valid entry in the table. + * Create a new iterator for a compose table. + * + * Intended use: + * + * ```c + * struct xkb_compose_table_iterator *iter = xkb_compose_table_iterator_new(compose_table); + * struct xkb_compose_table_entry *entry; + * while (entry = xkb_compose_table_iterator_next(iter)) { + * // ... + * } + * xkb_compose_table_iterator_free(iter); + * ``` + * + * @returns A new compose table iterator, or `NULL` on failure. + * + * @memberof xkb_compose_table_iterator + * @sa xkb_compose_table_iterator_free() + * @since 1.6.0 + */ +struct xkb_compose_table_iterator * +xkb_compose_table_iterator_new(struct xkb_compose_table *table); + +/** + * Free a compose iterator. + * + * @memberof xkb_compose_table_iterator + * @since 1.6.0 + */ +void +xkb_compose_table_iterator_free(struct xkb_compose_table_iterator *iter); + +/** + * Get the next compose entry from a compose table iterator. * * The entries are returned in lexicographic order of the left-hand * side of entries. This does not correspond to the order in which * the entries appear in the Compose file. * - * @memberof xkb_compose_table - * @since 1.5.0 + * Returns `NULL` in case there is no more entries. + * + * @memberof xkb_compose_table_iterator + * @since 1.6.0 */ -void -xkb_compose_table_for_each(struct xkb_compose_table *table, - xkb_compose_table_iter_t iter, - void *data); +struct xkb_compose_table_entry * +xkb_compose_table_iterator_next(struct xkb_compose_table_iterator *iter); /** Flags for compose state creation. */ enum xkb_compose_state_flags { diff --git a/meson.build b/meson.build index 6a79093cb..451a55424 100644 --- a/meson.build +++ b/meson.build @@ -767,6 +767,11 @@ benchmark( executable('bench-compose', 'bench/compose.c', dependencies: test_dep), env: bench_env, ) +benchmark( + 'compose-traversal', + executable('bench-compose-traversal', 'bench/compose-traversal.c', dependencies: test_dep), + env: bench_env, +) benchmark( 'atom', executable('bench-atom', 'bench/atom.c', dependencies: test_dep), diff --git a/src/compose/table.c b/src/compose/table.c index 8227a0e2c..6b9098568 100644 --- a/src/compose/table.c +++ b/src/compose/table.c @@ -229,11 +229,11 @@ xkb_compose_table_new_from_locale(struct xkb_context *ctx, } XKB_EXPORT const xkb_keysym_t * -xkb_compose_table_entry_lhs(struct xkb_compose_table_entry *entry, - size_t *nsyms) +xkb_compose_table_entry_sequence(struct xkb_compose_table_entry *entry, + size_t *sequence_length) { - *nsyms = entry->nsyms; - return entry->syms; + *sequence_length = entry->sequence_length; + return entry->sequence; } XKB_EXPORT xkb_keysym_t @@ -248,43 +248,137 @@ xkb_compose_table_entry_utf8(struct xkb_compose_table_entry *entry) return entry->utf8; } -static void -for_each_helper(struct xkb_compose_table *table, - xkb_compose_table_iter_t iter, - void *data, - xkb_keysym_t *syms, - size_t nsyms, - uint16_t p) +enum node_direction { + NODE_LEFT = 0, + NODE_DOWN, + NODE_RIGHT, + NODE_UP +}; + +struct xkb_compose_table_iterator_cursor { + uint32_t node_offset:30; /* WARNING: ensure it fits MAX_COMPOSE_NODES */ + uint8_t direction:2; /* enum node_direction: current direction + * traversing the tree */ +}; + +struct xkb_compose_table_iterator { + struct xkb_compose_table *table; + /* Current entry */ + struct xkb_compose_table_entry entry; + /* Stack of pending nodes to process */ + darray(struct xkb_compose_table_iterator_cursor) cursors; +}; + +XKB_EXPORT struct xkb_compose_table_iterator * +xkb_compose_table_iterator_new(struct xkb_compose_table *table) { - if (!p) { - return; + struct xkb_compose_table_iterator *iter; + struct xkb_compose_table_iterator_cursor cursor; + xkb_keysym_t *sequence; + + iter = calloc(1, sizeof(*iter)); + if (!iter) { + return NULL; } - const struct compose_node *node = &darray_item(table->nodes, p); - for_each_helper(table, iter, data, syms, nsyms, node->lokid); - syms[nsyms++] = node->keysym; - if (node->is_leaf) { - struct xkb_compose_table_entry entry = { - .syms = syms, - .nsyms = nsyms, - .keysym = node->leaf.keysym, - .utf8 = &darray_item(table->utf8, node->leaf.utf8), - }; - iter(&entry, data); - } else { - for_each_helper(table, iter, data, syms, nsyms, node->internal.eqkid); + iter->table = xkb_compose_table_ref(table); + sequence = calloc(MAX_LHS_LEN, sizeof(xkb_keysym_t)); + if (!sequence) { + free(iter); + return NULL; } - nsyms--; - for_each_helper(table, iter, data, syms, nsyms, node->hikid); + iter->entry.sequence = sequence; + iter->entry.sequence_length = 0; + + darray_init(iter->cursors); + cursor.direction = NODE_LEFT; + /* Offset 0 is a dummy null entry, skip it. */ + cursor.node_offset = 1; + darray_append(iter->cursors, cursor); + + return iter; } XKB_EXPORT void -xkb_compose_table_for_each(struct xkb_compose_table *table, - xkb_compose_table_iter_t iter, - void *data) +xkb_compose_table_iterator_free(struct xkb_compose_table_iterator *iter) { - if (darray_size(table->nodes) <= 1) { - return; + xkb_compose_table_unref(iter->table); + darray_free(iter->cursors); + free(iter->entry.sequence); + free(iter); +} + +XKB_EXPORT struct xkb_compose_table_entry * +xkb_compose_table_iterator_next(struct xkb_compose_table_iterator *iter) +{ + /* + * This function takes the following recursive traversal function, + * and makes it non-recursive and resumable. The iter->cursors stack + * is analogous to the call stack, and cursor->direction to the + * instruction pointer of a stack frame. + * + * traverse(xkb_keysym_t *sequence, size_t sequence_length, uint16_t p) { + * if (!p) return + * // cursor->direction == NODE_LEFT + * node = &darray_item(table->nodes, p) + * traverse(sequence, sequence_length, node->lokid) + * // cursor->direction == NODE_DOWN + * sequence[sequence_length++] = node->keysym + * if (node->is_leaf) + * emit(sequence, sequence_length, node->leaf.keysym, table->utf[node->leaf.utf8]) + * else + * traverse(sequence, sequence_length, node->internal.eqkid) + * sequence_length-- + * // cursor->direction == NODE_RIGHT + * traverse(sequence, sequence_length, node->hikid) + * // cursor->direction == NODE_UP + * } + */ + + struct xkb_compose_table_iterator_cursor *cursor; + const struct compose_node *node; + + while (!darray_empty(iter->cursors)) { + cursor = &darray_item(iter->cursors, darray_size(iter->cursors) - 1); + node = &darray_item(iter->table->nodes, cursor->node_offset); + + switch (cursor->direction) { + case NODE_LEFT: + cursor->direction = NODE_DOWN; + if (node->lokid) { + struct xkb_compose_table_iterator_cursor new_cursor = {node->lokid, NODE_LEFT}; + darray_append(iter->cursors, new_cursor); + } + break; + + case NODE_DOWN: + cursor->direction = NODE_RIGHT; + assert (iter->entry.sequence_length <= MAX_LHS_LEN); + iter->entry.sequence[iter->entry.sequence_length] = node->keysym; + iter->entry.sequence_length++; + if (node->is_leaf) { + iter->entry.keysym = node->leaf.keysym; + iter->entry.utf8 = &darray_item(iter->table->utf8, node->leaf.utf8); + return &iter->entry; + } else { + struct xkb_compose_table_iterator_cursor new_cursor = {node->internal.eqkid, NODE_LEFT}; + darray_append(iter->cursors, new_cursor); + } + break; + + case NODE_RIGHT: + cursor->direction = NODE_UP; + iter->entry.sequence_length--; + if (node->hikid) { + struct xkb_compose_table_iterator_cursor new_cursor = {node->hikid, NODE_LEFT}; + darray_append(iter->cursors, new_cursor); + } + break; + + case NODE_UP: + darray_remove_last(iter->cursors); + break; + } } - xkb_keysym_t syms[MAX_LHS_LEN]; - for_each_helper(table, iter, data, syms, 0, 1); + + return NULL; } diff --git a/src/compose/table.h b/src/compose/table.h index eda042053..f6904a1c2 100644 --- a/src/compose/table.h +++ b/src/compose/table.h @@ -120,8 +120,8 @@ struct xkb_compose_table { }; struct xkb_compose_table_entry { - xkb_keysym_t *syms; - size_t nsyms; + xkb_keysym_t *sequence; + size_t sequence_length; xkb_keysym_t keysym; const char *utf8; }; diff --git a/src/darray.h b/src/darray.h index de659ccad..b75d85f98 100644 --- a/src/darray.h +++ b/src/darray.h @@ -114,6 +114,11 @@ typedef darray (unsigned long) darray_ulong; #define darray_concat(arr_to, arr_from) \ darray_append_items((arr_to), (arr_from).item, (arr_from).size) +/*** Removal ***/ + +/* Warning: Do not call darray_remove_last on an empty darray. */ +#define darray_remove_last(arr) (--(arr).size) + /*** String buffer ***/ #define darray_append_string(arr, str) do { \ diff --git a/test/compose.c b/test/compose.c index 1e85cbd47..5e7cba0f3 100644 --- a/test/compose.c +++ b/test/compose.c @@ -580,6 +580,110 @@ test_override(struct xkb_context *ctx) XKB_KEY_NoSymbol)); } +static bool +test_eq_entry_va(struct xkb_compose_table_entry *entry, xkb_keysym_t keysym_ref, const char *utf8_ref, va_list ap) +{ + assert (entry != NULL); + + assert (xkb_compose_table_entry_keysym(entry) == keysym_ref); + + const char *utf8 = xkb_compose_table_entry_utf8(entry); + assert (utf8 && utf8_ref && strcmp(utf8, utf8_ref) == 0); + + size_t nsyms; + const xkb_keysym_t *sequence = xkb_compose_table_entry_sequence(entry, &nsyms); + + xkb_keysym_t keysym; + for (unsigned k = 0; ; k++) { + keysym = va_arg(ap, xkb_keysym_t); + if (keysym == XKB_KEY_NoSymbol) { + return (k == nsyms - 1); + } + assert (k < nsyms); + assert (keysym == sequence[k]); + } +} + +static bool +test_eq_entry(struct xkb_compose_table_entry *entry, xkb_keysym_t keysym, const char *utf8, ...) +{ + va_list ap; + bool ok; + va_start(ap, utf8); + ok = test_eq_entry_va(entry, keysym, utf8, ap); + va_end(ap); + return ok; +} + +static void +test_traverse(struct xkb_context *ctx) +{ + struct xkb_compose_table *table; + + const char *buffer = " : \"foo\" X\n" + " : \"foobar\"\n" + " : oe\n" + " : \"bar\" Y\n" + " : \"æ\" ae\n" + " : \"baz\" Z\n" + " : \"é\" eacute\n" + " : \"aac\"\n" + " : \"aab\"\n" + " : \"aaa\"\n"; + + table = xkb_compose_table_new_from_buffer(ctx, buffer, strlen(buffer), "", + XKB_COMPOSE_FORMAT_TEXT_V1, + XKB_COMPOSE_COMPILE_NO_FLAGS); + assert(table); + + struct xkb_compose_table_iterator *iter = xkb_compose_table_iterator_new(table); + + test_eq_entry(xkb_compose_table_iterator_next(iter), + XKB_KEY_eacute, "é", + XKB_KEY_dead_acute, XKB_KEY_e, XKB_KEY_NoSymbol); + + test_eq_entry(xkb_compose_table_iterator_next(iter), + XKB_KEY_Z, "baz", + XKB_KEY_dead_circumflex, XKB_KEY_a, XKB_KEY_NoSymbol); + + test_eq_entry(xkb_compose_table_iterator_next(iter), + XKB_KEY_Y, "bar", + XKB_KEY_dead_circumflex, XKB_KEY_e, XKB_KEY_NoSymbol); + + test_eq_entry(xkb_compose_table_iterator_next(iter), + XKB_KEY_X, "foo", + XKB_KEY_dead_circumflex, XKB_KEY_dead_circumflex, XKB_KEY_NoSymbol); + + test_eq_entry(xkb_compose_table_iterator_next(iter), + XKB_KEY_NoSymbol, "aaa", + XKB_KEY_Multi_key, XKB_KEY_a, XKB_KEY_a, XKB_KEY_a, XKB_KEY_NoSymbol); + + test_eq_entry(xkb_compose_table_iterator_next(iter), + XKB_KEY_NoSymbol, "aab", + XKB_KEY_Multi_key, XKB_KEY_a, XKB_KEY_a, XKB_KEY_b, XKB_KEY_NoSymbol); + + test_eq_entry(xkb_compose_table_iterator_next(iter), + XKB_KEY_NoSymbol, "aac", + XKB_KEY_Multi_key, XKB_KEY_a, XKB_KEY_a, XKB_KEY_c, XKB_KEY_NoSymbol); + + test_eq_entry(xkb_compose_table_iterator_next(iter), + XKB_KEY_ae, "æ", + XKB_KEY_Multi_key, XKB_KEY_a, XKB_KEY_e, XKB_KEY_NoSymbol); + + test_eq_entry(xkb_compose_table_iterator_next(iter), + XKB_KEY_oe, "", + XKB_KEY_Multi_key, XKB_KEY_o, XKB_KEY_e, XKB_KEY_NoSymbol); + + test_eq_entry(xkb_compose_table_iterator_next(iter), + XKB_KEY_NoSymbol, "foobar", + XKB_KEY_Ahook, XKB_KEY_x, XKB_KEY_NoSymbol); + + assert (xkb_compose_table_iterator_next(iter) == NULL); + + xkb_compose_table_iterator_free(iter); + xkb_compose_table_unref(table); +} + int main(int argc, char *argv[]) { @@ -612,6 +716,7 @@ main(int argc, char *argv[]) test_modifier_syntax(ctx); test_include(ctx); test_override(ctx); + test_traverse(ctx); xkb_context_unref(ctx); return 0; diff --git a/tools/compose.c b/tools/compose.c index fd206b88d..3a77347b8 100644 --- a/tools/compose.c +++ b/tools/compose.c @@ -36,9 +36,10 @@ static void usage(FILE *fp, char *progname) { fprintf(fp, - "Usage: %s [--locale LOCALE | --locale-from-env | --locale-from-setlocale]\n", + "Usage: %s [--file FILE] [--locale LOCALE | --locale-from-env | --locale-from-setlocale]\n", progname); fprintf(fp, + " --file - specify a file to load\n" " --locale - specify the locale directly\n" " --locale-from-env - get the locale from the LC_ALL/LC_CTYPE/LANG environment variables (falling back to C)\n" " --locale-from-setlocale - get the locale using setlocale(3)\n" @@ -46,10 +47,10 @@ usage(FILE *fp, char *progname) } static void -compose_table_print_cb(struct xkb_compose_table_entry *entry, void *data) +print_compose_table_entry(struct xkb_compose_table_entry *entry) { size_t nsyms; - const xkb_keysym_t *syms = xkb_compose_table_entry_lhs(entry, &nsyms); + const xkb_keysym_t *syms = xkb_compose_table_entry_sequence(entry, &nsyms); char buf[128]; for (size_t i = 0; i < nsyms; i++) { xkb_keysym_get_name(syms[i], buf, sizeof(buf)); @@ -78,12 +79,16 @@ main(int argc, char *argv[]) struct xkb_context *ctx = NULL; struct xkb_compose_table *compose_table = NULL; const char *locale = NULL; + const char *path = NULL; + enum xkb_compose_format format = XKB_COMPOSE_FORMAT_TEXT_V1; enum options { + OPT_FILE, OPT_LOCALE, OPT_LOCALE_FROM_ENV, OPT_LOCALE_FROM_SETLOCALE, }; static struct option opts[] = { + {"file", required_argument, 0, OPT_FILE}, {"locale", required_argument, 0, OPT_LOCALE}, {"locale-from-env", no_argument, 0, OPT_LOCALE_FROM_ENV}, {"locale-from-setlocale", no_argument, 0, OPT_LOCALE_FROM_SETLOCALE}, @@ -101,6 +106,9 @@ main(int argc, char *argv[]) break; switch (opt) { + case OPT_FILE: + path = optarg; + break; case OPT_LOCALE: locale = optarg; break; @@ -135,18 +143,40 @@ main(int argc, char *argv[]) goto out; } - compose_table = - xkb_compose_table_new_from_locale(ctx, locale, - XKB_COMPOSE_COMPILE_NO_FLAGS); - if (!compose_table) { - fprintf(stderr, "Couldn't create compose from locale\n"); - goto out; + if (path != NULL) { + FILE *file = fopen(path, "rb"); + if (file == NULL) { + perror(path); + goto file_error; + } + compose_table = + xkb_compose_table_new_from_file(ctx, file, locale, format, + XKB_COMPOSE_COMPILE_NO_FLAGS); + fclose(file); + if (!compose_table) { + fprintf(stderr, "Couldn't create compose from file: %s\n", path); + goto out; + } + } else { + compose_table = + xkb_compose_table_new_from_locale(ctx, locale, + XKB_COMPOSE_COMPILE_NO_FLAGS); + if (!compose_table) { + fprintf(stderr, "Couldn't create compose from locale\n"); + goto out; + } } - xkb_compose_table_for_each(compose_table, compose_table_print_cb, NULL); + struct xkb_compose_table_iterator *iter = xkb_compose_table_iterator_new(compose_table); + struct xkb_compose_table_entry *entry; + while ((entry = xkb_compose_table_iterator_next(iter))) { + print_compose_table_entry(entry); + } + xkb_compose_table_iterator_free(iter); out: xkb_compose_table_unref(compose_table); +file_error: xkb_context_unref(ctx); return ret; diff --git a/xkbcommon.map b/xkbcommon.map index 63a426d23..b2507272e 100644 --- a/xkbcommon.map +++ b/xkbcommon.map @@ -110,10 +110,12 @@ global: xkb_keymap_key_get_mods_for_level; } V_0.8.0; -V_1.5.0 { +V_1.6.0 { global: - xkb_compose_table_entry_lhs; - xkb_compose_table_entry_keysym; - xkb_compose_table_entry_utf8; - xkb_compose_table_for_each; + xkb_compose_table_entry_sequence; + xkb_compose_table_entry_keysym; + xkb_compose_table_entry_utf8; + xkb_compose_table_iterator_new; + xkb_compose_table_iterator_free; + xkb_compose_table_iterator_next; } V_1.0.0;