diff --git a/include/xkbcommon/xkbcommon-compose.h b/include/xkbcommon/xkbcommon-compose.h
index b28e4f843..1f6f480bd 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.
+ *
+ * <b><em>Without</em> `XKB_COMPOSE_COMPILE_OVERLAPPING_SEQUENCES`</b>
+ *
+ * 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.
+ * <b><em>With</em> `XKB_COMPOSE_COMPILE_OVERLAPPING_SEQUENCES`</b>
+ *
+ * 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
  */
@@ -499,6 +512,16 @@ enum xkb_compose_status {
     XKB_COMPOSE_NOTHING,
     /** In the middle of a sequence. */
     XKB_COMPOSE_COMPOSING,
+    /** 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,
     /** A complete sequence has been matched. */
     XKB_COMPOSE_COMPOSED,
     /** The last sequence was cancelled due to an unmatched keysym. */
@@ -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 4112baa16..8cc714fe1 100644
--- a/src/compose/parser.c
+++ b/src/compose/parser.c
@@ -347,13 +347,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.
@@ -376,8 +386,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);
@@ -397,28 +408,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");
@@ -426,18 +495,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 d7192f67c..1f422cdb3 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<A> : 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));
 }
@@ -254,7 +259,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,
         "<A>          :  \"foo\"  X \n"
         "<B> <A>      :  \"baz\"  Y \n",
         XKB_KEY_A,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,  "foo",   XKB_KEY_X,
@@ -265,7 +270,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,
@@ -275,7 +280,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,
         "<A> <B>     :  X \n"
         "<B> <A>     :  dollar \n"
         "<C>         :  dead_acute \n",
@@ -287,7 +292,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,
         "<A> <B>     :  X \n"
         "<C> <D>     :  Y \n",
         XKB_KEY_A,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING, "",      XKB_KEY_NoSymbol,
@@ -301,10 +306,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,
         "<A> <B> <C>  :  \"foo\"  A \n"
         "<A> <B>      :  \"bar\"  B \n",
         XKB_KEY_A,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
@@ -313,7 +318,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,
         "<A> <B>      :  \"bar\"  B \n"
         "<A> <B> <C>  :  \"foo\"  A \n",
         XKB_KEY_A,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
@@ -322,7 +327,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,
+        "<A> <B>      :  \"bar\"  B \n"
+        "<A> <B>      :  \"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,
+        "<A> <B>      :  \"foo\"  A \n"
+        "<A> <B>      :  \"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,
+        "<A> <B>      :  \"foo\"  A \n"
+        "<A> <B>      :  \"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,
+        "<A> <B>      :  \"foo\"  A \n"
+        "<A> <B>      :  \"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,
+        "<A>                 :  \"1\"    X \n"
+        "<A> <B> <C>         :  \"foo\"  A \n"
+        "<A> <B>             :  \"bar\"  B \n"
+        "<A>                 :  \"2\"    Y \n"
+        "<A> <B>             :  \"baz\"  C \n"
+        "<A> <B> <C> <D> <E> :  \"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,
+        "<A> <B> <C>  :  \"foo\"  A \n"
+        "<A> <B>      :  \"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,
+        "<A> <B>      :  \"bar\"  B \n"
+        "<A> <B> <C>  :  \"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,
         "<A> <B>      :  \"bar\"  B \n"
         "<A> <B>      :  \"bar\"  B \n",
         XKB_KEY_A,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
@@ -331,7 +406,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,
         "<A> <B>      :  \"foo\"  A \n"
         "<A> <B>      :  \"bar\"  B \n",
         XKB_KEY_A,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
@@ -339,7 +414,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,
         "<A> <B>      :  \"foo\"  A \n"
         "<A> <B>      :  \"foo\"  B \n",
         XKB_KEY_A,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
@@ -347,12 +422,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,
         "<A> <B>      :  \"foo\"  A \n"
         "<A> <B>      :  \"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,
+        "<A>                 :  \"1\"    X \n"  // #1: overriden by #4
+        "<A> <B> <C>         :  \"foo\"  A \n"  // #2: adds overlapping #1
+        "<A> <B>             :  \"bar\"  B \n"  // #3: adds overlapping; overriden by #5
+        "<A>                 :  \"2\"    Y \n"  // #4: overrides #1
+        "<A> <B>             :  \"baz\"  C \n"  // #5: overrides #3
+        "<A> <B> <C> <D> <E> :  \"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
@@ -489,7 +579,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 <A>          : X \n"
         "Shift <B>         : Y \n"
         "Ctrl <C>          : Y \n"
@@ -551,7 +641,7 @@ test_include(struct xkb_context *ctx)
                                  "<dead_tilde> <dead_tilde> : \"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,
@@ -575,17 +665,55 @@ test_override(struct xkb_context *ctx)
 {
     const char *table_string = "<dead_circumflex> <dead_circumflex> : \"foo\" X\n"
                                "<dead_circumflex> <e> : \"bar\" Y\n"
-                               "<dead_circumflex> <dead_circumflex> <e> : \"baz\" Z\n";
+                               "<dead_circumflex> <dead_circumflex> <e> : \"baz\" Z\n"
+                               "<dead_circumflex> <f> <g> : \"foo\" A\n"
+                               "<dead_circumflex> <f> : \"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));
 }
@@ -703,7 +831,7 @@ test_escape_sequences(struct xkb_context *ctx)
      */
     const char *table_string = "<o> <e> : \"\\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));
@@ -735,7 +863,8 @@ main(int argc, char *argv[])
 
     test_compose_utf8_bom(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