Compose: add iterator API
Allow users to iterate the entries in a compose table. This is useful for other projects which want programmable access to the sequences, without having to write their own parser. - New API: - `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`. - Add tests in `test/compose.c`. - Add benchmark for compose traversal. - `tools/compose.c`: - Print entries instead of just validating them. - Add `--file` option. - TODO: make this tool part of the xkbcli commands. Co-authored-by: Pierre Le Marre <dev@wismill.eu> Co-authored-by: Ran Benita <ran@unusedvar.com> Signed-off-by: Ran Benita <ran@unusedvar.com>master
parent
c0065c95a4
commit
a17701327e
|
@ -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;
|
||||
}
|
|
@ -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. */
|
||||
|
|
|
@ -804,6 +804,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),
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 { \
|
||||
|
|
105
test/compose.c
105
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;
|
||||
|
|
|
@ -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,6 +143,21 @@ main(int argc, char *argv[])
|
|||
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);
|
||||
|
@ -115,11 +165,18 @@ main(int argc, char *argv[])
|
|||
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;
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue