summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bench/compose-traversal.c88
-rw-r--r--include/xkbcommon/xkbcommon-compose.h137
-rw-r--r--meson.build5
-rw-r--r--src/compose/parser.c3
-rw-r--r--src/compose/parser.h3
-rw-r--r--src/compose/table.c156
-rw-r--r--src/compose/table.h7
-rw-r--r--src/darray.h5
-rw-r--r--test/compose.c105
-rw-r--r--tools/compose.c73
-rw-r--r--xkbcommon.map10
11 files changed, 581 insertions, 11 deletions
diff --git a/bench/compose-traversal.c b/bench/compose-traversal.c
new file mode 100644
index 0000000..be24beb
--- /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 <time.h>
+
+#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 8b41b98..b28e4f8 100644
--- a/include/xkbcommon/xkbcommon-compose.h
+++ b/include/xkbcommon/xkbcommon-compose.h
@@ -299,6 +299,143 @@ xkb_compose_table_ref(struct xkb_compose_table *table);
void
xkb_compose_table_unref(struct xkb_compose_table *table);
+/**
+ * @struct xkb_compose_table_entry
+ * Opaque Compose table entry object.
+ *
+ * Represents a single entry in a Compose file in the iteration API.
+ * It is immutable.
+ *
+ * @sa xkb_compose_table_iterator_new
+ * @since 1.6.0
+ */
+struct xkb_compose_table_entry;
+
+/**
+ * Get the left-hand keysym sequence of a Compose table entry.
+ *
+ * For example, given the following entry:
+ *
+ * ```
+ * <dead_tilde> <space> : "~" 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 @p sequence_length out-parameter.
+ *
+ * @memberof xkb_compose_table_entry
+ * @since 1.6.0
+ */
+const xkb_keysym_t *
+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:
+ *
+ * ```
+ * <dead_tilde> <space> : "~" 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.6.0
+ */
+xkb_keysym_t
+xkb_compose_table_entry_keysym(struct xkb_compose_table_entry *entry);
+
+/**
+ * Get the right-hand result string of a Compose table entry.
+ *
+ * The string is UTF-8 encoded and NULL-terminated.
+ *
+ * For example, given the following entry:
+ *
+ * ```
+ * <dead_tilde> <space> : "~" 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.6.0
+ */
+const char *
+xkb_compose_table_entry_utf8(struct xkb_compose_table_entry *entry);
+
+/**
+ * @struct xkb_compose_table_iterator
+ * Iterator over a compose table’s entries.
+ *
+ * @sa xkb_compose_table_iterator_new()
+ * @since 1.6.0
+ */
+struct xkb_compose_table_iterator;
+
+/**
+ * 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.
+ *
+ * @attention The return value is valid until the next call to this function.
+ *
+ * Returns `NULL` in case there is no more entries.
+ *
+ * @memberof xkb_compose_table_iterator
+ * @since 1.6.0
+ */
+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 {
/** Do not apply any flags. */
diff --git a/meson.build b/meson.build
index ce17b1d..91d0b36 100644
--- a/meson.build
+++ b/meson.build
@@ -805,6 +805,11 @@ benchmark(
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),
env: bench_env,
diff --git a/src/compose/parser.c b/src/compose/parser.c
index 88105fa..e1b81de 100644
--- a/src/compose/parser.c
+++ b/src/compose/parser.c
@@ -63,9 +63,6 @@ OR PERFORMANCE OF THIS SOFTWARE.
#include "utf8.h"
#include "parser.h"
-#define MAX_LHS_LEN 10
-#define MAX_INCLUDE_DEPTH 5
-
/*
* Grammar adapted from libX11/modules/im/ximcp/imLcPrs.c.
* See also the XCompose(5) manpage.
diff --git a/src/compose/parser.h b/src/compose/parser.h
index 3f64a07..487f1a9 100644
--- a/src/compose/parser.h
+++ b/src/compose/parser.h
@@ -24,6 +24,9 @@
#ifndef COMPOSE_PARSER_H
#define COMPOSE_PARSER_H
+#define MAX_LHS_LEN 10
+#define MAX_INCLUDE_DEPTH 5
+
bool
parse_string(struct xkb_compose_table *table,
const char *string, size_t len,
diff --git a/src/compose/table.c b/src/compose/table.c
index 3abc649..04fa8cb 100644
--- a/src/compose/table.c
+++ b/src/compose/table.c
@@ -27,6 +27,7 @@
#include "table.h"
#include "parser.h"
#include "paths.h"
+#include "xkbcommon/xkbcommon.h"
static struct xkb_compose_table *
xkb_compose_table_new(struct xkb_context *ctx,
@@ -228,3 +229,158 @@ found_path:
free(path);
return table;
}
+
+XKB_EXPORT const xkb_keysym_t *
+xkb_compose_table_entry_sequence(struct xkb_compose_table_entry *entry,
+ size_t *sequence_length)
+{
+ *sequence_length = entry->sequence_length;
+ return entry->sequence;
+}
+
+XKB_EXPORT xkb_keysym_t
+xkb_compose_table_entry_keysym(struct xkb_compose_table_entry *entry)
+{
+ return entry->keysym;
+}
+
+XKB_EXPORT const char *
+xkb_compose_table_entry_utf8(struct xkb_compose_table_entry *entry)
+{
+ return entry->utf8;
+}
+
+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)
+{
+ 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;
+ }
+ iter->table = xkb_compose_table_ref(table);
+ sequence = calloc(MAX_LHS_LEN, sizeof(xkb_keysym_t));
+ if (!sequence) {
+ free(iter);
+ return NULL;
+ }
+ 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_iterator_free(struct xkb_compose_table_iterator *iter)
+{
+ 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;
+ }
+ }
+
+ return NULL;
+}
diff --git a/src/compose/table.h b/src/compose/table.h
index 825a8e2..f6904a1 100644
--- a/src/compose/table.h
+++ b/src/compose/table.h
@@ -119,4 +119,11 @@ struct xkb_compose_table {
darray(struct compose_node) nodes;
};
+struct xkb_compose_table_entry {
+ xkb_keysym_t *sequence;
+ size_t sequence_length;
+ xkb_keysym_t keysym;
+ const char *utf8;
+};
+
#endif
diff --git a/src/darray.h b/src/darray.h
index de659cc..b75d85f 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 1e85cbd..5e7cba0 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 = "<dead_circumflex> <dead_circumflex> : \"foo\" X\n"
+ "<Ahook> <x> : \"foobar\"\n"
+ "<Multi_key> <o> <e> : oe\n"
+ "<dead_circumflex> <e> : \"bar\" Y\n"
+ "<Multi_key> <a> <e> : \"æ\" ae\n"
+ "<dead_circumflex> <a> : \"baz\" Z\n"
+ "<dead_acute> <e> : \"é\" eacute\n"
+ "<Multi_key> <a> <a> <c>: \"aac\"\n"
+ "<Multi_key> <a> <a> <b>: \"aab\"\n"
+ "<Multi_key> <a> <a> <a>: \"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 2b3ba64..3a77347 100644
--- a/tools/compose.c
+++ b/tools/compose.c
@@ -29,21 +29,49 @@
#include <stdlib.h>
#include "xkbcommon/xkbcommon.h"
+#include "xkbcommon/xkbcommon-keysyms.h"
#include "xkbcommon/xkbcommon-compose.h"
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"
);
}
+static void
+print_compose_table_entry(struct xkb_compose_table_entry *entry)
+{
+ size_t 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));
+ printf("<%s>", buf);
+ if (i + 1 < nsyms) {
+ printf(" ");
+ }
+ }
+ printf(":");
+ const char *utf8 = xkb_compose_table_entry_utf8(entry);
+ if (*utf8 != '\0') {
+ printf(" \"%s\"", utf8);
+ }
+ const xkb_keysym_t keysym = xkb_compose_table_entry_keysym(entry);
+ if (keysym != XKB_KEY_NoSymbol) {
+ xkb_keysym_get_name(keysym, buf, sizeof(buf));
+ printf(" %s", buf);
+ }
+ printf("\n");
+}
+
int
main(int argc, char *argv[])
{
@@ -51,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},
@@ -74,6 +106,9 @@ main(int argc, char *argv[])
break;
switch (opt) {
+ case OPT_FILE:
+ path = optarg;
+ break;
case OPT_LOCALE:
locale = optarg;
break;
@@ -108,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;
+ }
}
- printf("Compiled successfully from locale %s\n", locale);
+ 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 59ef971..b250727 100644
--- a/xkbcommon.map
+++ b/xkbcommon.map
@@ -109,3 +109,13 @@ global:
xkb_utf32_to_keysym;
xkb_keymap_key_get_mods_for_level;
} V_0.8.0;
+
+V_1.6.0 {
+global:
+ 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;