diff --git a/include/xkbcommon/xkbcommon-compose.h b/include/xkbcommon/xkbcommon-compose.h
index b28e4f843..100c83f28 100644
--- a/include/xkbcommon/xkbcommon-compose.h
+++ b/include/xkbcommon/xkbcommon-compose.h
@@ -83,14 +83,23 @@ extern "C" {
* @page compose-conflicting Conflicting Sequences
* @parblock
*
- * To avoid ambiguity, a sequence is not allowed to be a prefix of another.
+ * Sequences of length 1 are allowed.
+ *
+ * Without `XKB_COMPOSE_COMPILE_OVERLAPPING_SEQUENCES`
+ *
+ * To avoid ambiguity, a sequence is *not* allowed to be a prefix of another.
* In such a case, the conflict is resolved thus:
*
* 1. A longer sequence overrides a shorter one.
* 2. An equal sequence overrides an existing one.
* 3. A shorter sequence does not override a longer one.
*
- * Sequences of length 1 are allowed.
+ * With `XKB_COMPOSE_COMPILE_OVERLAPPING_SEQUENCES`
+ *
+ * Overlapping sequences of different lengths are *allowed* to co-exist.
+ * Conflicts are resolved with the following rule:
+ *
+ * 1. An equal sequence overrides an existing one.
*
* @endparblock
*/
@@ -141,7 +150,11 @@ struct xkb_compose_state;
/** Flags affecting Compose file compilation. */
enum xkb_compose_compile_flags {
/** Do not apply any flags. */
- XKB_COMPOSE_COMPILE_NO_FLAGS = 0
+ XKB_COMPOSE_COMPILE_NO_FLAGS = 0,
+ /** Allow overlapping sequences
+ * @since 1.7.0
+ */
+ XKB_COMPOSE_COMPILE_OVERLAPPING_SEQUENCES
};
/** The recognized Compose file formats. */
@@ -450,7 +463,7 @@ enum xkb_compose_state_flags {
* @param flags
* Optional flags for the compose state, or 0.
*
- * @returns A new compose state, or NULL on failure.
+ * @returns A new compose state, or `NULL` on failure.
*
* @memberof xkb_compose_state
*/
@@ -471,7 +484,7 @@ xkb_compose_state_ref(struct xkb_compose_state *state);
/**
* Release a reference on a compose state object, and possibly free it.
*
- * @param state The object. If NULL, do nothing.
+ * @param state The object. If `NULL`, do nothing.
*
* @memberof xkb_compose_state
*/
@@ -502,7 +515,17 @@ enum xkb_compose_status {
/** A complete sequence has been matched. */
XKB_COMPOSE_COMPOSED,
/** The last sequence was cancelled due to an unmatched keysym. */
- XKB_COMPOSE_CANCELLED
+ XKB_COMPOSE_CANCELLED,
+ /** A complete sequence has been matched, but a longer sequence also exists.
+ *
+ * @since 1.7.0
+ */
+ XKB_COMPOSE_CANDIDATE,
+ /** The last sequence was accepted due to an unmatched keysym.
+ *
+ * @since 1.7.0
+ */
+ XKB_COMPOSE_CANDIDATE_ACCEPTED
};
/** The effect of a keysym fed to xkb_compose_state_feed(). */
@@ -524,27 +547,45 @@ enum xkb_compose_feed_result {
* have no effect on the status or otherwise.
*
* The following is a description of the possible status transitions, in
- * the format CURRENT STATUS => NEXT STATUS, given a non-ignored input
- * keysym `keysym`:
+ * the format `CURRENT STATUS` => `NEXT STATUS`, given a non-ignored
+ * input keysym `keysym`:
*
@verbatim
- NOTHING or CANCELLED or COMPOSED =>
+ NOTHING or CANCELLED or COMPOSED or CANDIDATE_ACCEPTED =>
NOTHING if keysym does not start a sequence.
COMPOSING if keysym starts a sequence.
+ CANDIDATE if keysym starts and terminates a single-keysym sequence,
+ but a longer sequence also exists.
COMPOSED if keysym starts and terminates a single-keysym sequence.
COMPOSING =>
COMPOSING if keysym advances any of the currently possible
sequences but does not terminate any of them.
+ CANDIDATE if keysym terminates one of the currently possible
+ sequences, but a longer sequence also exists.
COMPOSED if keysym terminates one of the currently possible
sequences.
CANCELLED if keysym does not advance any of the currently
possible sequences.
+
+ CANDIDATE =>
+ COMPOSING if keysym advances any of the currently possible
+ sequences but does not terminate any of them.
+ CANDIDATE if keysym terminates one of the currently possible
+ sequences, but a longer sequence also exists.
+ COMPOSED if keysym terminates one of the currently possible
+ sequences.
+ CANDIDATE_ACCEPTED
+ if keysym does not advance any of the currently
+ possible sequences, but a candidate was proposed previously.
@endverbatim
*
+ * @note `CANDIDATE` and `CANDIDATE_ACCEPTED` are only possible when compiling
+ * using `XKB_COMPOSE_COMPILE_OVERLAPPING_SEQUENCES`.
+ *
* The current Compose formats do not support multiple-keysyms.
* Therefore, if you are using a function such as xkb_state_key_get_syms()
- * and it returns more than one keysym, consider feeding XKB_KEY_NoSymbol
+ * and it returns more than one keysym, consider feeding `XKB_KEY_NoSymbol`
* instead.
*
* @param state
@@ -565,7 +606,7 @@ xkb_compose_state_feed(struct xkb_compose_state *state,
/**
* Reset the Compose sequence state machine.
*
- * The status is set to XKB_COMPOSE_NOTHING, and the current sequence
+ * The status is set to `XKB_COMPOSE_NOTHING`, and the current sequence
* is discarded.
*
* @memberof xkb_compose_state
@@ -586,7 +627,7 @@ xkb_compose_state_get_status(struct xkb_compose_state *state);
* Get the result Unicode/UTF-8 string for a composed sequence.
*
* See @ref compose-overview for more details. This function is only
- * useful when the status is XKB_COMPOSE_COMPOSED.
+ * useful when the status is `XKB_COMPOSE_COMPOSED` or `XKB_COMPOSE_CANDIDATE`.
*
* @param[in] state
* The compose state.
@@ -618,10 +659,10 @@ xkb_compose_state_get_utf8(struct xkb_compose_state *state,
* Get the result keysym for a composed sequence.
*
* See @ref compose-overview for more details. This function is only
- * useful when the status is XKB_COMPOSE_COMPOSED.
+ * useful when the status is `XKB_COMPOSE_COMPOSED` or `XKB_COMPOSE_CANDIDATE`.
*
* @returns The result keysym. If the sequence is not complete, or does
- * not specify a result keysym, returns XKB_KEY_NoSymbol.
+ * not specify a result keysym, returns `XKB_KEY_NoSymbol`.
*
* @memberof xkb_compose_state
**/
diff --git a/src/compose/parser.c b/src/compose/parser.c
index ac11446a3..a7742aa96 100644
--- a/src/compose/parser.c
+++ b/src/compose/parser.c
@@ -346,13 +346,23 @@ add_production(struct xkb_compose_table *table, struct scanner *s,
uint32_t curr = darray_size(table->nodes) == 1 ? 0 : 1;
uint32_t *pptr = NULL;
struct compose_node *node = NULL;
+ bool allow_overlapping;
- /* Warn before potentially going over the limit, discard silently after. */
- if (darray_size(table->nodes) + production->len + MAX_LHS_LEN > MAX_COMPOSE_NODES)
+ // TODO: adapt limit if overlapping is disallowed?
+ /*
+ * Warn before potentially going over the limit, discard silently after.
+ *
+ * We may add up to production->len * 2 - 1 nodes:
+ * • one node per keysym in the sequence
+ * • plus one node per keysym for overlap, except for the last node.
+ */
+ if (darray_size(table->nodes) + production->len * 2 - 1 + MAX_LHS_LEN > MAX_COMPOSE_NODES)
scanner_warn(s, "too many sequences for one Compose file; will ignore further lines");
- if (darray_size(table->nodes) + production->len >= MAX_COMPOSE_NODES)
+ if (darray_size(table->nodes) + production->len * 2 - 1 >= MAX_COMPOSE_NODES)
return;
+ allow_overlapping = !!(table->flags & XKB_COMPOSE_COMPILE_OVERLAPPING_SEQUENCES);
+
/*
* Insert the sequence to the ternary search tree, creating new nodes as
* needed.
@@ -375,8 +385,9 @@ add_production(struct xkb_compose_table *table, struct scanner *s,
.lokid = 0,
.hikid = 0,
.internal = {
- .eqkid = 0,
+ .resid = 0,
.is_leaf = false,
+ .eqkid = 0,
},
};
curr = darray_size(table->nodes);
@@ -396,28 +407,86 @@ add_production(struct xkb_compose_table *table, struct scanner *s,
pptr = &node->hikid;
curr = node->hikid;
} else if (!last) {
+ /* Adding intermediate node */
if (node->is_leaf) {
- scanner_warn(s, "a sequence already exists which is a prefix of this sequence; overriding");
- node->internal.eqkid = 0;
+ /* Existing leaf */
+ if (allow_overlapping) {
+ /* Backup overlapping sequence result */
+ struct compose_node overlapping = {
+ .keysym = node->keysym,
+ .lokid = 0,
+ .hikid = 0,
+ .leaf = node->leaf
+ };
+ darray_append(table->nodes, overlapping);
+ node = &darray_item(table->nodes, curr);
+ node->internal.resid = darray_size(table->nodes) - 1;
+ } else {
+ scanner_warn(s, "a sequence already exists which is a prefix of this sequence; overriding");
+ node->internal.resid = 0;
+ }
+ /* Reset node */
node->internal.is_leaf = false;
+ node->internal.eqkid = 0;
}
lhs_pos++;
pptr = &node->internal.eqkid;
curr = node->internal.eqkid;
} else {
+ /* Adding the last node of the sequence and the result */
+ struct compose_node *result = NULL;
+ bool has_previous_leaf;
if (node->is_leaf) {
+ /* Existing leaf */
+ has_previous_leaf = true;
+ result = node;
+ } else if (node->internal.eqkid != 0) {
+ /* Existing non-leaf */
+ if (!allow_overlapping) {
+ scanner_warn(s, "this compose sequence is a prefix of another; skipping line");
+ return;
+ } else if (node->internal.resid) {
+ /* Reuse existing overlapping sequence result */
+ result = &darray_item(table->nodes, node->internal.resid);
+ has_previous_leaf = true;
+ } else {
+ /* Create a new overlapping sequence result */
+ node->internal.resid = darray_size(table->nodes);
+ struct compose_node overlapping = {
+ .keysym = node->keysym,
+ .lokid = 0,
+ .hikid = 0,
+ .leaf = {
+ .utf8 = 0,
+ .is_leaf = true,
+ .keysym = XKB_KEY_NoSymbol
+ }
+ };
+ darray_append(table->nodes, overlapping);
+ node = &darray_item(table->nodes, curr);
+ result = &darray_item(table->nodes,
+ node->internal.resid);
+ has_previous_leaf = false;
+ }
+ } else {
+ /* New leaf */
+ has_previous_leaf = false;
+ node->is_leaf = true;
+ result = node;
+ }
+ if (has_previous_leaf) {
bool same_string =
- (node->leaf.utf8 == 0 && !production->has_string) ||
+ (result->leaf.utf8 == 0 && !production->has_string) ||
(
- node->leaf.utf8 != 0 && production->has_string &&
- streq(&darray_item(table->utf8, node->leaf.utf8),
+ result->leaf.utf8 != 0 && production->has_string &&
+ streq(&darray_item(table->utf8, result->leaf.utf8),
production->string)
);
bool same_keysym =
- (node->leaf.keysym == XKB_KEY_NoSymbol && !production->has_keysym) ||
+ (result->leaf.keysym == XKB_KEY_NoSymbol && !production->has_keysym) ||
(
- node->leaf.keysym != XKB_KEY_NoSymbol && production->has_keysym &&
- node->leaf.keysym == production->keysym
+ result->leaf.keysym != XKB_KEY_NoSymbol && production->has_keysym &&
+ result->leaf.keysym == production->keysym
);
if (same_string && same_keysym) {
scanner_warn(s, "this compose sequence is a duplicate of another; skipping line");
@@ -425,18 +494,15 @@ add_production(struct xkb_compose_table *table, struct scanner *s,
} else {
scanner_warn(s, "this compose sequence already exists; overriding");
}
- } else if (node->internal.eqkid != 0) {
- scanner_warn(s, "this compose sequence is a prefix of another; skipping line");
- return;
}
- node->is_leaf = true;
+ result->is_leaf = true;
if (production->has_string) {
- node->leaf.utf8 = darray_size(table->utf8);
+ result->leaf.utf8 = darray_size(table->utf8);
darray_append_items(table->utf8, production->string,
strlen(production->string) + 1);
}
if (production->has_keysym) {
- node->leaf.keysym = production->keysym;
+ result->leaf.keysym = production->keysym;
}
return;
}
diff --git a/src/compose/state.c b/src/compose/state.c
index 7a788914e..f8051152e 100644
--- a/src/compose/state.c
+++ b/src/compose/state.c
@@ -143,14 +143,18 @@ xkb_compose_state_get_status(struct xkb_compose_state *state)
prev_node = &darray_item(state->table->nodes, state->prev_context);
node = &darray_item(state->table->nodes, state->context);
- if (state->context == 0 && !prev_node->is_leaf)
- return XKB_COMPOSE_CANCELLED;
-
- if (state->context == 0)
+ if (state->context == 0) {
+ if (!prev_node->is_leaf)
+ return prev_node->internal.resid
+ ? XKB_COMPOSE_CANDIDATE_ACCEPTED
+ : XKB_COMPOSE_CANCELLED;
return XKB_COMPOSE_NOTHING;
+ }
if (!node->is_leaf)
- return XKB_COMPOSE_COMPOSING;
+ return node->internal.resid
+ ? XKB_COMPOSE_CANDIDATE
+ : XKB_COMPOSE_COMPOSING;
return XKB_COMPOSE_COMPOSED;
}
@@ -162,8 +166,14 @@ xkb_compose_state_get_utf8(struct xkb_compose_state *state,
const struct compose_node *node =
&darray_item(state->table->nodes, state->context);
- if (!node->is_leaf)
- goto fail;
+ if (!node->is_leaf) {
+ if (node->internal.resid) {
+ node = &darray_item(state->table->nodes,
+ node->internal.resid);
+ } else {
+ goto fail;
+ }
+ }
/* If there's no string specified, but only a keysym, try to do the
* most helpful thing. */
@@ -195,7 +205,12 @@ xkb_compose_state_get_one_sym(struct xkb_compose_state *state)
{
const struct compose_node *node =
&darray_item(state->table->nodes, state->context);
- if (!node->is_leaf)
+ if (node->is_leaf) {
+ return node->leaf.keysym;
+ } else if (node->internal.resid) {
+ return darray_item(state->table->nodes,
+ node->internal.resid).leaf.keysym;
+ } else {
return XKB_KEY_NoSymbol;
- return node->leaf.keysym;
+ }
}
diff --git a/src/compose/table.c b/src/compose/table.c
index 04fa8cb74..522858570 100644
--- a/src/compose/table.c
+++ b/src/compose/table.c
@@ -60,8 +60,8 @@ xkb_compose_table_new(struct xkb_context *ctx,
darray_init(table->utf8);
dummy.keysym = XKB_KEY_NoSymbol;
- dummy.leaf.is_leaf = true;
dummy.leaf.utf8 = 0;
+ dummy.leaf.is_leaf = true;
dummy.leaf.keysym = XKB_KEY_NoSymbol;
darray_append(table->nodes, dummy);
@@ -89,6 +89,8 @@ xkb_compose_table_unref(struct xkb_compose_table *table)
free(table);
}
+#define XKB_COMPOSE_COMPILE_MASK XKB_COMPOSE_COMPILE_OVERLAPPING_SEQUENCES
+
XKB_EXPORT struct xkb_compose_table *
xkb_compose_table_new_from_file(struct xkb_context *ctx,
FILE *file,
@@ -99,7 +101,7 @@ xkb_compose_table_new_from_file(struct xkb_context *ctx,
struct xkb_compose_table *table;
bool ok;
- if (flags & ~(XKB_COMPOSE_COMPILE_NO_FLAGS)) {
+ if (flags & ~(XKB_COMPOSE_COMPILE_MASK)) {
log_err_func(ctx, "unrecognized flags: %#x\n", flags);
return NULL;
}
@@ -132,7 +134,7 @@ xkb_compose_table_new_from_buffer(struct xkb_context *ctx,
struct xkb_compose_table *table;
bool ok;
- if (flags & ~(XKB_COMPOSE_COMPILE_NO_FLAGS)) {
+ if (flags & ~(XKB_COMPOSE_COMPILE_MASK)) {
log_err_func(ctx, "unrecognized flags: %#x\n", flags);
return NULL;
}
@@ -165,7 +167,7 @@ xkb_compose_table_new_from_locale(struct xkb_context *ctx,
FILE *file;
bool ok;
- if (flags & ~(XKB_COMPOSE_COMPILE_NO_FLAGS)) {
+ if (flags & ~(XKB_COMPOSE_COMPILE_MASK)) {
log_err_func(ctx, "unrecognized flags: %#x\n", flags);
return NULL;
}
@@ -253,13 +255,18 @@ xkb_compose_table_entry_utf8(struct xkb_compose_table_entry *entry)
enum node_direction {
NODE_LEFT = 0,
NODE_DOWN,
+ NODE_DOWN_NON_OVERLAPPING,
NODE_RIGHT,
NODE_UP
};
+#define NODE_OFFSET_BIT_FIELD_SIZE 29
+_Static_assert(MAX_COMPOSE_NODES_LOG2 + 1 <= NODE_OFFSET_BIT_FIELD_SIZE,
+ "xkb_compose_table_iterator_cursor::node_offset cannot hold MAX_COMPOSE_NODES");
+
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
+ uint32_t node_offset:NODE_OFFSET_BIT_FIELD_SIZE;
+ uint8_t direction:3; /* enum node_direction: current direction
* traversing the tree */
};
@@ -347,26 +354,45 @@ xkb_compose_table_iterator_next(struct xkb_compose_table_iterator *iter)
case NODE_LEFT:
cursor->direction = NODE_DOWN;
if (node->lokid) {
- struct xkb_compose_table_iterator_cursor new_cursor = {node->lokid, NODE_LEFT};
+ struct xkb_compose_table_iterator_cursor new_cursor = {
+ .node_offset = node->lokid,
+ .direction = 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) {
+ cursor->direction = NODE_RIGHT;
+ iter->entry.keysym = node->leaf.keysym;
+ iter->entry.utf8 = &darray_item(iter->table->utf8, node->leaf.utf8);
+ return &iter->entry;
+ } else if (node->internal.resid) {
+ cursor->direction = NODE_DOWN_NON_OVERLAPPING;
+ node = &darray_item(iter->table->nodes, node->internal.resid);
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};
+down:
+ cursor->direction = NODE_RIGHT;
+ struct xkb_compose_table_iterator_cursor new_cursor = {
+ .node_offset = node->internal.eqkid,
+ .direction = NODE_LEFT
+ };
darray_append(iter->cursors, new_cursor);
}
break;
+ case NODE_DOWN_NON_OVERLAPPING:
+ assert (iter->entry.sequence_length <= MAX_LHS_LEN);
+ iter->entry.sequence[iter->entry.sequence_length] = node->keysym;
+ goto down;
+
case NODE_RIGHT:
cursor->direction = NODE_UP;
iter->entry.sequence_length--;
diff --git a/src/compose/table.h b/src/compose/table.h
index f6904a1c2..72ba88da0 100644
--- a/src/compose/table.h
+++ b/src/compose/table.h
@@ -77,23 +77,26 @@
/* 7 nodes for every potential Unicode character and then some should be
* enough for all purposes. */
-#define MAX_COMPOSE_NODES (1 << 23)
+#define MAX_COMPOSE_NODES_LOG2 23
+#define MAX_COMPOSE_NODES (1 << MAX_COMPOSE_NODES_LOG2)
struct compose_node {
xkb_keysym_t keysym;
- /* Offset into xkb_compose_table::nodes or 0. */
+ /* Low sibling keysym. Offset into xkb_compose_table::nodes or 0. */
uint32_t lokid;
- /* Offset into xkb_compose_table::nodes or 0. */
+ /* High sibling keysym. Offset into xkb_compose_table::nodes or 0. */
uint32_t hikid;
union {
struct {
- uint32_t _pad:31;
+ uint32_t _pad1:31;
bool is_leaf:1;
+ uint32_t _pad2;
};
struct {
- uint32_t _pad:31;
+ /* Overlapping result. Offset into xkb_compose_table::nodes or 0. */
+ uint32_t resid:31;
bool is_leaf:1;
/* Offset into xkb_compose_table::nodes or 0. */
uint32_t eqkid;
diff --git a/test/compose.c b/test/compose.c
index 56bd889c0..9fd2094c2 100644
--- a/test/compose.c
+++ b/test/compose.c
@@ -35,6 +35,10 @@ compose_status_string(enum xkb_compose_status status)
return "nothing";
case XKB_COMPOSE_COMPOSING:
return "composing";
+ case XKB_COMPOSE_CANDIDATE:
+ return "candidate";
+ case XKB_COMPOSE_CANDIDATE_ACCEPTED:
+ return "candidate accepted";
case XKB_COMPOSE_COMPOSED:
return "composed";
case XKB_COMPOSE_CANCELLED:
@@ -156,14 +160,15 @@ test_compose_seq(struct xkb_compose_table *table, ...)
}
static bool
-test_compose_seq_buffer(struct xkb_context *ctx, const char *buffer, ...)
+test_compose_seq_buffer(struct xkb_context *ctx,
+ enum xkb_compose_compile_flags flags,
+ const char *buffer, ...)
{
va_list ap;
bool ok;
struct xkb_compose_table *table;
table = xkb_compose_table_new_from_buffer(ctx, buffer, strlen(buffer), "",
- XKB_COMPOSE_FORMAT_TEXT_V1,
- XKB_COMPOSE_COMPILE_NO_FLAGS);
+ XKB_COMPOSE_FORMAT_TEXT_V1, flags);
assert(table);
va_start(ap, buffer);
ok = test_compose_seq_va(table, ap);
@@ -176,7 +181,7 @@ static void
test_compose_utf8_bom(struct xkb_context *ctx)
{
const char buffer[] = "\xef\xbb\xbf : X";
- assert(test_compose_seq_buffer(ctx, buffer,
+ assert(test_compose_seq_buffer(ctx, XKB_COMPOSE_COMPILE_NO_FLAGS, buffer,
XKB_KEY_A, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSED, "X", XKB_KEY_X,
XKB_KEY_NoSymbol));
}
@@ -188,7 +193,7 @@ test_invalid_encodings(struct xkb_context *ctx)
/* ISO 8859-1 (latin1) */
const char iso_8859_1[] = " : \"\xe1\" acute";
- assert(!test_compose_seq_buffer(ctx, iso_8859_1,
+ assert(!test_compose_seq_buffer(ctx, XKB_COMPOSE_COMPILE_NO_FLAGS, iso_8859_1,
XKB_KEY_A, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSED, "\xc3\xa1", XKB_KEY_acute,
XKB_KEY_NoSymbol));
@@ -328,7 +333,7 @@ test_seqs(struct xkb_context *ctx)
xkb_compose_table_unref(table);
/* Make sure one-keysym sequences work. */
- assert(test_compose_seq_buffer(ctx,
+ assert(test_compose_seq_buffer(ctx, XKB_COMPOSE_COMPILE_NO_FLAGS,
" : \"foo\" X \n"
" : \"baz\" Y \n",
XKB_KEY_A, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSED, "foo", XKB_KEY_X,
@@ -339,7 +344,7 @@ test_seqs(struct xkb_context *ctx)
XKB_KEY_NoSymbol));
/* No sequences at all. */
- assert(test_compose_seq_buffer(ctx,
+ assert(test_compose_seq_buffer(ctx, XKB_COMPOSE_COMPILE_NO_FLAGS,
"",
XKB_KEY_A, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_NOTHING, "", XKB_KEY_NoSymbol,
XKB_KEY_B, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_NOTHING, "", XKB_KEY_NoSymbol,
@@ -349,7 +354,7 @@ test_seqs(struct xkb_context *ctx)
XKB_KEY_NoSymbol));
/* Only keysym - string derived from keysym. */
- assert(test_compose_seq_buffer(ctx,
+ assert(test_compose_seq_buffer(ctx, XKB_COMPOSE_COMPILE_NO_FLAGS,
" : X \n"
" : dollar \n"
" : dead_acute \n",
@@ -361,7 +366,7 @@ test_seqs(struct xkb_context *ctx)
XKB_KEY_NoSymbol));
/* Make sure a cancelling keysym doesn't start a new sequence. */
- assert(test_compose_seq_buffer(ctx,
+ assert(test_compose_seq_buffer(ctx, XKB_COMPOSE_COMPILE_NO_FLAGS,
" : X \n"
" : Y \n",
XKB_KEY_A, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSING, "", XKB_KEY_NoSymbol,
@@ -375,10 +380,10 @@ test_seqs(struct xkb_context *ctx)
}
static void
-test_conflicting(struct xkb_context *ctx)
+test_conflicting_without_overlapping(struct xkb_context *ctx)
{
// new is prefix of old
- assert(test_compose_seq_buffer(ctx,
+ assert(test_compose_seq_buffer(ctx, XKB_COMPOSE_COMPILE_NO_FLAGS,
" : \"foo\" A \n"
" : \"bar\" B \n",
XKB_KEY_A, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSING, "", XKB_KEY_NoSymbol,
@@ -387,7 +392,7 @@ test_conflicting(struct xkb_context *ctx)
XKB_KEY_NoSymbol));
// old is a prefix of new
- assert(test_compose_seq_buffer(ctx,
+ assert(test_compose_seq_buffer(ctx, XKB_COMPOSE_COMPILE_NO_FLAGS,
" : \"bar\" B \n"
" : \"foo\" A \n",
XKB_KEY_A, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSING, "", XKB_KEY_NoSymbol,
@@ -396,7 +401,77 @@ test_conflicting(struct xkb_context *ctx)
XKB_KEY_NoSymbol));
// new duplicate of old
- assert(test_compose_seq_buffer(ctx,
+ assert(test_compose_seq_buffer(ctx, XKB_COMPOSE_COMPILE_NO_FLAGS,
+ " : \"bar\" B \n"
+ " : \"bar\" B \n",
+ XKB_KEY_A, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSING, "", XKB_KEY_NoSymbol,
+ XKB_KEY_B, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSED, "bar", XKB_KEY_B,
+ XKB_KEY_C, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_NOTHING, "", XKB_KEY_NoSymbol,
+ XKB_KEY_NoSymbol));
+
+ // new same length as old #1
+ assert(test_compose_seq_buffer(ctx, XKB_COMPOSE_COMPILE_NO_FLAGS,
+ " : \"foo\" A \n"
+ " : \"bar\" B \n",
+ XKB_KEY_A, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSING, "", XKB_KEY_NoSymbol,
+ XKB_KEY_B, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSED, "bar", XKB_KEY_B,
+ XKB_KEY_NoSymbol));
+
+ // new same length as old #2
+ assert(test_compose_seq_buffer(ctx, XKB_COMPOSE_COMPILE_NO_FLAGS,
+ " : \"foo\" A \n"
+ " : \"foo\" B \n",
+ XKB_KEY_A, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSING, "", XKB_KEY_NoSymbol,
+ XKB_KEY_B, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSED, "foo", XKB_KEY_B,
+ XKB_KEY_NoSymbol));
+
+ // new same length as old #3
+ assert(test_compose_seq_buffer(ctx, XKB_COMPOSE_COMPILE_NO_FLAGS,
+ " : \"foo\" A \n"
+ " : \"bar\" A \n",
+ XKB_KEY_A, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSING, "", XKB_KEY_NoSymbol,
+ XKB_KEY_B, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSED, "bar", XKB_KEY_A,
+ XKB_KEY_NoSymbol));
+
+ // overlapping tests
+ assert(test_compose_seq_buffer(ctx, XKB_COMPOSE_COMPILE_NO_FLAGS,
+ " : \"1\" X \n"
+ " : \"foo\" A \n"
+ " : \"bar\" B \n"
+ " : \"2\" Y \n"
+ " : \"baz\" C \n"
+ " : \"foo\" D \n",
+ XKB_KEY_A, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSING, "", XKB_KEY_NoSymbol,
+ XKB_KEY_B, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSING, "", XKB_KEY_NoSymbol,
+ XKB_KEY_C, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSING, "", XKB_KEY_NoSymbol,
+ XKB_KEY_D, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSING, "", XKB_KEY_NoSymbol,
+ XKB_KEY_E, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSED, "foo", XKB_KEY_D,
+ XKB_KEY_NoSymbol));
+}
+
+static void
+test_conflicting_with_overlapping(struct xkb_context *ctx)
+{
+ // new is prefix of old
+ assert(test_compose_seq_buffer(ctx, XKB_COMPOSE_COMPILE_OVERLAPPING_SEQUENCES,
+ " : \"foo\" A \n"
+ " : \"bar\" B \n",
+ XKB_KEY_A, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSING, "", XKB_KEY_NoSymbol,
+ XKB_KEY_B, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_CANDIDATE, "bar", XKB_KEY_B,
+ XKB_KEY_C, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSED, "foo", XKB_KEY_A,
+ XKB_KEY_NoSymbol));
+
+ // old is a prefix of new
+ assert(test_compose_seq_buffer(ctx, XKB_COMPOSE_COMPILE_OVERLAPPING_SEQUENCES,
+ " : \"bar\" B \n"
+ " : \"foo\" A \n",
+ XKB_KEY_A, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSING, "", XKB_KEY_NoSymbol,
+ XKB_KEY_B, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_CANDIDATE, "bar", XKB_KEY_B,
+ XKB_KEY_C, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSED, "foo", XKB_KEY_A,
+ XKB_KEY_NoSymbol));
+
+ // new duplicate of old
+ assert(test_compose_seq_buffer(ctx, XKB_COMPOSE_COMPILE_OVERLAPPING_SEQUENCES,
" : \"bar\" B \n"
" : \"bar\" B \n",
XKB_KEY_A, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSING, "", XKB_KEY_NoSymbol,
@@ -405,7 +480,7 @@ test_conflicting(struct xkb_context *ctx)
XKB_KEY_NoSymbol));
// new same length as old #1
- assert(test_compose_seq_buffer(ctx,
+ assert(test_compose_seq_buffer(ctx, XKB_COMPOSE_COMPILE_OVERLAPPING_SEQUENCES,
" : \"foo\" A \n"
" : \"bar\" B \n",
XKB_KEY_A, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSING, "", XKB_KEY_NoSymbol,
@@ -413,7 +488,7 @@ test_conflicting(struct xkb_context *ctx)
XKB_KEY_NoSymbol));
// new same length as old #2
- assert(test_compose_seq_buffer(ctx,
+ assert(test_compose_seq_buffer(ctx, XKB_COMPOSE_COMPILE_OVERLAPPING_SEQUENCES,
" : \"foo\" A \n"
" : \"foo\" B \n",
XKB_KEY_A, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSING, "", XKB_KEY_NoSymbol,
@@ -421,12 +496,27 @@ test_conflicting(struct xkb_context *ctx)
XKB_KEY_NoSymbol));
// new same length as old #3
- assert(test_compose_seq_buffer(ctx,
+ assert(test_compose_seq_buffer(ctx, XKB_COMPOSE_COMPILE_OVERLAPPING_SEQUENCES,
" : \"foo\" A \n"
" : \"bar\" A \n",
XKB_KEY_A, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSING, "", XKB_KEY_NoSymbol,
XKB_KEY_B, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSED, "bar", XKB_KEY_A,
XKB_KEY_NoSymbol));
+
+ // overlapping tests
+ assert(test_compose_seq_buffer(ctx, XKB_COMPOSE_COMPILE_OVERLAPPING_SEQUENCES,
+ " : \"1\" X \n" // #1: overriden by #4
+ " : \"foo\" A \n" // #2: adds overlapping #1
+ " : \"bar\" B \n" // #3: adds overlapping; overriden by #5
+ " : \"2\" Y \n" // #4: overrides #1
+ " : \"baz\" C \n" // #5: overrides #3
+ " : \"foo\" D \n", // #6: adds overlapping #2
+ XKB_KEY_A, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_CANDIDATE, "2", XKB_KEY_Y,
+ XKB_KEY_B, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_CANDIDATE, "baz", XKB_KEY_C,
+ XKB_KEY_C, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_CANDIDATE, "foo", XKB_KEY_A,
+ XKB_KEY_D, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSING, "", XKB_KEY_NoSymbol,
+ XKB_KEY_E, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSED, "foo", XKB_KEY_D,
+ XKB_KEY_NoSymbol));
}
static void
@@ -563,7 +653,7 @@ test_modifier_syntax(struct xkb_context *ctx)
/* We don't do anything with the modifiers, but make sure we can parse
* them. */
- assert(test_compose_seq_buffer(ctx,
+ assert(test_compose_seq_buffer(ctx, XKB_COMPOSE_COMPILE_NO_FLAGS,
"None : X \n"
"Shift : Y \n"
"Ctrl : Y \n"
@@ -625,7 +715,7 @@ test_include(struct xkb_context *ctx)
" : \"bar\" Y\n", path);
assert(table_string);
- assert(test_compose_seq_buffer(ctx, table_string,
+ assert(test_compose_seq_buffer(ctx, XKB_COMPOSE_COMPILE_NO_FLAGS, table_string,
/* No conflict. */
XKB_KEY_dead_acute, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSING, "", XKB_KEY_NoSymbol,
XKB_KEY_dead_acute, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSED, "´", XKB_KEY_acute,
@@ -649,17 +739,55 @@ test_override(struct xkb_context *ctx)
{
const char *table_string = " : \"foo\" X\n"
" : \"bar\" Y\n"
- " : \"baz\" Z\n";
+ " : \"baz\" Z\n"
+ " : \"foo\" A\n"
+ " : \"bar\" B\n";
+
+ /* Without overlapping */
+ assert(test_compose_seq_buffer(ctx, XKB_COMPOSE_COMPILE_NO_FLAGS, table_string,
+ /* Comes after - does override. */
+ XKB_KEY_dead_circumflex, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSING, "", XKB_KEY_NoSymbol,
+ XKB_KEY_dead_circumflex, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSING, "", XKB_KEY_NoSymbol,
+ XKB_KEY_e, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSED, "baz", XKB_KEY_Z,
+
+ /* Override does not affect sibling nodes */
+ XKB_KEY_dead_circumflex, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSING, "", XKB_KEY_NoSymbol,
+ XKB_KEY_e, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSED, "bar", XKB_KEY_Y,
- assert(test_compose_seq_buffer(ctx, table_string,
/* Comes after - does override. */
- XKB_KEY_dead_circumflex, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSING, "", XKB_KEY_NoSymbol,
- XKB_KEY_dead_circumflex, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSING, "", XKB_KEY_NoSymbol,
- XKB_KEY_e, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSED, "baz", XKB_KEY_Z,
+ XKB_KEY_dead_circumflex, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSING, "", XKB_KEY_NoSymbol,
+ XKB_KEY_f, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSING, "", XKB_KEY_NoSymbol,
+ XKB_KEY_g, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSED, "foo", XKB_KEY_A,
+
+ /* Can cancel after candidate. */
+ XKB_KEY_dead_circumflex, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSING, "", XKB_KEY_NoSymbol,
+ XKB_KEY_f, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSING, "", XKB_KEY_NoSymbol,
+ XKB_KEY_f, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_CANCELLED, "", XKB_KEY_NoSymbol,
+ XKB_KEY_f, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_NOTHING, "", XKB_KEY_NoSymbol,
+
+ XKB_KEY_NoSymbol));
+
+ /* With overlapping */
+ assert(test_compose_seq_buffer(ctx, XKB_COMPOSE_COMPILE_OVERLAPPING_SEQUENCES, table_string,
+ /* Comes after - does override. */
+ XKB_KEY_dead_circumflex, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSING, "", XKB_KEY_NoSymbol,
+ XKB_KEY_dead_circumflex, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_CANDIDATE, "foo", XKB_KEY_X,
+ XKB_KEY_e, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSED, "baz", XKB_KEY_Z,
/* Override does not affect sibling nodes */
- XKB_KEY_dead_circumflex, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSING, "", XKB_KEY_NoSymbol,
- XKB_KEY_e, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSED, "bar", XKB_KEY_Y,
+ XKB_KEY_dead_circumflex, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSING, "", XKB_KEY_NoSymbol,
+ XKB_KEY_e, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSED, "bar", XKB_KEY_Y,
+
+ /* Comes after - does override. */
+ XKB_KEY_dead_circumflex, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSING, "", XKB_KEY_NoSymbol,
+ XKB_KEY_f, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_CANDIDATE, "bar", XKB_KEY_B,
+ XKB_KEY_g, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSED, "foo", XKB_KEY_A,
+
+ /* Can cancel after candidate. */
+ XKB_KEY_dead_circumflex, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSING, "", XKB_KEY_NoSymbol,
+ XKB_KEY_f, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_CANDIDATE, "bar", XKB_KEY_B,
+ XKB_KEY_f, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_CANDIDATE_ACCEPTED, "", XKB_KEY_NoSymbol,
+ XKB_KEY_f, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_NOTHING, "", XKB_KEY_NoSymbol,
XKB_KEY_NoSymbol));
}
@@ -777,7 +905,7 @@ test_escape_sequences(struct xkb_context *ctx)
*/
const char *table_string = " : \"\\401f\\x0o\\0o\" X\n";
- assert(test_compose_seq_buffer(ctx, table_string,
+ assert(test_compose_seq_buffer(ctx, XKB_COMPOSE_COMPILE_NO_FLAGS, table_string,
XKB_KEY_o, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSING, "", XKB_KEY_NoSymbol,
XKB_KEY_e, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSED, "foo", XKB_KEY_X,
XKB_KEY_NoSymbol));
@@ -810,7 +938,8 @@ main(int argc, char *argv[])
test_compose_utf8_bom(ctx);
test_invalid_encodings(ctx);
test_seqs(ctx);
- test_conflicting(ctx);
+ test_conflicting_without_overlapping(ctx);
+ test_conflicting_with_overlapping(ctx);
test_XCOMPOSEFILE(ctx);
test_from_locale(ctx);
test_state(ctx);
diff --git a/tools/interactive-evdev.c b/tools/interactive-evdev.c
index 66bc18cfb..d7d2c0db2 100644
--- a/tools/interactive-evdev.c
+++ b/tools/interactive-evdev.c
@@ -57,6 +57,7 @@ static bool terminate;
static int evdev_offset = 8;
static bool report_state_changes;
static bool with_compose;
+static bool compose_overlapping;
static enum xkb_consumed_mode consumed_mode = XKB_CONSUMED_MODE_XKB;
#ifdef ENABLE_PRIVATE_APIS
@@ -287,7 +288,9 @@ process_event(struct keyboard *kbd, uint16_t type, uint16_t code, int32_t value)
if (with_compose) {
status = xkb_compose_state_get_status(kbd->compose_state);
- if (status == XKB_COMPOSE_CANCELLED || status == XKB_COMPOSE_COMPOSED)
+ if (status == XKB_COMPOSE_CANCELLED ||
+ status == XKB_COMPOSE_COMPOSED ||
+ status == XKB_COMPOSE_CANDIDATE_ACCEPTED)
xkb_compose_state_reset(kbd->compose_state);
}
@@ -389,6 +392,7 @@ usage(FILE *fp, char *progname)
" --short (do not print layout nor Unicode keysym translation)\n"
" --report-state-changes (report changes to the state)\n"
" --enable-compose (enable Compose)\n"
+ " --enable-compose-overlapping (enable Compose overlapping sequences)\n"
" --consumed-mode={xkb|gtk} (select the consumed modifiers mode, default: xkb)\n"
" --without-x11-offset (don't add X11 keycode offset)\n"
"Other:\n"
@@ -426,6 +430,7 @@ main(int argc, char *argv[])
OPT_WITHOUT_X11_OFFSET,
OPT_CONSUMED_MODE,
OPT_COMPOSE,
+ OPT_COMPOSE_OVERLAPPING,
OPT_SHORT,
OPT_REPORT_STATE,
#ifdef ENABLE_PRIVATE_APIS
@@ -444,6 +449,7 @@ main(int argc, char *argv[])
{"keymap", required_argument, 0, OPT_KEYMAP},
{"consumed-mode", required_argument, 0, OPT_CONSUMED_MODE},
{"enable-compose", no_argument, 0, OPT_COMPOSE},
+ {"enable-compose-overlapping", no_argument, 0, OPT_COMPOSE_OVERLAPPING},
{"short", no_argument, 0, OPT_SHORT},
{"report-state-changes", no_argument, 0, OPT_REPORT_STATE},
{"without-x11-offset", no_argument, 0, OPT_WITHOUT_X11_OFFSET},
@@ -505,6 +511,9 @@ main(int argc, char *argv[])
case OPT_COMPOSE:
with_compose = true;
break;
+ case OPT_COMPOSE_OVERLAPPING:
+ compose_overlapping = true;
+ break;
case OPT_SHORT:
print_fields &= ~PRINT_VERBOSE_FIELDS;
break;
@@ -591,9 +600,13 @@ main(int argc, char *argv[])
if (with_compose) {
locale = setlocale(LC_CTYPE, NULL);
+ enum xkb_compose_compile_flags compose_compile_flags =
+ compose_overlapping
+ ? XKB_COMPOSE_COMPILE_OVERLAPPING_SEQUENCES
+ : XKB_COMPOSE_COMPILE_NO_FLAGS;
compose_table =
xkb_compose_table_new_from_locale(ctx, locale,
- XKB_COMPOSE_COMPILE_NO_FLAGS);
+ compose_compile_flags);
if (!compose_table) {
fprintf(stderr, "Couldn't create compose from locale\n");
goto out;
diff --git a/tools/interactive-wayland.c b/tools/interactive-wayland.c
index bca33f86b..5282045cf 100644
--- a/tools/interactive-wayland.c
+++ b/tools/interactive-wayland.c
@@ -416,7 +416,9 @@ kbd_key(void *data, struct wl_keyboard *wl_kbd, uint32_t serial, uint32_t time,
if (seat->compose_state) {
enum xkb_compose_status status = xkb_compose_state_get_status(seat->compose_state);
- if (status == XKB_COMPOSE_CANCELLED || status == XKB_COMPOSE_COMPOSED)
+ if (status == XKB_COMPOSE_CANCELLED ||
+ status == XKB_COMPOSE_COMPOSED ||
+ status == XKB_COMPOSE_CANDIDATE_ACCEPTED)
xkb_compose_state_reset(seat->compose_state);
}
@@ -702,11 +704,12 @@ static void
usage(FILE *fp, char *progname)
{
fprintf(fp,
- "Usage: %s [--help] [--enable-compose]\n",
+ "Usage: %s [--help] [--enable-compose] [--enable-compose-overlapping]\n",
progname);
fprintf(fp,
- " --enable-compose enable Compose\n"
- " --help display this help and exit\n"
+ " --enable-compose enable Compose\n"
+ " --enable-compose-overlapping enable Compose overlapping sequences\n"
+ " --help display this help and exit\n"
);
}
@@ -720,12 +723,15 @@ main(int argc, char *argv[])
struct xkb_compose_table *compose_table = NULL;
bool with_compose = false;
+ bool compose_overlapping = false;
enum options {
OPT_COMPOSE,
+ OPT_COMPOSE_OVERLAPPING,
};
static struct option opts[] = {
- {"help", no_argument, 0, 'h'},
- {"enable-compose", no_argument, 0, OPT_COMPOSE},
+ {"help", no_argument, 0, 'h'},
+ {"enable-compose", no_argument, 0, OPT_COMPOSE},
+ {"enable-compose-overlapping", no_argument, 0, OPT_COMPOSE_OVERLAPPING},
{0, 0, 0, 0},
};
@@ -741,6 +747,9 @@ main(int argc, char *argv[])
case OPT_COMPOSE:
with_compose = true;
break;
+ case OPT_COMPOSE_OVERLAPPING:
+ compose_overlapping = true;
+ break;
case 'h':
usage(stdout, argv[0]);
return EXIT_SUCCESS;
@@ -771,9 +780,13 @@ main(int argc, char *argv[])
if (with_compose) {
locale = setlocale(LC_CTYPE, NULL);
+ enum xkb_compose_compile_flags compose_compile_flags =
+ compose_overlapping
+ ? XKB_COMPOSE_COMPILE_OVERLAPPING_SEQUENCES
+ : XKB_COMPOSE_COMPILE_NO_FLAGS;
compose_table =
xkb_compose_table_new_from_locale(inter.ctx, locale,
- XKB_COMPOSE_COMPILE_NO_FLAGS);
+ compose_compile_flags);
if (!compose_table) {
fprintf(stderr, "Couldn't create compose from locale\n");
goto err_compose;
diff --git a/tools/interactive-x11.c b/tools/interactive-x11.c
index 0ab1898db..81f186a54 100644
--- a/tools/interactive-x11.c
+++ b/tools/interactive-x11.c
@@ -264,7 +264,8 @@ process_event(xcb_generic_event_t *gevent, struct keyboard *kbd)
if (kbd->compose_state) {
enum xkb_compose_status status = xkb_compose_state_get_status(kbd->compose_state);
if (status == XKB_COMPOSE_CANCELLED ||
- status == XKB_COMPOSE_COMPOSED)
+ status == XKB_COMPOSE_COMPOSED ||
+ status == XKB_COMPOSE_CANDIDATE_ACCEPTED)
xkb_compose_state_reset(kbd->compose_state);
}
@@ -354,10 +355,11 @@ static void
usage(FILE *fp, char *progname)
{
fprintf(fp,
- "Usage: %s [--help] [--enable-compose]\n",
+ "Usage: %s [--help] [--enable-compose] [--enable-compose-overlapping]\n",
progname);
fprintf(fp,
" --enable-compose enable Compose\n"
+ " --enable-compose-overlapping enable Compose overlapping sequences\n"
" --help display this help and exit\n"
);
}
@@ -375,12 +377,15 @@ main(int argc, char *argv[])
struct xkb_compose_table *compose_table = NULL;
bool with_compose = false;
+ bool compose_overlapping = false;
enum options {
OPT_COMPOSE,
+ OPT_COMPOSE_OVERLAPPING,
};
static struct option opts[] = {
{"help", no_argument, 0, 'h'},
{"enable-compose", no_argument, 0, OPT_COMPOSE},
+ {"enable-compose-overlapping", no_argument, 0, OPT_COMPOSE_OVERLAPPING},
{0, 0, 0, 0},
};
@@ -396,6 +401,9 @@ main(int argc, char *argv[])
case OPT_COMPOSE:
with_compose = true;
break;
+ case OPT_COMPOSE_OVERLAPPING:
+ compose_overlapping = true;
+ break;
case 'h':
usage(stdout, argv[0]);
return EXIT_SUCCESS;
@@ -434,9 +442,12 @@ main(int argc, char *argv[])
if (with_compose) {
locale = setlocale(LC_CTYPE, NULL);
+ enum xkb_compose_compile_flags compose_flags =
+ compose_overlapping
+ ? XKB_COMPOSE_COMPILE_OVERLAPPING_SEQUENCES
+ : XKB_COMPOSE_COMPILE_NO_FLAGS;
compose_table =
- xkb_compose_table_new_from_locale(ctx, locale,
- XKB_COMPOSE_COMPILE_NO_FLAGS);
+ xkb_compose_table_new_from_locale(ctx, locale, compose_flags);
if (!compose_table) {
fprintf(stderr, "Couldn't create compose from locale\n");
goto err_compose;
diff --git a/tools/tools-common.c b/tools/tools-common.c
index b163438fe..3dab32e3c 100644
--- a/tools/tools-common.c
+++ b/tools/tools-common.c
@@ -173,7 +173,7 @@ tools_print_keycode_state(char *prefix,
if (status == XKB_COMPOSE_COMPOSING || status == XKB_COMPOSE_CANCELLED)
return;
- if (status == XKB_COMPOSE_COMPOSED) {
+ if (status == XKB_COMPOSE_COMPOSED || status == XKB_COMPOSE_CANDIDATE) {
sym = xkb_compose_state_get_one_sym(compose_state);
syms = &sym;
nsyms = 1;
@@ -202,7 +202,7 @@ tools_print_keycode_state(char *prefix,
printf("] ");
if (fields & PRINT_UNICODE) {
- if (status == XKB_COMPOSE_COMPOSED)
+ if (status == XKB_COMPOSE_COMPOSED || status == XKB_COMPOSE_CANDIDATE)
xkb_compose_state_get_utf8(compose_state, s, sizeof(s));
else
xkb_state_key_get_utf8(state, keycode, s, sizeof(s));
diff --git a/tools/xkbcli-interactive-evdev.1 b/tools/xkbcli-interactive-evdev.1
index 167e3ec65..aede3a752 100644
--- a/tools/xkbcli-interactive-evdev.1
+++ b/tools/xkbcli-interactive-evdev.1
@@ -77,6 +77,9 @@ Report changes to the keyboard state
.It Fl \-enable\-compose
Enable Compose functionality
.
+.It Fl \-enable\-compose\-overlapping
+Allow Compose to handle overlapping sequences
+.
.It Fl \-consumed\-mode Brq xkb|gtk
Set the consumed modifiers mode (default: xkb)
.
diff --git a/tools/xkbcli-interactive-wayland.1 b/tools/xkbcli-interactive-wayland.1
index 0542e07d8..80bfffc17 100644
--- a/tools/xkbcli-interactive-wayland.1
+++ b/tools/xkbcli-interactive-wayland.1
@@ -31,6 +31,9 @@ Print help and exit
.
.It Fl \-enable\-compose
Enable Compose functionality
+.
+.It Fl \-enable\-compose\-overlapping
+Allow Compose to handle overlapping sequences
.El
.
.Sh SEE ALSO
diff --git a/tools/xkbcli-interactive-x11.1 b/tools/xkbcli-interactive-x11.1
index dbbd25b34..ff740007e 100644
--- a/tools/xkbcli-interactive-x11.1
+++ b/tools/xkbcli-interactive-x11.1
@@ -31,6 +31,9 @@ Print help and exit
.
.It Fl \-enable\-compose
Enable Compose functionality
+.
+.It Fl \-enable\-compose\-overlapping
+Allow Compose to handle overlapping sequences
.El
.
.Sh SEE ALSO