public inbox for git-commits@fedoraproject.org
help / color / mirror / Atom feed
* [rpms/ibus] autotool: Make Compose preedit less intrusive
@ 2026-05-31 2:07 Takao Fujiwara
0 siblings, 0 replies; only message in thread
From: Takao Fujiwara @ 2026-05-31 2:07 UTC (permalink / raw)
To: git-commits
A new commit has been pushed.
Repo : rpms/ibus
Branch : autotool
Commit : fb4b95e6ad201b9eaf27905b9030a0d9a1d8749b
Author : Takao Fujiwara <tfujiwar@redhat.com>
Date : 2021-07-26T23:57:28+09:00
Stats : +3319/-1 in 2 file(s)
URL : https://src.fedoraproject.org/rpms/ibus/c/fb4b95e6ad201b9eaf27905b9030a0d9a1d8749b?branch=autotool
Log:
Make Compose preedit less intrusive
- Set Multi_key to 0xB7 in compose preedit
- Search language name in engine list in ibus-setup
---
diff --git a/ibus-HEAD.patch b/ibus-HEAD.patch
index 9e6842e..362ebb1 100644
--- a/ibus-HEAD.patch
+++ b/ibus-HEAD.patch
@@ -2724,3 +2724,3316 @@ index a79e9296..099b9c60 100644
--
2.28.0
+From a4939f67f9de8275219e2cfcd3873e0fbe59058f Mon Sep 17 00:00:00 2001
+From: fujiwarat <takao.fujiwara1@gmail.com>
+Date: Fri, 16 Jul 2021 13:08:02 +0900
+Subject: [PATCH] setup: Enhance engine search function
+
+ibus-setup can search both the language names and input method names
+in the top language list but when you search a language keyword
+in the language list and move to the input method list after click
+the hit language name, any input methods could not be shown because
+the language didn't hit in any input method names.
+
+In this enhancement, ibus-setup can show the input methods to hit
+the language names.
+---
+ setup/enginedialog.py | 26 ++++++++++++++++++++------
+ 1 file changed, 20 insertions(+), 6 deletions(-)
+
+diff --git a/setup/enginedialog.py b/setup/enginedialog.py
+index e1c322bf..470f801c 100644
+--- a/setup/enginedialog.py
++++ b/setup/enginedialog.py
+@@ -120,12 +120,26 @@ class EngineDialog(Gtk.Dialog):
+ return True
+ if word in row.untrans.lower():
+ return True
+- if row.lang_info and row.name in self.__engines_for_lang:
+- for row_e in self.__engines_for_lang[row.name]:
+- if word in row_e.name.lower():
+- return True
+- if word in row_e.untrans.lower():
+- return True
++ # Search engine name in language list
++ if row.lang_info:
++ if row.name in self.__engines_for_lang.keys():
++ for row_l in self.__engines_for_lang[row.name]:
++ if word in row_l.name.lower():
++ return True
++ if word in row_l.untrans.lower():
++ return True
++ # Search language name in engine list
++ if not row.lang_info:
++ for l in self.__engines_for_lang.keys():
++ if word in l.lower():
++ for row_l in self.__engines_for_lang[l]:
++ if row.name == row_l.name:
++ return True
++ for (trans, untrans) in self.__untrans_for_lang.items():
++ if word in untrans.lower():
++ for row_l in self.__engines_for_lang[trans]:
++ if row.name == row_l.name:
++ return True
+ return False
+
+
+--
+2.28.0
+
+From 7e12d589ee4979fdd0f0b08f1bac9049741ec628 Mon Sep 17 00:00:00 2001
+From: fujiwarat <takao.fujiwara1@gmail.com>
+Date: Mon, 26 Jul 2021 22:52:12 +0900
+Subject: [PATCH 1/6] src/ibuscomposetable: Move ibus_compose_table_check
+
+- Move ibus_compose_table_check and ibus_compose_table_compact_check
+ from ibusenginesimple.c to ibuscomposetable.c
+- Fix src/tests/ibus-compose.c to read compose sequences correctly.
+---
+ src/ibuscomposetable.c | 519 +++++++++++++++++++++++++++++-
+ src/ibusenginesimple.c | 583 ++++------------------------------
+ src/ibusenginesimpleprivate.h | 17 +-
+ src/tests/Makefile.am | 3 +-
+ src/tests/ibus-compose.c | 7 +-
+ 5 files changed, 601 insertions(+), 528 deletions(-)
+
+diff --git a/src/ibuscomposetable.c b/src/ibuscomposetable.c
+index dd7cbf83..f85177a6 100644
+--- a/src/ibuscomposetable.c
++++ b/src/ibuscomposetable.c
+@@ -33,6 +33,13 @@
+
+ #include "ibusenginesimpleprivate.h"
+
++/* This file contains the table of the compose sequences,
++ * static const guint16 gtk_compose_seqs_compact[] = {}
++ * It is generated from the compose-parse.py script.
++ */
++#include "gtkimcontextsimpleseqs.h"
++
++
+ #define IBUS_COMPOSE_TABLE_MAGIC "IBusComposeTable"
+ #define IBUS_COMPOSE_TABLE_VERSION (4)
+ #define X11_DATADIR X11_DATA_PREFIX "/share/X11/locale"
+@@ -44,6 +51,10 @@ typedef struct {
+ } IBusComposeData;
+
+
++extern const IBusComposeTableCompactEx ibus_compose_table_compact;
++extern const IBusComposeTableCompactEx ibus_compose_table_compact_32bit;
++
++
+ static void
+ ibus_compose_data_free (IBusComposeData *compose_data)
+ {
+@@ -527,20 +538,22 @@ ibus_compose_list_check_duplicated (GList *compose_list,
+ is_32bit = (n_outputs > 1) ? TRUE :
+ (compose_data->values[0] >= 0xFFFF) ? TRUE : FALSE;
+ if (!is_32bit &&
+- ibus_check_compact_table (&ibus_compose_table_compact,
+- keysyms,
+- n_compose,
+- &compose_finish,
+- &output_chars) && compose_finish) {
++ ibus_compose_table_compact_check (&ibus_compose_table_compact,
++ keysyms,
++ n_compose,
++ &compose_finish,
++ &output_chars) &&
++ compose_finish) {
+ if (compose_data->values[0] == *output_chars)
+ removed_list = g_list_append (removed_list, compose_data);
+ g_free (output_chars);
+ } else if (is_32bit &&
+- ibus_check_compact_table (&ibus_compose_table_compact_32bit,
+- keysyms,
+- n_compose,
+- &compose_finish,
+- &output_chars) && compose_finish) {
++ ibus_compose_table_compact_check (
++ &ibus_compose_table_compact_32bit,
++ keysyms,
++ n_compose,
++ &compose_finish,
++ &output_chars) && compose_finish) {
+
+ if (n_outputs == unichar_length (output_chars)) {
+ int j = 0;
+@@ -1354,3 +1367,489 @@ ibus_compose_table_list_add_file (GSList *compose_tables,
+ ibus_compose_table_save_cache (compose_table);
+ return g_slist_prepend (compose_tables, compose_table);
+ }
++
++
++static int
++compare_seq (const void *key, const void *value)
++{
++ int i = 0;
++ const guint16 *keysyms = key;
++ const guint16 *seq = value;
++
++ while (keysyms[i]) {
++ if (keysyms[i] < seq[i])
++ return -1;
++ else if (keysyms[i] > seq[i])
++ return 1;
++
++ i++;
++ }
++
++ return 0;
++}
++
++
++gboolean
++ibus_compose_table_check (const IBusComposeTableEx *table,
++ guint16 *compose_buffer,
++ gint n_compose,
++ gboolean *compose_finish,
++ gboolean *compose_match,
++ GString *output,
++ gboolean is_32bit)
++{
++ gint row_stride = table->max_seq_len + 2;
++ guint16 *data_first;
++ int n_seqs;
++ guint16 *seq;
++
++ if (compose_finish)
++ *compose_finish = FALSE;
++ if (compose_match)
++ *compose_match = FALSE;
++ if (output)
++ g_string_set_size (output, 0);
++
++ if (n_compose > table->max_seq_len)
++ return FALSE;
++
++ if (is_32bit) {
++ if (!table->priv)
++ return FALSE;
++ data_first = table->priv->data_first;
++ n_seqs = table->priv->first_n_seqs;
++ } else {
++ data_first = table->data;
++ n_seqs = table->n_seqs;
++ }
++ seq = bsearch (compose_buffer,
++ data_first, n_seqs,
++ sizeof (guint16) * row_stride,
++ compare_seq);
++
++ if (seq == NULL)
++ return FALSE;
++
++ guint16 *prev_seq;
++
++ /* Back up to the first sequence that matches to make sure
++ * we find the exact match if their is one.
++ */
++ while (seq > data_first) {
++ prev_seq = seq - row_stride;
++ if (compare_seq (compose_buffer, prev_seq) != 0)
++ break;
++ seq = prev_seq;
++ }
++
++ /* complete sequence */
++ if (n_compose == table->max_seq_len || seq[n_compose] == 0) {
++ guint16 *next_seq;
++ gunichar value = 0;
++ int num = 0;
++ int index = 0;
++ gchar *output_str = NULL;
++ GError *error = NULL;
++
++ if (is_32bit) {
++ num = seq[table->max_seq_len];
++ index = seq[table->max_seq_len + 1];
++ value = table->priv->data_second[index];
++ } else {
++ value = seq[table->max_seq_len];
++ }
++
++ if (is_32bit) {
++ output_str = g_ucs4_to_utf8 (table->priv->data_second + index,
++ num, NULL, NULL, &error);
++ if (output_str) {
++ if (output)
++ g_string_append (output, output_str);
++ g_free (output_str);
++ if (compose_match)
++ *compose_match = TRUE;
++ } else {
++ g_warning ("Failed to output multiple characters: %s",
++ error->message);
++ g_error_free (error);
++ }
++ } else {
++ if (output)
++ g_string_append_unichar (output, value);
++ if (compose_match)
++ *compose_match = TRUE;
++ }
++
++ /* We found a tentative match. See if there are any longer
++ * sequences containing this subsequence
++ */
++ next_seq = seq + row_stride;
++ if (next_seq < data_first + row_stride * n_seqs) {
++ if (compare_seq (compose_buffer, next_seq) == 0)
++ return TRUE;
++ }
++
++ if (compose_finish)
++ *compose_finish = TRUE;
++ compose_buffer[0] = 0;
++ }
++ return TRUE;
++}
++
++
++static int
++compare_seq_index (const void *key, const void *value)
++{
++ const guint16 *keysyms = key;
++ const guint16 *seq = value;
++
++ if (keysyms[0] < seq[0])
++ return -1;
++ else if (keysyms[0] > seq[0])
++ return 1;
++ return 0;
++}
++
++
++/**
++ * ibus_compose_table_compact_check:
++ * @table: A const `IBusComposeTableCompactEx`
++ * @compose_buffer: Typed compose sequence buffer
++ * @n_compose: The length of `compose_buffer`
++ * @compose_finish: If %TRUE, `output_chars` should be committed
++ * @output_chars: An array of gunichar of output compose characters
++ *
++ * output_chars is better to use gunichar instead of GString because
++ * IBusComposeData->values[] is the gunichar array.
++ */
++gboolean
++ibus_compose_table_compact_check (const IBusComposeTableCompactEx *table,
++ guint16
++ *compose_buffer,
++ gint n_compose,
++ gboolean
++ *compose_finish,
++ gunichar **output_chars)
++{
++ gint row_stride;
++ guint16 *seq_index;
++ guint16 *seq;
++ gint i;
++
++ if (compose_finish)
++ *compose_finish = FALSE;
++ if (output_chars)
++ *output_chars = NULL;
++
++ /* Will never match, if the sequence in the compose buffer is longer
++ * than the sequences in the table. Further, compare_seq (key, val)
++ * will overrun val if key is longer than val. */
++ if (n_compose > table->max_seq_len)
++ return FALSE;
++
++ seq_index = bsearch (compose_buffer,
++ table->data,
++ table->n_index_size,
++ sizeof (guint16) * table->n_index_stride,
++ compare_seq_index);
++
++ if (seq_index == NULL)
++ return FALSE;
++
++ if (n_compose == 1)
++ return TRUE;
++
++ seq = NULL;
++
++ if (table->priv) {
++ for (i = n_compose - 1; i < table->max_seq_len; i++) {
++ row_stride = i + 2;
++
++ if (seq_index[i + 1] - seq_index[i] > 0) {
++ seq = bsearch (compose_buffer + 1,
++ table->data + seq_index[i],
++ (seq_index[i + 1] - seq_index[i]) / row_stride,
++ sizeof (guint16) * row_stride,
++ compare_seq);
++ if (seq) {
++ if (i == n_compose - 1)
++ break;
++ else
++ return TRUE;
++ }
++ }
++ }
++ if (!seq) {
++ return FALSE;
++ } else {
++ int index = seq[row_stride - 2];
++ int length = seq[row_stride - 1];
++ int j;
++ if (compose_finish)
++ *compose_finish = TRUE;
++ if (output_chars) {
++ *output_chars = g_new (gunichar, length + 1);
++ for (j = 0; j < length; j++) {
++ (*output_chars)[j] = table->priv->data2[index + j];
++ }
++ (*output_chars)[length] = 0;
++ }
++
++ return TRUE;
++ }
++ } else {
++ for (i = n_compose - 1; i < table->max_seq_len; i++) {
++ row_stride = i + 1;
++
++ if (seq_index[i + 1] - seq_index[i] > 0) {
++ seq = bsearch (compose_buffer + 1,
++ table->data + seq_index[i],
++ (seq_index[i + 1] - seq_index[i]) / row_stride,
++ sizeof (guint16) * row_stride,
++ compare_seq);
++
++ if (seq) {
++ if (i == n_compose - 1)
++ break;
++ else
++ return TRUE;
++ }
++ }
++ }
++ if (!seq) {
++ return FALSE;
++ } else {
++ if (compose_finish)
++ *compose_finish = TRUE;
++ if (output_chars) {
++ *output_chars = g_new (gunichar, 2);
++ (*output_chars)[0] = seq[row_stride - 1];
++ (*output_chars)[1] = 0;
++ }
++
++ return TRUE;
++ }
++ }
++
++ g_assert_not_reached ();
++}
++
++
++/* Checks if a keysym is a dead key. Dead key keysym values are defined in
++ * ../gdk/gdkkeysyms.h and the first is GDK_KEY_dead_grave. As X.Org is updated,
++ * more dead keys are added and we need to update the upper limit.
++ * Currently, the upper limit is GDK_KEY_dead_dasia+1. The +1 has to do with
++ * a temporary issue in the X.Org header files.
++ * In future versions it will be just the keysym (no +1).
++ */
++#define IS_DEAD_KEY(k) \
++ ((k) >= IBUS_KEY_dead_grave && (k) <= IBUS_KEY_dead_greek)
++
++/* This function receives a sequence of Unicode characters and tries to
++ * normalize it (NFC). We check for the case the the resulting string
++ * has length 1 (single character).
++ * NFC normalisation normally rearranges diacritic marks, unless these
++ * belong to the same Canonical Combining Class.
++ * If they belong to the same canonical combining class, we produce all
++ * permutations of the diacritic marks, then attempt to normalize.
++ */
++static gboolean
++check_normalize_nfc (gunichar* combination_buffer, gint n_compose)
++{
++ gunichar combination_buffer_temp[IBUS_MAX_COMPOSE_LEN];
++ gchar *combination_utf8_temp = NULL;
++ gchar *nfc_temp = NULL;
++ gint n_combinations;
++ gunichar temp_swap;
++ gint i;
++
++ n_combinations = 1;
++
++ for (i = 1; i < n_compose; i++ )
++ n_combinations *= i;
++
++ /* Xorg reuses dead_tilde for the perispomeni diacritic mark.
++ * We check if base character belongs to Greek Unicode block,
++ * and if so, we replace tilde with perispomeni. */
++ if (combination_buffer[0] >= 0x390 && combination_buffer[0] <= 0x3FF) {
++ for (i = 1; i < n_compose; i++ )
++ if (combination_buffer[i] == 0x303)
++ combination_buffer[i] = 0x342;
++ }
++
++ memcpy (combination_buffer_temp,
++ combination_buffer,
++ IBUS_MAX_COMPOSE_LEN * sizeof (gunichar) );
++
++ for (i = 0; i < n_combinations; i++ ) {
++ g_unicode_canonical_ordering (combination_buffer_temp, n_compose);
++ combination_utf8_temp = g_ucs4_to_utf8 (combination_buffer_temp, -1,
++ NULL, NULL, NULL);
++ nfc_temp = g_utf8_normalize (combination_utf8_temp, -1,
++ G_NORMALIZE_NFC);
++
++ if (g_utf8_strlen (nfc_temp, -1) == 1) {
++ memcpy (combination_buffer,
++ combination_buffer_temp,
++ IBUS_MAX_COMPOSE_LEN * sizeof (gunichar) );
++
++ g_free (combination_utf8_temp);
++ g_free (nfc_temp);
++
++ return TRUE;
++ }
++
++ g_free (combination_utf8_temp);
++ g_free (nfc_temp);
++
++ if (n_compose > 2) {
++ gint j = i % (n_compose - 1) + 1;
++ gint k = (i+1) % (n_compose - 1) + 1;
++ if (j >= IBUS_MAX_COMPOSE_LEN) {
++ g_warning ("j >= IBUS_MAX_COMPOSE_LEN for " \
++ "combination_buffer_temp");
++ break;
++ }
++ if (k >= IBUS_MAX_COMPOSE_LEN) {
++ g_warning ("k >= IBUS_MAX_COMPOSE_LEN for " \
++ "combination_buffer_temp");
++ break;
++ }
++ temp_swap = combination_buffer_temp[j];
++ combination_buffer_temp[j] = combination_buffer_temp[k];
++ combination_buffer_temp[k] = temp_swap;
++ } else {
++ break;
++ }
++ }
++
++ return FALSE;
++}
++
++
++gboolean
++ibus_check_algorithmically (const guint16 *compose_buffer,
++ gint n_compose,
++ gunichar *output_char)
++
++{
++ gint i;
++ gunichar combination_buffer[IBUS_MAX_COMPOSE_LEN];
++ gchar *combination_utf8, *nfc;
++
++ if (output_char)
++ *output_char = 0;
++
++ if (n_compose >= IBUS_MAX_COMPOSE_LEN)
++ return FALSE;
++
++ for (i = 0; i < n_compose && IS_DEAD_KEY (compose_buffer[i]); i++)
++ ;
++ if (i == n_compose)
++ return TRUE;
++
++ if (i > 0 && i == n_compose - 1) {
++ combination_buffer[0] = ibus_keyval_to_unicode (compose_buffer[i]);
++ combination_buffer[n_compose] = 0;
++ i--;
++ while (i >= 0) {
++ combination_buffer[i+1] = ibus_keysym_to_unicode (compose_buffer[i],
++ TRUE);
++ if (!combination_buffer[i+1]) {
++ combination_buffer[i+1] =
++ ibus_keyval_to_unicode (compose_buffer[i]);
++ }
++ i--;
++ }
++
++ /* If the buffer normalizes to a single character,
++ * then modify the order of combination_buffer accordingly, if
++ * necessary, and return TRUE.
++ */
++ if (check_normalize_nfc (combination_buffer, n_compose)) {
++ combination_utf8 = g_ucs4_to_utf8 (combination_buffer, -1,
++ NULL, NULL, NULL);
++ nfc = g_utf8_normalize (combination_utf8, -1, G_NORMALIZE_NFC);
++
++ if (output_char)
++ *output_char = g_utf8_get_char (nfc);
++
++ g_free (combination_utf8);
++ g_free (nfc);
++
++ return TRUE;
++ }
++ }
++
++ return FALSE;
++}
++
++
++gunichar
++ibus_keysym_to_unicode (guint16 keysym,
++ gboolean combining) {
++#define CASE(keysym_suffix, unicode) \
++ case IBUS_KEY_dead_##keysym_suffix: return unicode
++#define CASE_COMBINE(keysym_suffix, combined_unicode, isolated_unicode) \
++ case IBUS_KEY_dead_##keysym_suffix: \
++ if (combining) \
++ return combined_unicode; \
++ else \
++ return isolated_unicode
++ switch (keysym) {
++ CASE (a, 0x03041);
++ CASE (A, 0x03042);
++ CASE (i, 0x03043);
++ CASE (I, 0x03044);
++ CASE (u, 0x03045);
++ CASE (U, 0x03046);
++ CASE (e, 0x03047);
++ CASE (E, 0x03048);
++ CASE (o, 0x03049);
++ CASE (O, 0x0304A);
++ CASE (abovecomma, 0x0313);
++ CASE_COMBINE (abovedot, 0x0307, 0x02D9);
++ CASE (abovereversedcomma, 0x0314);
++ CASE_COMBINE (abovering, 0x030A, 0x02DA);
++ CASE_COMBINE (acute, 0x0301, 0x00B4);
++ CASE (belowbreve, 0x032E);
++ CASE_COMBINE (belowcircumflex, 0x032D, 0xA788);
++ CASE_COMBINE (belowcomma, 0x0326, 0x002C);
++ CASE (belowdiaeresis, 0x0324);
++ CASE_COMBINE (belowdot, 0x0323, 0x002E);
++ CASE_COMBINE (belowmacron, 0x0331, 0x02CD);
++ CASE_COMBINE (belowring, 0x030A, 0x02F3);
++ CASE_COMBINE (belowtilde, 0x0330, 0x02F7);
++ CASE_COMBINE (breve, 0x0306, 0x02D8);
++ CASE_COMBINE (capital_schwa, 0x018F, 0x04D8);
++ CASE_COMBINE (caron, 0x030C, 0x02C7);
++ CASE_COMBINE (cedilla, 0x0327, 0x00B8);
++ CASE_COMBINE (circumflex, 0x0302, 0x005E);
++ CASE (currency, 0x00A4);
++ // IBUS_KEY_dead_dasia == IBUS_KEY_dead_abovereversedcomma
++ CASE_COMBINE (diaeresis, 0x0308, 0x00A8);
++ CASE_COMBINE (doubleacute, 0x030B, 0x02DD);
++ CASE_COMBINE (doublegrave, 0x030F, 0x02F5);
++ CASE_COMBINE (grave, 0x0300, 0x0060);
++ CASE (greek, 0x03BC);
++ CASE (hook, 0x0309);
++ CASE (horn, 0x031B);
++ CASE (invertedbreve, 0x032F);
++ CASE_COMBINE (iota, 0x0345, 0x037A);
++ CASE_COMBINE (macron, 0x0304, 0x00AF);
++ CASE_COMBINE (ogonek, 0x0328, 0x02DB);
++ // IBUS_KEY_dead_perispomeni == IBUS_KEY_dead_tilde
++ // IBUS_KEY_dead_psili == IBUS_KEY_dead_abovecomma
++ CASE_COMBINE (semivoiced_sound, 0x309A, 0x309C);
++ CASE_COMBINE (small_schwa, 0x1D4A, 0x04D9);
++ CASE (stroke, 0x002F);
++ CASE_COMBINE (tilde, 0x0303, 0x007E);
++ CASE_COMBINE (voiced_sound, 0x3099, 0x309B);
++ case IBUS_KEY_Multi_key:
++ return 0x2384;
++ default:;
++ }
++ return 0x0;
++#undef CASE
++#undef CASE_COMBINE
++}
+diff --git a/src/ibusenginesimple.c b/src/ibusenginesimple.c
+index 6dbc39c7..4644620b 100644
+--- a/src/ibusenginesimple.c
++++ b/src/ibusenginesimple.c
+@@ -91,11 +91,6 @@ struct _IBusEngineSimplePrivate {
+ gboolean lookup_table_visible;
+ };
+
+-struct _IBusComposeTableCompactPrivate
+-{
+- const guint32 *data2;
+-};
+-
+ /* From the values below, the value 30 means the number of different first keysyms
+ * that exist in the Compose file (from Xorg). When running compose-parse.py without
+ * parameters, you get the count that you can put here. Needed when updating the
+@@ -124,6 +119,7 @@ const IBusComposeTableCompactEx ibus_compose_table_compact_32bit = {
+ };
+
+ guint COMPOSE_BUFFER_SIZE = 20;
++G_LOCK_DEFINE_STATIC (global_tables);
+ static GSList *global_tables;
+
+ /* functions prototype */
+@@ -261,74 +257,6 @@ ibus_engine_simple_commit_char (IBusEngineSimple *simple,
+ ibus_text_new_from_unichar (ch));
+ }
+
+-static gunichar
+-ibus_keysym_to_unicode (guint16 keysym,
+- gboolean combining) {
+-#define CASE(keysym_suffix, unicode) \
+- case IBUS_KEY_dead_##keysym_suffix: return unicode
+-#define CASE_COMBINE(keysym_suffix, combined_unicode, isolated_unicode) \
+- case IBUS_KEY_dead_##keysym_suffix: \
+- if (combining) \
+- return combined_unicode; \
+- else \
+- return isolated_unicode
+- switch (keysym) {
+- CASE (a, 0x03041);
+- CASE (A, 0x03042);
+- CASE (i, 0x03043);
+- CASE (I, 0x03044);
+- CASE (u, 0x03045);
+- CASE (U, 0x03046);
+- CASE (e, 0x03047);
+- CASE (E, 0x03048);
+- CASE (o, 0x03049);
+- CASE (O, 0x0304A);
+- CASE (abovecomma, 0x0313);
+- CASE_COMBINE (abovedot, 0x0307, 0x02D9);
+- CASE (abovereversedcomma, 0x0314);
+- CASE_COMBINE (abovering, 0x030A, 0x02DA);
+- CASE_COMBINE (acute, 0x0301, 0x00B4);
+- CASE (belowbreve, 0x032E);
+- CASE_COMBINE (belowcircumflex, 0x032D, 0xA788);
+- CASE_COMBINE (belowcomma, 0x0326, 0x002C);
+- CASE (belowdiaeresis, 0x0324);
+- CASE_COMBINE (belowdot, 0x0323, 0x002E);
+- CASE_COMBINE (belowmacron, 0x0331, 0x02CD);
+- CASE_COMBINE (belowring, 0x030A, 0x02F3);
+- CASE_COMBINE (belowtilde, 0x0330, 0x02F7);
+- CASE_COMBINE (breve, 0x0306, 0x02D8);
+- CASE_COMBINE (capital_schwa, 0x018F, 0x04D8);
+- CASE_COMBINE (caron, 0x030C, 0x02C7);
+- CASE_COMBINE (cedilla, 0x0327, 0x00B8);
+- CASE_COMBINE (circumflex, 0x0302, 0x005E);
+- CASE (currency, 0x00A4);
+- // IBUS_KEY_dead_dasia == IBUS_KEY_dead_abovereversedcomma
+- CASE_COMBINE (diaeresis, 0x0308, 0x00A8);
+- CASE_COMBINE (doubleacute, 0x030B, 0x02DD);
+- CASE_COMBINE (doublegrave, 0x030F, 0x02F5);
+- CASE_COMBINE (grave, 0x0300, 0x0060);
+- CASE (greek, 0x03BC);
+- CASE (hook, 0x0309);
+- CASE (horn, 0x031B);
+- CASE (invertedbreve, 0x032F);
+- CASE_COMBINE (iota, 0x0345, 0x037A);
+- CASE_COMBINE (macron, 0x0304, 0x00AF);
+- CASE_COMBINE (ogonek, 0x0328, 0x02DB);
+- // IBUS_KEY_dead_perispomeni == IBUS_KEY_dead_tilde
+- // IBUS_KEY_dead_psili == IBUS_KEY_dead_abovecomma
+- CASE_COMBINE (semivoiced_sound, 0x309A, 0x309C);
+- CASE_COMBINE (small_schwa, 0x1D4A, 0x04D9);
+- CASE (stroke, 0x002F);
+- CASE_COMBINE (tilde, 0x0303, 0x007E);
+- CASE_COMBINE (voiced_sound, 0x3099, 0x309B);
+- case IBUS_KEY_Multi_key:
+- return 0x2384;
+- default:;
+- }
+- return 0x0;
+-#undef CASE
+-#undef CASE_COMBINE
+-}
+
+ static void
+ ibus_engine_simple_commit_str (IBusEngineSimple *simple,
+@@ -607,415 +535,6 @@ check_emoji_table (IBusEngineSimple *simple,
+ return FALSE;
+ }
+
+-static int
+-compare_seq_index (const void *key, const void *value)
+-{
+- const guint16 *keysyms = key;
+- const guint16 *seq = value;
+-
+- if (keysyms[0] < seq[0])
+- return -1;
+- else if (keysyms[0] > seq[0])
+- return 1;
+- return 0;
+-}
+-
+-static int
+-compare_seq (const void *key, const void *value)
+-{
+- int i = 0;
+- const guint16 *keysyms = key;
+- const guint16 *seq = value;
+-
+- while (keysyms[i]) {
+- if (keysyms[i] < seq[i])
+- return -1;
+- else if (keysyms[i] > seq[i])
+- return 1;
+-
+- i++;
+- }
+-
+- return 0;
+-}
+-
+-
+-static gboolean
+-check_table (IBusEngineSimple *simple,
+- const IBusComposeTableEx *table,
+- gint n_compose,
+- gboolean is_32bit)
+-{
+- IBusEngineSimplePrivate *priv = simple->priv;
+- gint row_stride = table->max_seq_len + 2;
+- guint16 *data_first;
+- int n_seqs;
+- guint16 *seq;
+-
+- g_assert (IBUS_IS_ENGINE_SIMPLE (simple));
+- CHECK_COMPOSE_BUFFER_LENGTH (n_compose);
+-
+- if (n_compose > table->max_seq_len)
+- return FALSE;
+-
+- if (is_32bit) {
+- if (!table->priv)
+- return FALSE;
+- data_first = table->priv->data_first;
+- n_seqs = table->priv->first_n_seqs;
+- } else {
+- data_first = table->data;
+- n_seqs = table->n_seqs;
+- }
+- seq = bsearch (priv->compose_buffer,
+- data_first, n_seqs,
+- sizeof (guint16) * row_stride,
+- compare_seq);
+-
+- if (seq == NULL)
+- return FALSE;
+-
+- guint16 *prev_seq;
+-
+- priv->tentative_match = 0;
+- priv->tentative_match_len = 0;
+- /* Back up to the first sequence that matches to make sure
+- * we find the exact match if their is one.
+- */
+- while (seq > data_first) {
+- prev_seq = seq - row_stride;
+- if (compare_seq (priv->compose_buffer, prev_seq) != 0) {
+- break;
+- }
+- seq = prev_seq;
+- }
+-
+- /* complete sequence */
+- if (n_compose == table->max_seq_len || seq[n_compose] == 0) {
+- guint16 *next_seq;
+- gunichar value = 0;
+- int num = 0;
+- int index = 0;
+- gchar *output_str = NULL;
+- GError *error = NULL;
+-
+- if (is_32bit) {
+- num = seq[table->max_seq_len];
+- index = seq[table->max_seq_len + 1];
+- value = table->priv->data_second[index];
+- } else {
+- value = seq[table->max_seq_len];
+- }
+-
+- /* We found a tentative match. See if there are any longer
+- * sequences containing this subsequence
+- */
+- next_seq = seq + row_stride;
+- if (next_seq < data_first + row_stride * n_seqs) {
+- if (compare_seq (priv->compose_buffer, next_seq) == 0) {
+- priv->tentative_match = value;
+- priv->tentative_match_len = n_compose;
+-
+- ibus_engine_simple_update_preedit_text (simple);
+-
+- return TRUE;
+- }
+- }
+-
+- if (is_32bit) {
+- output_str = g_ucs4_to_utf8 (table->priv->data_second + index,
+- num, NULL, NULL, &error);
+- if (output_str) {
+- ibus_engine_simple_commit_str(simple, output_str);
+- g_free (output_str);
+- } else {
+- g_warning ("Failed to output multiple characters: %s",
+- error->message);
+- g_error_free (error);
+- }
+- } else {
+- ibus_engine_simple_commit_char (simple, value);
+- }
+- priv->compose_buffer[0] = 0;
+- }
+- ibus_engine_simple_update_preedit_text (simple);
+- return TRUE;
+-}
+-
+-gboolean
+-ibus_check_compact_table (const IBusComposeTableCompactEx *table,
+- guint16 *compose_buffer,
+- gint n_compose,
+- gboolean *compose_finish,
+- gunichar **output_chars)
+-{
+- gint row_stride;
+- guint16 *seq_index;
+- guint16 *seq;
+- gint i;
+-
+- if (compose_finish)
+- *compose_finish = FALSE;
+- if (output_chars)
+- *output_chars = NULL;
+-
+- CHECK_COMPOSE_BUFFER_LENGTH (n_compose);
+-
+- /* Will never match, if the sequence in the compose buffer is longer
+- * than the sequences in the table. Further, compare_seq (key, val)
+- * will overrun val if key is longer than val. */
+- if (n_compose > table->max_seq_len)
+- return FALSE;
+-
+- // g_debug ("check_compact_table(n_compose=%d) [%04x, %04x, %04x, %04x]",
+- // n_compose,
+- // compose_buffer[0],
+- // compose_buffer[1],
+- // compose_buffer[2],
+- // compose_buffer[3]);
+-
+- seq_index = bsearch (compose_buffer,
+- table->data,
+- table->n_index_size,
+- sizeof (guint16) * table->n_index_stride,
+- compare_seq_index);
+-
+- if (seq_index == NULL) {
+- // g_debug ("compact: no\n");
+- return FALSE;
+- }
+-
+- if (n_compose == 1) {
+- // g_debug ("compact: yes\n");
+- return TRUE;
+- }
+-
+- // g_debug ("compact: %04x ", *seq_index);
+- seq = NULL;
+-
+- if (table->priv) {
+- for (i = n_compose - 1; i < table->max_seq_len; i++) {
+- row_stride = i + 2;
+-
+- if (seq_index[i + 1] - seq_index[i] > 0) {
+- seq = bsearch (compose_buffer + 1,
+- table->data + seq_index[i],
+- (seq_index[i + 1] - seq_index[i]) / row_stride,
+- sizeof (guint16) * row_stride,
+- compare_seq);
+- if (seq) {
+- if (i == n_compose - 1)
+- break;
+- else
+- return TRUE;
+- }
+- }
+- }
+- if (!seq) {
+- return FALSE;
+- } else {
+- int index = seq[row_stride - 2];
+- int length = seq[row_stride - 1];
+- int j;
+- if (compose_finish)
+- *compose_finish = TRUE;
+- if (output_chars) {
+- *output_chars = g_new (gunichar, length + 1);
+- for (j = 0; j < length; j++)
+- (*output_chars)[j] = table->priv->data2[index + j];
+- (*output_chars)[length] = 0;
+- }
+-
+- // g_debug ("U+%04X\n", value);
+- return TRUE;
+- }
+- } else {
+- for (i = n_compose - 1; i < table->max_seq_len; i++) {
+- row_stride = i + 1;
+-
+- if (seq_index[i + 1] - seq_index[i] > 0) {
+- seq = bsearch (compose_buffer + 1,
+- table->data + seq_index[i],
+- (seq_index[i + 1] - seq_index[i]) / row_stride,
+- sizeof (guint16) * row_stride,
+- compare_seq);
+-
+- if (seq) {
+- if (i == n_compose - 1)
+- break;
+- else
+- return TRUE;
+- }
+- }
+- }
+- if (!seq) {
+- return FALSE;
+- } else {
+- if (compose_finish)
+- *compose_finish = TRUE;
+- if (output_chars) {
+- *output_chars = g_new (gunichar, 2);
+- (*output_chars)[0] = seq[row_stride - 1];
+- (*output_chars)[1] = 0;
+- }
+-
+- // g_debug ("U+%04X\n", value);
+- return TRUE;
+- }
+- }
+-
+- g_assert_not_reached ();
+-}
+-
+-
+-/* Checks if a keysym is a dead key. Dead key keysym values are defined in
+- * ../gdk/gdkkeysyms.h and the first is GDK_KEY_dead_grave. As X.Org is updated,
+- * more dead keys are added and we need to update the upper limit.
+- * Currently, the upper limit is GDK_KEY_dead_dasia+1. The +1 has to do with
+- * a temporary issue in the X.Org header files.
+- * In future versions it will be just the keysym (no +1).
+- */
+-#define IS_DEAD_KEY(k) \
+- ((k) >= IBUS_KEY_dead_grave && (k) <= (IBUS_KEY_dead_dasia + 1))
+-
+-/* This function receives a sequence of Unicode characters and tries to
+- * normalize it (NFC). We check for the case the the resulting string
+- * has length 1 (single character).
+- * NFC normalisation normally rearranges diacritic marks, unless these
+- * belong to the same Canonical Combining Class.
+- * If they belong to the same canonical combining class, we produce all
+- * permutations of the diacritic marks, then attempt to normalize.
+- */
+-static gboolean
+-check_normalize_nfc (gunichar* combination_buffer, gint n_compose)
+-{
+- gunichar combination_buffer_temp[IBUS_MAX_COMPOSE_LEN];
+- gchar *combination_utf8_temp = NULL;
+- gchar *nfc_temp = NULL;
+- gint n_combinations;
+- gunichar temp_swap;
+- gint i;
+-
+- n_combinations = 1;
+-
+- CHECK_COMPOSE_BUFFER_LENGTH (n_compose);
+-
+- for (i = 1; i < n_compose; i++ )
+- n_combinations *= i;
+-
+- /* Xorg reuses dead_tilde for the perispomeni diacritic mark.
+- * We check if base character belongs to Greek Unicode block,
+- * and if so, we replace tilde with perispomeni. */
+- if (combination_buffer[0] >= 0x390 && combination_buffer[0] <= 0x3FF) {
+- for (i = 1; i < n_compose; i++ )
+- if (combination_buffer[i] == 0x303)
+- combination_buffer[i] = 0x342;
+- }
+-
+- memcpy (combination_buffer_temp,
+- combination_buffer,
+- IBUS_MAX_COMPOSE_LEN * sizeof (gunichar) );
+-
+- for (i = 0; i < n_combinations; i++ ) {
+- g_unicode_canonical_ordering (combination_buffer_temp, n_compose);
+- combination_utf8_temp = g_ucs4_to_utf8 (combination_buffer_temp, -1, NULL, NULL, NULL);
+- nfc_temp = g_utf8_normalize (combination_utf8_temp, -1, G_NORMALIZE_NFC);
+-
+- if (g_utf8_strlen (nfc_temp, -1) == 1) {
+- memcpy (combination_buffer,
+- combination_buffer_temp,
+- IBUS_MAX_COMPOSE_LEN * sizeof (gunichar) );
+-
+- g_free (combination_utf8_temp);
+- g_free (nfc_temp);
+-
+- return TRUE;
+- }
+-
+- g_free (combination_utf8_temp);
+- g_free (nfc_temp);
+-
+- if (n_compose > 2) {
+- gint j = i % (n_compose - 1) + 1;
+- gint k = (i+1) % (n_compose - 1) + 1;
+- if (j >= IBUS_MAX_COMPOSE_LEN) {
+- g_warning ("j >= IBUS_MAX_COMPOSE_LEN for " \
+- "combination_buffer_temp");
+- break;
+- }
+- if (k >= IBUS_MAX_COMPOSE_LEN) {
+- g_warning ("k >= IBUS_MAX_COMPOSE_LEN for " \
+- "combination_buffer_temp");
+- break;
+- }
+- temp_swap = combination_buffer_temp[j];
+- combination_buffer_temp[j] = combination_buffer_temp[k];
+- combination_buffer_temp[k] = temp_swap;
+- }
+- else
+- break;
+- }
+-
+- return FALSE;
+-}
+-
+-gboolean
+-ibus_check_algorithmically (const guint16 *compose_buffer,
+- gint n_compose,
+- gunichar *output_char)
+-
+-{
+- gint i;
+- gunichar combination_buffer[IBUS_MAX_COMPOSE_LEN];
+- gchar *combination_utf8, *nfc;
+-
+- if (output_char)
+- *output_char = 0;
+-
+- CHECK_COMPOSE_BUFFER_LENGTH (n_compose);
+-
+- if (n_compose >= IBUS_MAX_COMPOSE_LEN)
+- return FALSE;
+-
+- for (i = 0; i < n_compose && IS_DEAD_KEY (compose_buffer[i]); i++)
+- ;
+- if (i == n_compose)
+- return TRUE;
+-
+- if (i > 0 && i == n_compose - 1) {
+- combination_buffer[0] = ibus_keyval_to_unicode (compose_buffer[i]);
+- combination_buffer[n_compose] = 0;
+- i--;
+- while (i >= 0) {
+- combination_buffer[i+1] = ibus_keysym_to_unicode (compose_buffer[i],
+- TRUE);
+- if (!combination_buffer[i+1]) {
+- combination_buffer[i+1] =
+- ibus_keyval_to_unicode (compose_buffer[i]);
+- }
+- i--;
+- }
+-
+- /* If the buffer normalizes to a single character,
+- * then modify the order of combination_buffer accordingly, if necessary,
+- * and return TRUE.
+- */
+- if (check_normalize_nfc (combination_buffer, n_compose)) {
+- combination_utf8 = g_ucs4_to_utf8 (combination_buffer, -1, NULL, NULL, NULL);
+- nfc = g_utf8_normalize (combination_utf8, -1, G_NORMALIZE_NFC);
+-
+- if (output_char)
+- *output_char = g_utf8_get_char (nfc);
+-
+- g_free (combination_utf8);
+- g_free (nfc);
+-
+- return TRUE;
+- }
+- }
+-
+- return FALSE;
+-}
+
+ static gboolean
+ no_sequence_matches (IBusEngineSimple *simple,
+@@ -1184,34 +703,71 @@ ibus_engine_simple_check_all_compose_table (IBusEngineSimple *simple,
+ gint n_compose)
+ {
+ IBusEngineSimplePrivate *priv = simple->priv;
+- gboolean compose_finish;
+- gunichar output_char;
++ gboolean compose_finish = FALSE;
++ gboolean compose_match = FALSE;
++ GString *output = g_string_new ("");
++ gboolean does_hit = FALSE;
++ gboolean is_32bit = FALSE;
++ GSList *tmp_list;
+ gunichar *output_chars = NULL;
+- gchar *output_str = NULL;
+- GError *error = NULL;
+- GSList *list = global_tables;
++ gunichar output_char;
+
+- while (list) {
+- if (check_table (simple,
+- (IBusComposeTableEx *)list->data,
++ G_LOCK (global_tables);
++ tmp_list = global_tables;
++ while (tmp_list) {
++ is_32bit = FALSE;
++ if (ibus_compose_table_check (
++ (IBusComposeTableEx *)tmp_list->data,
++ priv->compose_buffer,
+ n_compose,
+- FALSE)) {
+- return TRUE;
++ &compose_finish,
++ &compose_match,
++ output,
++ is_32bit)) {
++ does_hit = TRUE;
++ break;
+ }
+- if (check_table (simple,
+- (IBusComposeTableEx *)list->data,
++ is_32bit = TRUE;
++ if (ibus_compose_table_check (
++ (IBusComposeTableEx *)tmp_list->data,
++ priv->compose_buffer,
+ n_compose,
+- TRUE)) {
+- return TRUE;
++ &compose_finish,
++ &compose_match,
++ output,
++ is_32bit)) {
++ does_hit = TRUE;
++ break;
+ }
+- list = list->next;
++ tmp_list = tmp_list->next;
+ }
++ G_UNLOCK (global_tables);
+
+- if (ibus_check_compact_table (&ibus_compose_table_compact,
+- priv->compose_buffer,
+- n_compose,
+- &compose_finish,
+- &output_chars)) {
++ if (does_hit) {
++ if (compose_finish) {
++ if (compose_match) {
++ if (is_32bit) {
++ ibus_engine_simple_commit_str (simple, output->str);
++ } else {
++ ibus_engine_simple_commit_char (simple,
++ g_utf8_get_char (output->str));
++ }
++ }
++ } else if (compose_match) {
++ priv->tentative_match = g_utf8_get_char (output->str);
++ priv->tentative_match_len = n_compose;
++ }
++ ibus_engine_simple_update_preedit_text (simple);
++ g_string_free (output, TRUE);
++ return TRUE;
++ }
++ g_string_free (output, TRUE);
++
++ if (ibus_compose_table_compact_check (&ibus_compose_table_compact,
++ priv->compose_buffer,
++ n_compose,
++ &compose_finish,
++ &output_chars)) {
+ if (compose_finish) {
+ ibus_engine_simple_commit_char (simple, *output_chars);
+ g_free (output_chars);
+@@ -1220,28 +776,29 @@ ibus_engine_simple_check_all_compose_table (IBusEngineSimple *simple,
+ ibus_engine_simple_update_preedit_text (simple);
+ return TRUE;
+ }
+- if (ibus_check_compact_table (&ibus_compose_table_compact_32bit,
+- priv->compose_buffer,
+- n_compose,
+- &compose_finish,
+- &output_chars)) {
++ if (ibus_compose_table_compact_check (&ibus_compose_table_compact_32bit,
++ priv->compose_buffer,
++ n_compose,
++ &compose_finish,
++ &output_chars)) {
+ if (compose_finish) {
+- output_str = g_ucs4_to_utf8 (output_chars, -1, NULL, NULL, &error);
+- if (output_str) {
+- ibus_engine_simple_commit_str (simple, output_str);
+- g_free (output_str);
++ GError *error = NULL;
++ char *str = g_ucs4_to_utf8 (output_chars, -1, NULL, NULL, &error);
++ if (str) {
++ ibus_engine_simple_commit_str (simple, str);
++ g_free (str);
+ } else {
+ g_warning ("Failed to output multiple characters: %s",
+ error->message);
+ g_error_free (error);
+ }
+- g_free (output_chars);
+ priv->compose_buffer[0] = 0;
+ }
+-
++ g_free (output_chars);
+ ibus_engine_simple_update_preedit_text (simple);
+ return TRUE;
+ }
++ g_assert (!output_chars);
+ if (ibus_check_algorithmically (priv->compose_buffer,
+ n_compose,
+ &output_char)) {
+diff --git a/src/ibusenginesimpleprivate.h b/src/ibusenginesimpleprivate.h
+index 508d9344..47f2a3d4 100644
+--- a/src/ibusenginesimpleprivate.h
++++ b/src/ibusenginesimpleprivate.h
+@@ -37,15 +37,30 @@ struct _IBusComposeTablePrivate
+ gsize second_size;
+ };
+
++struct _IBusComposeTableCompactPrivate
++{
++ const guint32 *data2;
++};
++
+ gboolean ibus_check_algorithmically (const guint16 *compose_buffer,
+ gint n_compose,
+ gunichar *output);
+-gboolean ibus_check_compact_table (const IBusComposeTableCompactEx
++gboolean ibus_compose_table_check (const IBusComposeTableEx *table,
++ guint16 *compose_buffer,
++ gint n_compose,
++ gboolean *compose_finish,
++ gboolean *compose_match,
++ GString *output,
++ gboolean is_32bit);
++gboolean ibus_compose_table_compact_check
++ (const IBusComposeTableCompactEx
+ *table,
+ guint16 *compose_buffer,
+ gint n_compose,
+ gboolean *compose_finish,
+ gunichar **output_chars);
++gunichar ibus_keysym_to_unicode (guint16 keysym,
++ gboolean combining);
+
+ G_END_DECLS
+
+diff --git a/src/tests/Makefile.am b/src/tests/Makefile.am
+index 0a2e523c..13c06eb4 100644
+--- a/src/tests/Makefile.am
++++ b/src/tests/Makefile.am
+@@ -3,7 +3,7 @@
+ # ibus - The Input Bus
+ #
+ # Copyright (c) 2007-2015 Peng Huang <shawn.p.huang@gmail.com>
+-# Copyright (c) 2015-2019 Takao Fujiwara <takao.fujiwara1@gmail.com>
++# Copyright (c) 2015-2021 Takao Fujiwara <takao.fujiwara1@gmail.com>
+ # Copyright (c) 2007-2018 Red Hat, Inc.
+ #
+ # This library is free software; you can redistribute it and/or
+@@ -30,6 +30,7 @@ AM_CPPFLAGS = \
+ @GLIB2_CFLAGS@ \
+ @GIO2_CFLAGS@ \
+ -DIBUS_DISABLE_DEPRECATION_WARNINGS \
++ -DX11_DATA_PREFIX=\"$(X11_PREFIX)\" \
+ -I$(top_srcdir)/src \
+ -I$(top_builddir)/src \
+ $(NULL)
+diff --git a/src/tests/ibus-compose.c b/src/tests/ibus-compose.c
+index 81bfc69b..0be01d27 100644
+--- a/src/tests/ibus-compose.c
++++ b/src/tests/ibus-compose.c
+@@ -6,6 +6,7 @@
+ #define GREEN "\033[0;32m"
+ #define RED "\033[0;31m"
+ #define NC "\033[0m"
++#define X11_DATADIR X11_DATA_PREFIX "/share/X11/locale"
+
+ IBusBus *m_bus;
+ gchar *m_compose_file;
+@@ -35,7 +36,7 @@ get_compose_path ()
+ break;
+ if (g_strcmp0 (*l, "C") == 0)
+ break;
+- compose_path = g_build_filename ("/usr/share/X11/locale",
++ compose_path = g_build_filename (X11_DATADIR,
+ *l,
+ "Compose",
+ NULL);
+@@ -155,7 +156,7 @@ set_engine_cb (GObject *object, GAsyncResult *res, gpointer data)
+ for (i = 0;
+ i < (m_compose_table->n_seqs * index_stride);
+ i += index_stride) {
+- for (j = i; j < i + (index_stride - 1); j++) {
++ for (j = i; j < i + (index_stride - 2); j++) {
+ guint keyval = m_compose_table->data[j];
+ guint keycode = 0;
+ guint modifiers = 0;
+@@ -175,7 +176,7 @@ set_engine_cb (GObject *object, GAsyncResult *res, gpointer data)
+ for (i = 0;
+ i < (priv->first_n_seqs * index_stride);
+ i += index_stride) {
+- for (j = i; j < i + (index_stride - 1); j++) {
++ for (j = i; j < i + (index_stride - 2); j++) {
+ guint keyval = priv->data_first[j];
+ guint keycode = 0;
+ guint modifiers = 0;
+--
+2.28.0
+
+From 2fc1a028aa697f1320d85a76548cde12894bf9ab Mon Sep 17 00:00:00 2001
+From: fujiwarat <takao.fujiwara1@gmail.com>
+Date: Mon, 26 Jul 2021 22:52:18 +0900
+Subject: [PATCH 2/6] src/ibusenginesimple: Multi_key to 0xB7
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Use · instead of ⎄ to display Multi_key in pre-edit.
+
+BUG=https://gitlab.gnome.org/GNOME/gtk/-/issues/3669
+BUG=https://gitlab.gnome.org/GNOME/gtk/-/merge_requests/3220
+---
+ src/ibuscomposetable.c | 8 ++++++-
+ src/ibusenginesimple.c | 49 ++++++++++++++++++++----------------------
+ 2 files changed, 30 insertions(+), 27 deletions(-)
+
+diff --git a/src/ibuscomposetable.c b/src/ibuscomposetable.c
+index f85177a6..4ebf119b 100644
+--- a/src/ibuscomposetable.c
++++ b/src/ibuscomposetable.c
+@@ -1846,7 +1846,13 @@ ibus_keysym_to_unicode (guint16 keysym,
+ CASE_COMBINE (tilde, 0x0303, 0x007E);
+ CASE_COMBINE (voiced_sound, 0x3099, 0x309B);
+ case IBUS_KEY_Multi_key:
+- return 0x2384;
++ /* We only show the Compose key visibly when it is the
++ * only glyph in the preedit, or when it occurs in the
++ * middle of the sequence. Sadly, the official character,
++ * U+2384, COMPOSITION SYMBOL, is bit too distracting, so
++ * we use U+00B7, MIDDLE DOT.
++ */
++ return 0x00B7;
+ default:;
+ }
+ return 0x0;
+diff --git a/src/ibusenginesimple.c b/src/ibusenginesimple.c
+index 4644620b..57ca10c4 100644
+--- a/src/ibusenginesimple.c
++++ b/src/ibusenginesimple.c
+@@ -289,32 +289,27 @@ ibus_engine_simple_update_preedit_text (IBusEngineSimple *simple)
+ {
+ IBusEngineSimplePrivate *priv = simple->priv;
+
+- gunichar outbuf[COMPOSE_BUFFER_SIZE + 1];
+- int len = 0;
++ GString *s = g_string_new ("");
+
+ if (priv->in_hex_sequence || priv->in_emoji_sequence) {
+ int hexchars = 0;
+
+ if (priv->in_hex_sequence)
+- outbuf[0] = L'u';
++ g_string_append_c (s, 'u');
+ else
+- outbuf[0] = L'@';
+-
+- len = 1;
++ g_string_append_c (s, '@');
+
+ while (priv->compose_buffer[hexchars] != 0) {
+- outbuf[len] = ibus_keyval_to_unicode (
+- priv->compose_buffer[hexchars]);
+- ++len;
+- ++hexchars;
++ g_string_append_unichar(
++ s,
++ ibus_keyval_to_unicode (priv->compose_buffer[hexchars++])
++ );
+ }
+-
+- g_assert (len <= COMPOSE_BUFFER_SIZE);
+ } else if (priv->tentative_match) {
+- outbuf[len++] = priv->tentative_match;
++ g_string_append_unichar(s, priv->tentative_match);
+ } else if (priv->tentative_emoji && *priv->tentative_emoji) {
+ IBusText *text = ibus_text_new_from_string (priv->tentative_emoji);
+- len = strlen (priv->tentative_emoji);
++ int len = strlen (priv->tentative_emoji);
+ ibus_text_append_attribute (text,
+ IBUS_ATTR_TYPE_UNDERLINE, IBUS_ATTR_UNDERLINE_SINGLE, 0, len);
+ ibus_engine_update_preedit_text ((IBusEngine *)simple, text, len, TRUE);
+@@ -324,31 +319,33 @@ ibus_engine_simple_update_preedit_text (IBusEngineSimple *simple)
+ while (priv->compose_buffer[hexchars] != 0) {
+ guint16 keysym = priv->compose_buffer[hexchars];
+ gunichar unichar = ibus_keysym_to_unicode (keysym, FALSE);
+- if (unichar > 0)
+- outbuf[len] = unichar;
+- else
+- outbuf[len] = ibus_keyval_to_unicode (keysym);
+- if (!outbuf[len]) {
++ if (unichar > 0) {
++ g_string_append_unichar(s, unichar);
++ } else {
++ unichar = ibus_keyval_to_unicode (keysym);
++ g_string_append_unichar(s, unichar);
++ }
++ if (!unichar) {
+ g_warning (
+ "Not found alternative character of compose key 0x%X",
+ priv->compose_buffer[hexchars]);
+ }
+- ++len;
+ ++hexchars;
+ }
+- g_assert (len <= IBUS_MAX_COMPOSE_LEN);
+ }
+
+- outbuf[len] = L'\0';
+- if (len == 0) {
++ if (s->len == 0) {
+ ibus_engine_hide_preedit_text ((IBusEngine *)simple);
+- }
+- else {
+- IBusText *text = ibus_text_new_from_ucs4 (outbuf);
++ } else if (s->len >= G_MAXINT) {
++ g_warning ("%s is too long compose length: %lu", s->str, s->len);
++ } else {
++ int len = (int)s->len;
++ IBusText *text = ibus_text_new_from_string (s->str);
+ ibus_text_append_attribute (text,
+ IBUS_ATTR_TYPE_UNDERLINE, IBUS_ATTR_UNDERLINE_SINGLE, 0, len);
+ ibus_engine_update_preedit_text ((IBusEngine *)simple, text, len, TRUE);
+ }
++ g_string_free (s, TRUE);
+ }
+
+
+--
+2.28.0
+
+From 3e2609e68c9107ce7c65e2d5876bfdc9f0f8c854 Mon Sep 17 00:00:00 2001
+From: fujiwarat <takao.fujiwara1@gmail.com>
+Date: Mon, 26 Jul 2021 22:52:21 +0900
+Subject: [PATCH 3/6] src: Update ibuskeysyms.h for latest dead keys
+
+---
+ src/ibuskeysyms.h | 186 +++++++++++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 185 insertions(+), 1 deletion(-)
+
+diff --git a/src/ibuskeysyms.h b/src/ibuskeysyms.h
+index d35eb4a7..2fb2ea19 100644
+--- a/src/ibuskeysyms.h
++++ b/src/ibuskeysyms.h
+@@ -274,6 +274,10 @@
+ #define IBUS_KEY_dead_invertedbreve 0xfe6d
+ #define IBUS_KEY_dead_belowcomma 0xfe6e
+ #define IBUS_KEY_dead_currency 0xfe6f
++#define IBUS_KEY_dead_lowline 0xfe90
++#define IBUS_KEY_dead_aboveverticalline 0xfe91
++#define IBUS_KEY_dead_belowverticalline 0xfe92
++#define IBUS_KEY_dead_longsolidusoverlay 0xfe93
+ #define IBUS_KEY_dead_a 0xfe80
+ #define IBUS_KEY_dead_A 0xfe81
+ #define IBUS_KEY_dead_e 0xfe82
+@@ -1201,6 +1205,7 @@
+ #define IBUS_KEY_leftdoublequotemark 0xad2
+ #define IBUS_KEY_rightdoublequotemark 0xad3
+ #define IBUS_KEY_prescription 0xad4
++#define IBUS_KEY_permille 0xad5
+ #define IBUS_KEY_minutes 0xad6
+ #define IBUS_KEY_seconds 0xad7
+ #define IBUS_KEY_latincross 0xad9
+@@ -1633,8 +1638,8 @@
+ #define IBUS_KEY_ocaron 0x10001d2
+ #define IBUS_KEY_obarred 0x1000275
+ #define IBUS_KEY_SCHWA 0x100018f
+-#define IBUS_KEY_EZH 0x10001b7
+ #define IBUS_KEY_schwa 0x1000259
++#define IBUS_KEY_EZH 0x10001b7
+ #define IBUS_KEY_ezh 0x1000292
+ #define IBUS_KEY_Lbelowdot 0x1001e36
+ #define IBUS_KEY_lbelowdot 0x1001e37
+@@ -2121,5 +2126,184 @@
+ #define IBUS_KEY_Sinh_ruu2 0x1000df2
+ #define IBUS_KEY_Sinh_luu2 0x1000df3
+ #define IBUS_KEY_Sinh_kunddaliya 0x1000df4
++#define IBUS_KEY_ModeLock 0x1008ff01
++#define IBUS_KEY_MonBrightnessUp 0x1008ff02
++#define IBUS_KEY_MonBrightnessDown 0x1008ff03
++#define IBUS_KEY_KbdLightOnOff 0x1008ff04
++#define IBUS_KEY_KbdBrightnessUp 0x1008ff05
++#define IBUS_KEY_KbdBrightnessDown 0x1008ff06
++#define IBUS_KEY_Standby 0x1008ff10
++#define IBUS_KEY_AudioLowerVolume 0x1008ff11
++#define IBUS_KEY_AudioMute 0x1008ff12
++#define IBUS_KEY_AudioRaiseVolume 0x1008ff13
++#define IBUS_KEY_AudioPlay 0x1008ff14
++#define IBUS_KEY_AudioStop 0x1008ff15
++#define IBUS_KEY_AudioPrev 0x1008ff16
++#define IBUS_KEY_AudioNext 0x1008ff17
++#define IBUS_KEY_HomePage 0x1008ff18
++#define IBUS_KEY_Mail 0x1008ff19
++#define IBUS_KEY_Start 0x1008ff1a
++#define IBUS_KEY_Search 0x1008ff1b
++#define IBUS_KEY_AudioRecord 0x1008ff1c
++#define IBUS_KEY_Calculator 0x1008ff1d
++#define IBUS_KEY_Memo 0x1008ff1e
++#define IBUS_KEY_ToDoList 0x1008ff1f
++#define IBUS_KEY_Calendar 0x1008ff20
++#define IBUS_KEY_PowerDown 0x1008ff21
++#define IBUS_KEY_ContrastAdjust 0x1008ff22
++#define IBUS_KEY_RockerUp 0x1008ff23
++#define IBUS_KEY_RockerDown 0x1008ff24
++#define IBUS_KEY_RockerEnter 0x1008ff25
++#define IBUS_KEY_Back 0x1008ff26
++#define IBUS_KEY_Forward 0x1008ff27
++#define IBUS_KEY_Stop 0x1008ff28
++#define IBUS_KEY_Refresh 0x1008ff29
++#define IBUS_KEY_PowerOff 0x1008ff2a
++#define IBUS_KEY_WakeUp 0x1008ff2b
++#define IBUS_KEY_Eject 0x1008ff2c
++#define IBUS_KEY_ScreenSaver 0x1008ff2d
++#define IBUS_KEY_WWW 0x1008ff2e
++#define IBUS_KEY_Sleep 0x1008ff2f
++#define IBUS_KEY_Favorites 0x1008ff30
++#define IBUS_KEY_AudioPause 0x1008ff31
++#define IBUS_KEY_AudioMedia 0x1008ff32
++#define IBUS_KEY_MyComputer 0x1008ff33
++#define IBUS_KEY_VendorHome 0x1008ff34
++#define IBUS_KEY_LightBulb 0x1008ff35
++#define IBUS_KEY_Shop 0x1008ff36
++#define IBUS_KEY_History 0x1008ff37
++#define IBUS_KEY_OpenURL 0x1008ff38
++#define IBUS_KEY_AddFavorite 0x1008ff39
++#define IBUS_KEY_HotLinks 0x1008ff3a
++#define IBUS_KEY_BrightnessAdjust 0x1008ff3b
++#define IBUS_KEY_Finance 0x1008ff3c
++#define IBUS_KEY_Community 0x1008ff3d
++#define IBUS_KEY_AudioRewind 0x1008ff3e
++#define IBUS_KEY_BackForward 0x1008ff3f
++#define IBUS_KEY_Launch0 0x1008ff40
++#define IBUS_KEY_Launch1 0x1008ff41
++#define IBUS_KEY_Launch2 0x1008ff42
++#define IBUS_KEY_Launch3 0x1008ff43
++#define IBUS_KEY_Launch4 0x1008ff44
++#define IBUS_KEY_Launch5 0x1008ff45
++#define IBUS_KEY_Launch6 0x1008ff46
++#define IBUS_KEY_Launch7 0x1008ff47
++#define IBUS_KEY_Launch8 0x1008ff48
++#define IBUS_KEY_Launch9 0x1008ff49
++#define IBUS_KEY_LaunchA 0x1008ff4a
++#define IBUS_KEY_LaunchB 0x1008ff4b
++#define IBUS_KEY_LaunchC 0x1008ff4c
++#define IBUS_KEY_LaunchD 0x1008ff4d
++#define IBUS_KEY_LaunchE 0x1008ff4e
++#define IBUS_KEY_LaunchF 0x1008ff4f
++#define IBUS_KEY_ApplicationLeft 0x1008ff50
++#define IBUS_KEY_ApplicationRight 0x1008ff51
++#define IBUS_KEY_Book 0x1008ff52
++#define IBUS_KEY_CD 0x1008ff53
++#define IBUS_KEY_WindowClear 0x1008ff55
++#define IBUS_KEY_Close 0x1008ff56
++#define IBUS_KEY_Copy 0x1008ff57
++#define IBUS_KEY_Cut 0x1008ff58
++#define IBUS_KEY_Display 0x1008ff59
++#define IBUS_KEY_DOS 0x1008ff5a
++#define IBUS_KEY_Documents 0x1008ff5b
++#define IBUS_KEY_Excel 0x1008ff5c
++#define IBUS_KEY_Explorer 0x1008ff5d
++#define IBUS_KEY_Game 0x1008ff5e
++#define IBUS_KEY_Go 0x1008ff5f
++#define IBUS_KEY_iTouch 0x1008ff60
++#define IBUS_KEY_LogOff 0x1008ff61
++#define IBUS_KEY_Market 0x1008ff62
++#define IBUS_KEY_Meeting 0x1008ff63
++#define IBUS_KEY_MenuKB 0x1008ff65
++#define IBUS_KEY_MenuPB 0x1008ff66
++#define IBUS_KEY_MySites 0x1008ff67
++#define IBUS_KEY_New 0x1008ff68
++#define IBUS_KEY_News 0x1008ff69
++#define IBUS_KEY_OfficeHome 0x1008ff6a
++#define IBUS_KEY_Open 0x1008ff6b
++#define IBUS_KEY_Option 0x1008ff6c
++#define IBUS_KEY_Paste 0x1008ff6d
++#define IBUS_KEY_Phone 0x1008ff6e
++#define IBUS_KEY_Reply 0x1008ff72
++#define IBUS_KEY_Reload 0x1008ff73
++#define IBUS_KEY_RotateWindows 0x1008ff74
++#define IBUS_KEY_RotationPB 0x1008ff75
++#define IBUS_KEY_RotationKB 0x1008ff76
++#define IBUS_KEY_Save 0x1008ff77
++#define IBUS_KEY_ScrollUp 0x1008ff78
++#define IBUS_KEY_ScrollDown 0x1008ff79
++#define IBUS_KEY_ScrollClick 0x1008ff7a
++#define IBUS_KEY_Send 0x1008ff7b
++#define IBUS_KEY_Spell 0x1008ff7c
++#define IBUS_KEY_SplitScreen 0x1008ff7d
++#define IBUS_KEY_Support 0x1008ff7e
++#define IBUS_KEY_TaskPane 0x1008ff7f
++#define IBUS_KEY_Terminal 0x1008ff80
++#define IBUS_KEY_Tools 0x1008ff81
++#define IBUS_KEY_Travel 0x1008ff82
++#define IBUS_KEY_UserPB 0x1008ff84
++#define IBUS_KEY_User1KB 0x1008ff85
++#define IBUS_KEY_User2KB 0x1008ff86
++#define IBUS_KEY_Video 0x1008ff87
++#define IBUS_KEY_WheelButton 0x1008ff88
++#define IBUS_KEY_Word 0x1008ff89
++#define IBUS_KEY_Xfer 0x1008ff8a
++#define IBUS_KEY_ZoomIn 0x1008ff8b
++#define IBUS_KEY_ZoomOut 0x1008ff8c
++#define IBUS_KEY_Away 0x1008ff8d
++#define IBUS_KEY_Messenger 0x1008ff8e
++#define IBUS_KEY_WebCam 0x1008ff8f
++#define IBUS_KEY_MailForward 0x1008ff90
++#define IBUS_KEY_Pictures 0x1008ff91
++#define IBUS_KEY_Music 0x1008ff92
++#define IBUS_KEY_Battery 0x1008ff93
++#define IBUS_KEY_Bluetooth 0x1008ff94
++#define IBUS_KEY_WLAN 0x1008ff95
++#define IBUS_KEY_UWB 0x1008ff96
++#define IBUS_KEY_AudioForward 0x1008ff97
++#define IBUS_KEY_AudioRepeat 0x1008ff98
++#define IBUS_KEY_AudioRandomPlay 0x1008ff99
++#define IBUS_KEY_Subtitle 0x1008ff9a
++#define IBUS_KEY_AudioCycleTrack 0x1008ff9b
++#define IBUS_KEY_CycleAngle 0x1008ff9c
++#define IBUS_KEY_FrameBack 0x1008ff9d
++#define IBUS_KEY_FrameForward 0x1008ff9e
++#define IBUS_KEY_Time 0x1008ff9f
++#define IBUS_KEY_SelectButton 0x1008ffa0
++#define IBUS_KEY_View 0x1008ffa1
++#define IBUS_KEY_TopMenu 0x1008ffa2
++#define IBUS_KEY_Red 0x1008ffa3
++#define IBUS_KEY_Green 0x1008ffa4
++#define IBUS_KEY_Yellow 0x1008ffa5
++#define IBUS_KEY_Blue 0x1008ffa6
++#define IBUS_KEY_Suspend 0x1008ffa7
++#define IBUS_KEY_Hibernate 0x1008ffa8
++#define IBUS_KEY_TouchpadToggle 0x1008ffa9
++#define IBUS_KEY_TouchpadOn 0x1008ffb0
++#define IBUS_KEY_TouchpadOff 0x1008ffb1
++#define IBUS_KEY_AudioMicMute 0x1008ffb2
++#define IBUS_KEY_Keyboard 0x1008ffb3
++#define IBUS_KEY_WWAN 0x1008ffb4
++#define IBUS_KEY_RFKill 0x1008ffb5
++#define IBUS_KEY_AudioPreset 0x1008ffb6
++#define IBUS_KEY_Switch_VT_1 0x1008fe01
++#define IBUS_KEY_Switch_VT_2 0x1008fe02
++#define IBUS_KEY_Switch_VT_3 0x1008fe03
++#define IBUS_KEY_Switch_VT_4 0x1008fe04
++#define IBUS_KEY_Switch_VT_5 0x1008fe05
++#define IBUS_KEY_Switch_VT_6 0x1008fe06
++#define IBUS_KEY_Switch_VT_7 0x1008fe07
++#define IBUS_KEY_Switch_VT_8 0x1008fe08
++#define IBUS_KEY_Switch_VT_9 0x1008fe09
++#define IBUS_KEY_Switch_VT_10 0x1008fe0a
++#define IBUS_KEY_Switch_VT_11 0x1008fe0b
++#define IBUS_KEY_Switch_VT_12 0x1008fe0c
++#define IBUS_KEY_Ungrab 0x1008fe20
++#define IBUS_KEY_ClearGrab 0x1008fe21
++#define IBUS_KEY_Next_VMode 0x1008fe22
++#define IBUS_KEY_Prev_VMode 0x1008fe23
++#define IBUS_KEY_LogWindowTree 0x1008fe24
++#define IBUS_KEY_LogGrabInfo 0x1008fe25
+
+ #endif /* __IBUS_KEYSYMS_H__ */
+--
+2.28.0
+
+From df495660a6da492fe0cac8f7fe748f25a1c3217c Mon Sep 17 00:00:00 2001
+From: fujiwarat <takao.fujiwara1@gmail.com>
+Date: Mon, 26 Jul 2021 22:52:23 +0900
+Subject: [PATCH 4/6] src/ibusenginesimple: Change gint to int
+
+---
+ src/ibuscomposetable.c | 128 +++++++++++++++++-----------------
+ src/ibusenginesimple.c | 58 +++++++--------
+ src/ibusenginesimpleprivate.h | 6 +-
+ 3 files changed, 96 insertions(+), 96 deletions(-)
+
+diff --git a/src/ibuscomposetable.c b/src/ibuscomposetable.c
+index 4ebf119b..aa58f136 100644
+--- a/src/ibuscomposetable.c
++++ b/src/ibuscomposetable.c
+@@ -47,7 +47,7 @@
+ typedef struct {
+ gunichar *sequence;
+ gunichar *values;
+- gchar *comment;
++ char *comment;
+ } IBusComposeData;
+
+
+@@ -85,7 +85,7 @@ unichar_length (gunichar *uni_array)
+
+
+ static gboolean
+-is_codepoint (const gchar *str)
++is_codepoint (const char *str)
+ {
+ int i;
+
+@@ -104,11 +104,11 @@ is_codepoint (const gchar *str)
+
+ static gboolean
+ parse_compose_value (IBusComposeData *compose_data,
+- const gchar *val,
+- const gchar *line)
++ const char *val,
++ const char *line)
+ {
+- gchar *head, *end, *p;
+- gchar *ustr = NULL;
++ char *head, *end, *p;
++ char *ustr = NULL;
+ gunichar *uchars = NULL, *up;
+ GError *error = NULL;
+ int n_uchars = 0;
+@@ -184,10 +184,10 @@ fail:
+
+ static int
+ parse_compose_sequence (IBusComposeData *compose_data,
+- const gchar *seq,
+- const gchar *line)
++ const char *seq,
++ const char *line)
+ {
+- gchar **words = g_strsplit (seq, "<", -1);
++ char **words = g_strsplit (seq, "<", -1);
+ int i;
+ int n = 0;
+
+@@ -198,9 +198,9 @@ parse_compose_sequence (IBusComposeData *compose_data,
+ }
+
+ for (i = 1; words[i] != NULL; i++) {
+- gchar *start = words[i];
+- gchar *end = index (words[i], '>');
+- gchar *match;
++ char *start = words[i];
++ char *end = index (words[i], '>');
++ char *match;
+ gunichar codepoint;
+
+ if (words[i][0] == '\0')
+@@ -256,18 +256,18 @@ fail:
+ }
+
+
+-static gchar *
+-expand_include_path (const gchar *include_path) {
+- gchar *out = strdup ("");
+- const gchar *head, *i;
+- gchar *former, *o;
++static char *
++expand_include_path (const char *include_path) {
++ char *out = strdup ("");
++ const char *head, *i;
++ char *former, *o;
+
+ for (head = i = include_path; *i; ++i) {
+ /* expand sequence */
+ if (*i == '%') {
+ switch (*(i + 1)) {
+ case 'H': { /* $HOME */
+- const gchar *home = getenv ("HOME");
++ const char *home = getenv ("HOME");
+ if (!home) {
+ g_warning ("while parsing XCompose include target %s, "
+ "%%H replacement failed because HOME is not "
+@@ -325,11 +325,11 @@ fail:
+
+ static void
+ parse_compose_line (GList **compose_list,
+- const gchar *line,
++ const char *line,
+ int *compose_len,
+- gchar **include)
++ char **include)
+ {
+- gchar **components = NULL;
++ char **components = NULL;
+ IBusComposeData *compose_data = NULL;
+ int l;
+
+@@ -395,13 +395,13 @@ fail:
+ }
+
+
+-static gchar *
++static char *
+ get_en_compose_file (void)
+ {
+- gchar * const sys_langs[] = { "en_US.UTF-8", "en_US", "en.UTF-8",
+- "en", NULL };
+- gchar * const *sys_lang = NULL;
+- gchar *path = NULL;
++ char * const sys_langs[] = { "en_US.UTF-8", "en_US", "en.UTF-8",
++ "en", NULL };
++ char * const *sys_lang = NULL;
++ char *path = NULL;
+ for (sys_lang = sys_langs; *sys_lang; sys_lang++) {
+ path = g_build_filename (X11_DATADIR, *sys_lang, "Compose", NULL);
+ if (g_file_test (path, G_FILE_TEST_EXISTS))
+@@ -413,11 +413,11 @@ get_en_compose_file (void)
+
+
+ static GList *
+-ibus_compose_list_parse_file (const gchar *compose_file,
+- int *max_compose_len)
++ibus_compose_list_parse_file (const char *compose_file,
++ int *max_compose_len)
+ {
+- gchar *contents = NULL;
+- gchar **lines = NULL;
++ char *contents = NULL;
++ char **lines = NULL;
+ gsize length = 0;
+ GError *error = NULL;
+ GList *compose_list = NULL;
+@@ -435,14 +435,14 @@ ibus_compose_list_parse_file (const gchar *compose_file,
+ g_free (contents);
+ for (i = 0; lines[i] != NULL; i++) {
+ int compose_len = 0;
+- gchar *include = NULL;
++ char *include = NULL;
+ parse_compose_line (&compose_list, lines[i], &compose_len, &include);
+ if (*max_compose_len < compose_len)
+ *max_compose_len = compose_len;
+ if (include && *include) {
+ GStatBuf buf_include = { 0, };
+ GStatBuf buf_parent = { 0, };
+- gchar *en_compose;
++ char *en_compose;
+ errno = 0;
+ if (g_stat (include, &buf_include)) {
+ g_warning ("Cannot access %s: %s",
+@@ -588,7 +588,7 @@ ibus_compose_list_check_duplicated (GList *compose_list,
+ }
+
+
+-static gint
++static int
+ ibus_compose_data_compare (gpointer a,
+ gpointer b,
+ gpointer data)
+@@ -625,7 +625,7 @@ ibus_compose_list_print (GList *compose_list,
+ int i, j;
+ IBusComposeData *compose_data;
+ int total_size = 0;
+- const gchar *keyval;
++ const char *keyval;
+
+ for (list = compose_list; list != NULL; list = list->next) {
+ compose_data = list->data;
+@@ -682,13 +682,13 @@ ibus_compose_table_data_hash (gconstpointer v,
+ }
+
+
+-static gchar *
++static char *
+ ibus_compose_hash_get_cache_path (guint32 hash)
+ {
+- gchar *basename = NULL;
+- const gchar *cache_dir;
+- gchar *dir = NULL;
+- gchar *path = NULL;
++ char *basename = NULL;
++ const char *cache_dir;
++ char *dir = NULL;
++ char *path = NULL;
+
+ basename = g_strdup_printf ("%08x.cache", hash);
+
+@@ -715,7 +715,7 @@ ibus_compose_hash_get_cache_path (guint32 hash)
+ static GVariant *
+ ibus_compose_table_serialize (IBusComposeTableEx *compose_table)
+ {
+- const gchar *header = IBUS_COMPOSE_TABLE_MAGIC;
++ const char *header = IBUS_COMPOSE_TABLE_MAGIC;
+ const guint16 version = IBUS_COMPOSE_TABLE_VERSION;
+ guint16 max_seq_len;
+ guint16 index_stride;
+@@ -825,7 +825,7 @@ out_serialize:
+ }
+
+
+-static gint
++static int
+ ibus_compose_table_find (gconstpointer data1,
+ gconstpointer data2)
+ {
+@@ -837,8 +837,8 @@ ibus_compose_table_find (gconstpointer data1,
+
+
+ static IBusComposeTableEx *
+-ibus_compose_table_deserialize (const gchar *contents,
+- gsize length)
++ibus_compose_table_deserialize (const char *contents,
++ gsize length)
+ {
+ IBusComposeTableEx *retval = NULL;
+ GVariantType *type;
+@@ -846,7 +846,7 @@ ibus_compose_table_deserialize (const gchar *contents,
+ GVariant *variant_data_32bit_first = NULL;
+ GVariant *variant_data_32bit_second = NULL;
+ GVariant *variant_table = NULL;
+- const gchar *header = NULL;
++ const char *header = NULL;
+ guint16 version = 0;
+ guint16 max_seq_len = 0;
+ guint16 n_seqs = 0;
+@@ -1020,8 +1020,8 @@ ibus_compose_table_load_cache (const gchar *compose_file)
+ {
+ IBusComposeTableEx *retval = NULL;
+ guint32 hash;
+- gchar *path = NULL;
+- gchar *contents = NULL;
++ char *path = NULL;
++ char *contents = NULL;
+ GStatBuf original_buf;
+ GStatBuf cache_buf;
+ gsize length = 0;
+@@ -1063,9 +1063,9 @@ ibus_compose_table_load_cache (const gchar *compose_file)
+ void
+ ibus_compose_table_save_cache (IBusComposeTableEx *compose_table)
+ {
+- gchar *path = NULL;
++ char *path = NULL;
+ GVariant *variant_table = NULL;
+- const gchar *contents = NULL;
++ const char *contents = NULL;
+ GError *error = NULL;
+ gsize length = 0;
+
+@@ -1392,13 +1392,13 @@ compare_seq (const void *key, const void *value)
+ gboolean
+ ibus_compose_table_check (const IBusComposeTableEx *table,
+ guint16 *compose_buffer,
+- gint n_compose,
++ int n_compose,
+ gboolean *compose_finish,
+ gboolean *compose_match,
+ GString *output,
+ gboolean is_32bit)
+ {
+- gint row_stride = table->max_seq_len + 2;
++ int row_stride = table->max_seq_len + 2;
+ guint16 *data_first;
+ int n_seqs;
+ guint16 *seq;
+@@ -1448,7 +1448,7 @@ ibus_compose_table_check (const IBusComposeTableEx *table,
+ gunichar value = 0;
+ int num = 0;
+ int index = 0;
+- gchar *output_str = NULL;
++ char *output_str = NULL;
+ GError *error = NULL;
+
+ if (is_32bit) {
+@@ -1526,15 +1526,15 @@ gboolean
+ ibus_compose_table_compact_check (const IBusComposeTableCompactEx *table,
+ guint16
+ *compose_buffer,
+- gint n_compose,
++ int n_compose,
+ gboolean
+ *compose_finish,
+ gunichar **output_chars)
+ {
+- gint row_stride;
++ int row_stride;
+ guint16 *seq_index;
+ guint16 *seq;
+- gint i;
++ int i;
+
+ if (compose_finish)
+ *compose_finish = FALSE;
+@@ -1654,14 +1654,14 @@ ibus_compose_table_compact_check (const IBusComposeTableCompactEx *table,
+ * permutations of the diacritic marks, then attempt to normalize.
+ */
+ static gboolean
+-check_normalize_nfc (gunichar* combination_buffer, gint n_compose)
++check_normalize_nfc (gunichar* combination_buffer, int n_compose)
+ {
+ gunichar combination_buffer_temp[IBUS_MAX_COMPOSE_LEN];
+- gchar *combination_utf8_temp = NULL;
+- gchar *nfc_temp = NULL;
+- gint n_combinations;
++ char *combination_utf8_temp = NULL;
++ char *nfc_temp = NULL;
++ int n_combinations;
+ gunichar temp_swap;
+- gint i;
++ int i;
+
+ n_combinations = 1;
+
+@@ -1703,8 +1703,8 @@ check_normalize_nfc (gunichar* combination_buffer, gint n_compose)
+ g_free (nfc_temp);
+
+ if (n_compose > 2) {
+- gint j = i % (n_compose - 1) + 1;
+- gint k = (i+1) % (n_compose - 1) + 1;
++ int j = i % (n_compose - 1) + 1;
++ int k = (i+1) % (n_compose - 1) + 1;
+ if (j >= IBUS_MAX_COMPOSE_LEN) {
+ g_warning ("j >= IBUS_MAX_COMPOSE_LEN for " \
+ "combination_buffer_temp");
+@@ -1729,13 +1729,13 @@ check_normalize_nfc (gunichar* combination_buffer, gint n_compose)
+
+ gboolean
+ ibus_check_algorithmically (const guint16 *compose_buffer,
+- gint n_compose,
++ int n_compose,
+ gunichar *output_char)
+
+ {
+- gint i;
++ int i;
+ gunichar combination_buffer[IBUS_MAX_COMPOSE_LEN];
+- gchar *combination_utf8, *nfc;
++ char *combination_utf8, *nfc;
+
+ if (output_char)
+ *output_char = 0;
+diff --git a/src/ibusenginesimple.c b/src/ibusenginesimple.c
+index 57ca10c4..699cf0d4 100644
+--- a/src/ibusenginesimple.c
++++ b/src/ibusenginesimple.c
+@@ -79,8 +79,8 @@ typedef struct {
+ struct _IBusEngineSimplePrivate {
+ guint16 *compose_buffer;
+ gunichar tentative_match;
+- gchar *tentative_emoji;
+- gint tentative_match_len;
++ char *tentative_emoji;
++ int tentative_match_len;
+
+ guint hex_mode_enabled : 1;
+ guint in_hex_sequence : 1;
+@@ -142,7 +142,7 @@ static void ibus_engine_simple_candidate_clicked
+ static void ibus_engine_simple_commit_char (IBusEngineSimple *simple,
+ gunichar ch);
+ static void ibus_engine_simple_commit_str (IBusEngineSimple *simple,
+- const gchar *str);
++ const char *str);
+ static void ibus_engine_simple_update_preedit_text
+ (IBusEngineSimple *simple);
+
+@@ -260,10 +260,10 @@ ibus_engine_simple_commit_char (IBusEngineSimple *simple,
+
+ static void
+ ibus_engine_simple_commit_str (IBusEngineSimple *simple,
+- const gchar *str)
++ const char *str)
+ {
+ IBusEngineSimplePrivate *priv = simple->priv;
+- gchar *backup_str;
++ char *backup_str;
+
+ g_return_if_fail (str && *str);
+
+@@ -368,15 +368,15 @@ ibus_engine_simple_update_preedit_text (IBusEngineSimple *simple)
+
+ static gboolean
+ check_hex (IBusEngineSimple *simple,
+- gint n_compose)
++ int n_compose)
+ {
+ IBusEngineSimplePrivate *priv = simple->priv;
+
+- gint i;
++ int i;
+ GString *str;
+ gulong n;
+- gchar *nptr = NULL;
+- gchar buf[7];
++ char *nptr = NULL;
++ char buf[7];
+
+ CHECK_COMPOSE_BUFFER_LENGTH (n_compose);
+
+@@ -453,14 +453,14 @@ load_emoji_dict ()
+
+ static gboolean
+ check_emoji_table (IBusEngineSimple *simple,
+- gint n_compose,
+- gint index)
++ int n_compose,
++ int index)
+ {
+ IBusEngineSimplePrivate *priv = simple->priv;
+ IBusEngineDict *emoji_dict = priv->emoji_dict;
+ GString *str = NULL;
+- gint i;
+- gchar buf[7];
++ int i;
++ char buf[7];
+ GSList *words = NULL;
+
+ g_assert (IBUS_IS_ENGINE_SIMPLE (simple));
+@@ -535,7 +535,7 @@ check_emoji_table (IBusEngineSimple *simple,
+
+ static gboolean
+ no_sequence_matches (IBusEngineSimple *simple,
+- gint n_compose,
++ int n_compose,
+ guint keyval,
+ guint keycode,
+ guint modifiers)
+@@ -550,7 +550,7 @@ no_sequence_matches (IBusEngineSimple *simple,
+ * match pending.
+ */
+ if (priv->tentative_match) {
+- gint len = priv->tentative_match_len;
++ int len = priv->tentative_match_len;
+ int i;
+
+ ibus_engine_simple_commit_char (simple, priv->tentative_match);
+@@ -615,7 +615,7 @@ ibus_engine_simple_update_lookup_and_aux_table (IBusEngineSimple *simple)
+ {
+ IBusEngineSimplePrivate *priv;
+ guint index, candidates;
+- gchar *aux_label = NULL;
++ char *aux_label = NULL;
+ IBusText *text = NULL;
+
+ g_return_if_fail (IBUS_IS_ENGINE_SIMPLE (simple));
+@@ -697,7 +697,7 @@ ibus_engine_simple_set_number_on_lookup_table (IBusEngineSimple *simple,
+
+ static gboolean
+ ibus_engine_simple_check_all_compose_table (IBusEngineSimple *simple,
+- gint n_compose)
++ int n_compose)
+ {
+ IBusEngineSimplePrivate *priv = simple->priv;
+ gboolean compose_finish = FALSE;
+@@ -818,7 +818,7 @@ ibus_engine_simple_process_key_event (IBusEngine *engine,
+ {
+ IBusEngineSimple *simple = (IBusEngineSimple *)engine;
+ IBusEngineSimplePrivate *priv = simple->priv;
+- gint n_compose = 0;
++ int n_compose = 0;
+ gboolean have_hex_mods;
+ gboolean is_hex_start = FALSE;
+ gboolean is_emoji_start = FALSE;
+@@ -828,7 +828,7 @@ ibus_engine_simple_process_key_event (IBusEngine *engine,
+ gboolean is_escape;
+ guint hex_keyval;
+ guint printable_keyval;
+- gint i;
++ int i;
+
+ while (n_compose <= COMPOSE_BUFFER_SIZE && priv->compose_buffer[n_compose] != 0)
+ n_compose++;
+@@ -1274,7 +1274,7 @@ ibus_engine_simple_candidate_clicked (IBusEngine *engine,
+ IBusEngineSimple *simple = (IBusEngineSimple *)engine;
+ IBusEngineSimplePrivate *priv = simple->priv;
+ guint keyval;
+- gint n_compose = 0;
++ int n_compose = 0;
+
+ if (priv->lookup_table == NULL || !priv->lookup_table_visible)
+ return;
+@@ -1308,18 +1308,18 @@ ibus_engine_simple_add_table_by_locale (IBusEngineSimple *simple,
+ {
+ /* Now ibus_engine_simple_add_compose_file() always returns TRUE. */
+ gboolean retval = TRUE;
+- gchar *path = NULL;
+- const gchar *home;
++ char *path = NULL;
++ const char *home;
+ #if GLIB_CHECK_VERSION (2, 58, 0)
+- const gchar * const *langs;
+- const gchar * const *lang = NULL;
++ const char * const *langs;
++ const char * const *lang = NULL;
+ #else
+- const gchar *_locale;
+- gchar **langs = NULL;
+- gchar **lang = NULL;
++ const char *_locale;
++ char **langs = NULL;
++ char **lang = NULL;
+ #endif
+- gchar * const sys_langs[] = { "el_gr", "fi_fi", "pt_br", NULL };
+- gchar * const *sys_lang = NULL;
++ char * const sys_langs[] = { "el_gr", "fi_fi", "pt_br", NULL };
++ char * const *sys_lang = NULL;
+
+ if (locale == NULL) {
+ path = g_build_filename (g_get_user_config_dir (),
+diff --git a/src/ibusenginesimpleprivate.h b/src/ibusenginesimpleprivate.h
+index 47f2a3d4..5479e553 100644
+--- a/src/ibusenginesimpleprivate.h
++++ b/src/ibusenginesimpleprivate.h
+@@ -43,11 +43,11 @@ struct _IBusComposeTableCompactPrivate
+ };
+
+ gboolean ibus_check_algorithmically (const guint16 *compose_buffer,
+- gint n_compose,
++ int n_compose,
+ gunichar *output);
+ gboolean ibus_compose_table_check (const IBusComposeTableEx *table,
+ guint16 *compose_buffer,
+- gint n_compose,
++ int n_compose,
+ gboolean *compose_finish,
+ gboolean *compose_match,
+ GString *output,
+@@ -56,7 +56,7 @@ gboolean ibus_compose_table_compact_check
+ (const IBusComposeTableCompactEx
+ *table,
+ guint16 *compose_buffer,
+- gint n_compose,
++ int n_compose,
+ gboolean *compose_finish,
+ gunichar **output_chars);
+ gunichar ibus_keysym_to_unicode (guint16 keysym,
+--
+2.28.0
+
+From 4259f16324d578aa2505cf50f2f23923d8d87e76 Mon Sep 17 00:00:00 2001
+From: fujiwarat <takao.fujiwara1@gmail.com>
+Date: Mon, 26 Jul 2021 22:53:19 +0900
+Subject: [PATCH 5/6] src/ibusenginesimple: Make Compose preedit less intrusive
+
+Tweak the preedit display for Compose sequences to
+be not so distracting. We only show the Compose key
+when it occurs in the middle of the sequence or is
+the only key so far.
+
+BUG=https://gitlab.gnome.org/GNOME/gtk/-/issues/3669
+BUG=https://gitlab.gnome.org/GNOME/gtk/-/merge_requests/3220
+BUG=https://blog.gtk.org/2021/03/24/input-revisited/
+---
+ src/ibuscomposetable.c | 175 +++++++++++---------
+ src/ibusenginesimple.c | 301 +++++++++++++++++++++++++---------
+ src/ibusenginesimpleprivate.h | 9 +-
+ 3 files changed, 325 insertions(+), 160 deletions(-)
+
+diff --git a/src/ibuscomposetable.c b/src/ibuscomposetable.c
+index aa58f136..2ad21551 100644
+--- a/src/ibuscomposetable.c
++++ b/src/ibuscomposetable.c
+@@ -1523,23 +1523,22 @@ compare_seq_index (const void *key, const void *value)
+ * IBusComposeData->values[] is the gunichar array.
+ */
+ gboolean
+-ibus_compose_table_compact_check (const IBusComposeTableCompactEx *table,
+- guint16
+- *compose_buffer,
+- int n_compose,
+- gboolean
+- *compose_finish,
+- gunichar **output_chars)
++ibus_compose_table_compact_check (const IBusComposeTableCompactEx
++ *table,
++ guint16 *compose_buffer,
++ int n_compose,
++ gboolean *compose_finish,
++ gunichar **output_chars)
+ {
+ int row_stride;
+ guint16 *seq_index;
+ guint16 *seq;
+ int i;
+
+- if (compose_finish)
+- *compose_finish = FALSE;
+- if (output_chars)
+- *output_chars = NULL;
++ /* compose_finish and output_chars should not be initialized because
++ * ibus_compose_table_check() is called at first and
++ * engine->priv->tentative_match will be preedit after this is called.
++ */
+
+ /* Will never match, if the sequence in the compose buffer is longer
+ * than the sequences in the table. Further, compare_seq (key, val)
+@@ -1588,7 +1587,8 @@ ibus_compose_table_compact_check (const IBusComposeTableCompactEx *table,
+ if (compose_finish)
+ *compose_finish = TRUE;
+ if (output_chars) {
+- *output_chars = g_new (gunichar, length + 1);
++ if (!(*output_chars))
++ *output_chars = g_new (gunichar, length + 1);
+ for (j = 0; j < length; j++) {
+ (*output_chars)[j] = table->priv->data2[index + j];
+ }
+@@ -1622,7 +1622,8 @@ ibus_compose_table_compact_check (const IBusComposeTableCompactEx *table,
+ if (compose_finish)
+ *compose_finish = TRUE;
+ if (output_chars) {
+- *output_chars = g_new (gunichar, 2);
++ if (!(*output_chars))
++ *output_chars = g_new (gunichar, 2);
+ (*output_chars)[0] = seq[row_stride - 1];
+ (*output_chars)[1] = 0;
+ }
+@@ -1635,16 +1636,6 @@ ibus_compose_table_compact_check (const IBusComposeTableCompactEx *table,
+ }
+
+
+-/* Checks if a keysym is a dead key. Dead key keysym values are defined in
+- * ../gdk/gdkkeysyms.h and the first is GDK_KEY_dead_grave. As X.Org is updated,
+- * more dead keys are added and we need to update the upper limit.
+- * Currently, the upper limit is GDK_KEY_dead_dasia+1. The +1 has to do with
+- * a temporary issue in the X.Org header files.
+- * In future versions it will be just the keysym (no +1).
+- */
+-#define IS_DEAD_KEY(k) \
+- ((k) >= IBUS_KEY_dead_grave && (k) <= IBUS_KEY_dead_greek)
+-
+ /* This function receives a sequence of Unicode characters and tries to
+ * normalize it (NFC). We check for the case the the resulting string
+ * has length 1 (single character).
+@@ -1754,7 +1745,8 @@ ibus_check_algorithmically (const guint16 *compose_buffer,
+ i--;
+ while (i >= 0) {
+ combination_buffer[i+1] = ibus_keysym_to_unicode (compose_buffer[i],
+- TRUE);
++ TRUE,
++ NULL);
+ if (!combination_buffer[i+1]) {
+ combination_buffer[i+1] =
+ ibus_keyval_to_unicode (compose_buffer[i]);
+@@ -1786,66 +1778,91 @@ ibus_check_algorithmically (const guint16 *compose_buffer,
+
+
+ gunichar
+-ibus_keysym_to_unicode (guint16 keysym,
+- gboolean combining) {
+-#define CASE(keysym_suffix, unicode) \
+- case IBUS_KEY_dead_##keysym_suffix: return unicode
+-#define CASE_COMBINE(keysym_suffix, combined_unicode, isolated_unicode) \
+- case IBUS_KEY_dead_##keysym_suffix: \
+- if (combining) \
+- return combined_unicode; \
+- else \
++ibus_keysym_to_unicode (guint16 keysym,
++ gboolean combining,
++ gboolean *need_space) {
++#define CASE(keysym_suffix, unicode, sp) \
++ case IBUS_KEY_dead_##keysym_suffix: \
++ if (need_space) \
++ *need_space = sp; \
++ return unicode
++#define CASE_COMBINE(keysym_suffix, combined_unicode, isolated_unicode, sp) \
++ case IBUS_KEY_dead_##keysym_suffix: \
++ if (need_space) \
++ *need_space = sp; \
++ if (combining) \
++ return combined_unicode; \
++ else \
+ return isolated_unicode
+ switch (keysym) {
+- CASE (a, 0x03041);
+- CASE (A, 0x03042);
+- CASE (i, 0x03043);
+- CASE (I, 0x03044);
+- CASE (u, 0x03045);
+- CASE (U, 0x03046);
+- CASE (e, 0x03047);
+- CASE (E, 0x03048);
+- CASE (o, 0x03049);
+- CASE (O, 0x0304A);
+- CASE (abovecomma, 0x0313);
+- CASE_COMBINE (abovedot, 0x0307, 0x02D9);
+- CASE (abovereversedcomma, 0x0314);
+- CASE_COMBINE (abovering, 0x030A, 0x02DA);
+- CASE_COMBINE (acute, 0x0301, 0x00B4);
+- CASE (belowbreve, 0x032E);
+- CASE_COMBINE (belowcircumflex, 0x032D, 0xA788);
+- CASE_COMBINE (belowcomma, 0x0326, 0x002C);
+- CASE (belowdiaeresis, 0x0324);
+- CASE_COMBINE (belowdot, 0x0323, 0x002E);
+- CASE_COMBINE (belowmacron, 0x0331, 0x02CD);
+- CASE_COMBINE (belowring, 0x030A, 0x02F3);
+- CASE_COMBINE (belowtilde, 0x0330, 0x02F7);
+- CASE_COMBINE (breve, 0x0306, 0x02D8);
+- CASE_COMBINE (capital_schwa, 0x018F, 0x04D8);
+- CASE_COMBINE (caron, 0x030C, 0x02C7);
+- CASE_COMBINE (cedilla, 0x0327, 0x00B8);
+- CASE_COMBINE (circumflex, 0x0302, 0x005E);
+- CASE (currency, 0x00A4);
++#ifdef IBUS_ENGLISH_DEAD_KEY
++ CASE (a, 0x0363, 1);
++ CASE (A, 0x0363, 1);
++ CASE (i, 0x0365, 1);
++ CASE (I, 0x0365, 1);
++ CASE (u, 0x0367, 1);
++ CASE (U, 0x0367, 1);
++ CASE (e, 0x0364, 1);
++ CASE (E, 0x0364, 1);
++ CASE (o, 0x0366, 1);
++ CASE (O, 0x0366, 1);
++#else
++ CASE (a, 0x3041, 0);
++ CASE (A, 0x3042, 0);
++ CASE (i, 0x3043, 0);
++ CASE (I, 0x3044, 0);
++ CASE (u, 0x3045, 0);
++ CASE (U, 0x3046, 0);
++ CASE (e, 0x3047, 0);
++ CASE (E, 0x3048, 0);
++ CASE (o, 0x3049, 0);
++ CASE (O, 0x304A, 0);
++#endif
++ CASE_COMBINE (abovecomma, 0x0313, 0x02BC, 0);
++ CASE_COMBINE (abovedot, 0x0307, 0x02D9, 0);
++ CASE_COMBINE (abovereversedcomma, 0x0314, 0x02BD, 0);
++ CASE_COMBINE (abovering, 0x030A, 0x02DA, 0);
++ CASE_COMBINE (aboveverticalline, 0x030D, 0x02C8, 0);
++ CASE_COMBINE (acute, 0x0301, 0x00B4, 0);
++ CASE (belowbreve, 0x032E, 1);
++ CASE_COMBINE (belowcircumflex, 0x032D, 0xA788, 0);
++ CASE_COMBINE (belowcomma, 0x0326, 0x002C, 0);
++ CASE (belowdiaeresis, 0x0324, 1);
++ CASE_COMBINE (belowdot, 0x0323, 0x002E, 0);
++ CASE_COMBINE (belowmacron, 0x0331, 0x02CD, 0);
++ CASE_COMBINE (belowring, 0x030A, 0x02F3, 0);
++ CASE_COMBINE (belowtilde, 0x0330, 0x02F7, 0);
++ CASE_COMBINE (belowverticalline, 0x0329, 0x02CC, 0);
++ CASE_COMBINE (breve, 0x0306, 0x02D8, 0);
++ CASE_COMBINE (capital_schwa, 0x1DEA, 0x1D4A, 0);
++ CASE_COMBINE (caron, 0x030C, 0x02C7, 0);
++ CASE_COMBINE (cedilla, 0x0327, 0x00B8, 0);
++ CASE_COMBINE (circumflex, 0x0302, 0x005E, 0);
++ CASE (currency, 0x00A4, 0);
+ // IBUS_KEY_dead_dasia == IBUS_KEY_dead_abovereversedcomma
+- CASE_COMBINE (diaeresis, 0x0308, 0x00A8);
+- CASE_COMBINE (doubleacute, 0x030B, 0x02DD);
+- CASE_COMBINE (doublegrave, 0x030F, 0x02F5);
+- CASE_COMBINE (grave, 0x0300, 0x0060);
+- CASE (greek, 0x03BC);
+- CASE (hook, 0x0309);
+- CASE (horn, 0x031B);
+- CASE (invertedbreve, 0x032F);
+- CASE_COMBINE (iota, 0x0345, 0x037A);
+- CASE_COMBINE (macron, 0x0304, 0x00AF);
+- CASE_COMBINE (ogonek, 0x0328, 0x02DB);
++ CASE_COMBINE (diaeresis, 0x0308, 0x00A8, 0);
++ CASE_COMBINE (doubleacute, 0x030B, 0x02DD, 0);
++ CASE_COMBINE (doublegrave, 0x030F, 0x02F5, 0);
++ CASE_COMBINE (grave, 0x0300, 0x0060, 0);
++ CASE (greek, 0x03BC, 0);
++ CASE_COMBINE (hook, 0x0309, 0x02C0, 0);
++ CASE (horn, 0x031B, 1);
++ CASE (invertedbreve, 0x032F, 1);
++ CASE_COMBINE (iota, 0x0345, 0x037A, 0);
++ CASE (longsolidusoverlay, 0x0338, 1);
++ CASE_COMBINE (lowline, 0x0332, 0x005F, 0);
++ CASE_COMBINE (macron, 0x0304, 0x00AF, 0);
++ CASE_COMBINE (ogonek, 0x0328, 0x02DB, 0);
+ // IBUS_KEY_dead_perispomeni == IBUS_KEY_dead_tilde
+ // IBUS_KEY_dead_psili == IBUS_KEY_dead_abovecomma
+- CASE_COMBINE (semivoiced_sound, 0x309A, 0x309C);
+- CASE_COMBINE (small_schwa, 0x1D4A, 0x04D9);
+- CASE (stroke, 0x002F);
+- CASE_COMBINE (tilde, 0x0303, 0x007E);
+- CASE_COMBINE (voiced_sound, 0x3099, 0x309B);
++ CASE_COMBINE (semivoiced_sound, 0x309A, 0x309C, 0);
++ CASE_COMBINE (small_schwa, 0x1DEA, 0x1D4A, 0);
++ CASE (stroke, 0x0335, 1);
++ CASE_COMBINE (tilde, 0x0303, 0x007E, 0);
++ CASE_COMBINE (voiced_sound, 0x3099, 0x309B, 0);
+ case IBUS_KEY_Multi_key:
++ if (need_space)
++ *need_space = FALSE;
+ /* We only show the Compose key visibly when it is the
+ * only glyph in the preedit, or when it occurs in the
+ * middle of the sequence. Sadly, the official character,
+@@ -1854,6 +1871,8 @@ ibus_keysym_to_unicode (guint16 keysym,
+ */
+ return 0x00B7;
+ default:;
++ if (need_space)
++ *need_space = FALSE;
+ }
+ return 0x0;
+ #undef CASE
+diff --git a/src/ibusenginesimple.c b/src/ibusenginesimple.c
+index 699cf0d4..acfc0264 100644
+--- a/src/ibusenginesimple.c
++++ b/src/ibusenginesimple.c
+@@ -78,13 +78,14 @@ typedef struct {
+
+ struct _IBusEngineSimplePrivate {
+ guint16 *compose_buffer;
+- gunichar tentative_match;
+- char *tentative_emoji;
++ GString *tentative_match;
+ int tentative_match_len;
++ char *tentative_emoji;
+
+ guint hex_mode_enabled : 1;
+ guint in_hex_sequence : 1;
+ guint in_emoji_sequence : 1;
++ guint in_compose_sequence : 1;
+ guint modifiers_dropped : 1;
+ IBusEngineDict *emoji_dict;
+ IBusLookupTable *lookup_table;
+@@ -178,6 +179,8 @@ ibus_engine_simple_init (IBusEngineSimple *simple)
+ simple->priv->hex_mode_enabled =
+ g_getenv("IBUS_ENABLE_CTRL_SHIFT_U") != NULL ||
+ g_getenv("IBUS_ENABLE_CONTROL_SHIFT_U") != NULL;
++ simple->priv->tentative_match = g_string_new ("");
++ simple->priv->tentative_match_len = 0;
+ }
+
+
+@@ -196,6 +199,9 @@ ibus_engine_simple_destroy (IBusEngineSimple *simple)
+ g_clear_object (&priv->lookup_table);
+ g_clear_pointer (&priv->compose_buffer, g_free);
+ g_clear_pointer (&priv->tentative_emoji, g_free);
++ g_string_free (priv->tentative_match, TRUE);
++ priv->tentative_match = NULL;
++ priv->tentative_match_len = 0;
+
+ IBUS_OBJECT_CLASS(ibus_engine_simple_parent_class)->destroy (
+ IBUS_OBJECT (simple));
+@@ -222,15 +228,15 @@ ibus_engine_simple_reset (IBusEngine *engine)
+
+ priv->compose_buffer[0] = 0;
+
+- if (priv->tentative_match || priv->in_hex_sequence) {
++ if (priv->tentative_match->len > 0 || priv->in_hex_sequence) {
+ priv->in_hex_sequence = FALSE;
+- priv->tentative_match = 0;
++ g_string_set_size (priv->tentative_match, 0);
+ priv->tentative_match_len = 0;
+ } else if (priv->tentative_emoji || priv->in_emoji_sequence) {
+ priv->in_emoji_sequence = FALSE;
+ g_clear_pointer (&priv->tentative_emoji, g_free);
+ } else if (!priv->in_hex_sequence && !priv->in_emoji_sequence) {
+- priv->tentative_match = 0;
++ g_string_set_size (priv->tentative_match, 0);
+ priv->tentative_match_len = 0;
+ }
+ ibus_engine_hide_preedit_text ((IBusEngine *)simple);
+@@ -244,10 +250,17 @@ ibus_engine_simple_commit_char (IBusEngineSimple *simple,
+
+ IBusEngineSimplePrivate *priv = simple->priv;
+
+- if (priv->tentative_match || priv->in_hex_sequence) {
+- priv->in_hex_sequence = FALSE;
+- priv->tentative_match = 0;
++ if (priv->in_hex_sequence ||
++ priv->tentative_match_len > 0 ||
++ priv->compose_buffer[0] != 0) {
++ g_string_set_size (priv->tentative_match, 0);
+ priv->tentative_match_len = 0;
++ priv->in_hex_sequence = FALSE;
++ priv->in_compose_sequence = FALSE;
++ priv->compose_buffer[0] = 0;
++ /* Don't call ibus_engine_simple_update_preedit_text() inside
++ * not to call it as duplilcated.
++ */
+ }
+ if (priv->tentative_emoji || priv->in_emoji_sequence) {
+ priv->in_emoji_sequence = FALSE;
+@@ -269,10 +282,17 @@ ibus_engine_simple_commit_str (IBusEngineSimple *simple,
+
+ backup_str = g_strdup (str);
+
+- if (priv->tentative_match || priv->in_hex_sequence) {
+- priv->in_hex_sequence = FALSE;
+- priv->tentative_match = 0;
++ if (priv->in_hex_sequence ||
++ priv->tentative_match_len > 0 ||
++ priv->compose_buffer[0] != 0) {
++ g_string_set_size (priv->tentative_match, 0);
+ priv->tentative_match_len = 0;
++ priv->in_hex_sequence = FALSE;
++ priv->in_compose_sequence = FALSE;
++ priv->compose_buffer[0] = 0;
++ /* Don't call ibus_engine_simple_update_preedit_text() inside
++ * not to call it as duplilcated.
++ */
+ }
+ if (priv->tentative_emoji || priv->in_emoji_sequence) {
+ priv->in_emoji_sequence = FALSE;
+@@ -288,8 +308,8 @@ static void
+ ibus_engine_simple_update_preedit_text (IBusEngineSimple *simple)
+ {
+ IBusEngineSimplePrivate *priv = simple->priv;
+-
+ GString *s = g_string_new ("");
++ int i, j;
+
+ if (priv->in_hex_sequence || priv->in_emoji_sequence) {
+ int hexchars = 0;
+@@ -305,32 +325,59 @@ ibus_engine_simple_update_preedit_text (IBusEngineSimple *simple)
+ ibus_keyval_to_unicode (priv->compose_buffer[hexchars++])
+ );
+ }
+- } else if (priv->tentative_match) {
+- g_string_append_unichar(s, priv->tentative_match);
+ } else if (priv->tentative_emoji && *priv->tentative_emoji) {
+ IBusText *text = ibus_text_new_from_string (priv->tentative_emoji);
+ int len = strlen (priv->tentative_emoji);
+ ibus_text_append_attribute (text,
+ IBUS_ATTR_TYPE_UNDERLINE, IBUS_ATTR_UNDERLINE_SINGLE, 0, len);
+ ibus_engine_update_preedit_text ((IBusEngine *)simple, text, len, TRUE);
++ g_string_free (s, TRUE);
+ return;
+- } else {
+- int hexchars = 0;
+- while (priv->compose_buffer[hexchars] != 0) {
+- guint16 keysym = priv->compose_buffer[hexchars];
+- gunichar unichar = ibus_keysym_to_unicode (keysym, FALSE);
+- if (unichar > 0) {
+- g_string_append_unichar(s, unichar);
+- } else {
+- unichar = ibus_keyval_to_unicode (keysym);
+- g_string_append_unichar(s, unichar);
+- }
+- if (!unichar) {
+- g_warning (
++ } else if (priv->in_compose_sequence) {
++ if (priv->tentative_match->len > 0 && priv->compose_buffer[0] != 0) {
++ g_string_append (s, priv->tentative_match->str);
++ } else {
++ for (i = 0; priv->compose_buffer[i]; ++i) {
++ guint16 keysym = priv->compose_buffer[i];
++ gboolean show_keysym = TRUE;
++ gboolean need_space = FALSE;
++ gunichar ch;
++
++ if (keysym == IBUS_KEY_Multi_key) {
++ /* We only show the Compose key visibly when it is the
++ * only glyph in the preedit, or when it occurs in the
++ * middle of the sequence. Sadly, the official character,
++ * U+2384, COMPOSITION SYMBOL, is bit too distracting, so
++ * we use U+00B7, MIDDLE DOT.
++ */
++ for (j = i + 1; priv->compose_buffer[j]; j++) {
++ if (priv->compose_buffer[j] != IBUS_KEY_Multi_key) {
++ show_keysym = FALSE;
++ break;
++ }
++ }
++ if (!show_keysym)
++ continue;
++ ch = ibus_keysym_to_unicode (keysym, FALSE, NULL);
++ g_string_append_unichar (s, ch);
++ } else if (IS_DEAD_KEY (keysym)) {
++ ch = ibus_keysym_to_unicode (keysym, FALSE, &need_space);
++ if (ch) {
++ if (need_space)
++ g_string_append_c (s, ' ');
++ g_string_append_unichar (s, ch);
++ }
++ } else {
++ ch = ibus_keyval_to_unicode (keysym);
++ if (ch)
++ g_string_append_unichar(s, ch);
++ }
++ if (!ch) {
++ g_warning (
+ "Not found alternative character of compose key 0x%X",
+- priv->compose_buffer[hexchars]);
++ priv->compose_buffer[i]);
++ }
+ }
+- ++hexchars;
+ }
+ }
+
+@@ -380,7 +427,7 @@ check_hex (IBusEngineSimple *simple,
+
+ CHECK_COMPOSE_BUFFER_LENGTH (n_compose);
+
+- priv->tentative_match = 0;
++ g_string_set_size (priv->tentative_match, 0);
+ priv->tentative_match_len = 0;
+
+ str = g_string_new (NULL);
+@@ -421,7 +468,8 @@ check_hex (IBusEngineSimple *simple,
+ }
+
+ if (g_unichar_validate (n)) {
+- priv->tentative_match = n;
++ g_string_set_size (priv->tentative_match, 0);
++ g_string_append_unichar (priv->tentative_match, n);
+ priv->tentative_match_len = n_compose;
+ }
+
+@@ -546,21 +594,31 @@ no_sequence_matches (IBusEngineSimple *simple,
+
+ CHECK_COMPOSE_BUFFER_LENGTH (n_compose);
+
++ priv->in_compose_sequence = FALSE;
++
+ /* No compose sequences found, check first if we have a partial
+ * match pending.
+ */
+- if (priv->tentative_match) {
++ if (priv->tentative_match_len > 0) {
++ guint16 *compose_buffer;
+ int len = priv->tentative_match_len;
+ int i;
++ char *str;
+
+- ibus_engine_simple_commit_char (simple, priv->tentative_match);
+- priv->compose_buffer[0] = 0;
++ compose_buffer = alloca (sizeof (guint16) * COMPOSE_BUFFER_SIZE);
++ memcpy (compose_buffer,
++ priv->compose_buffer,
++ sizeof (guint16) * COMPOSE_BUFFER_SIZE);
++
++ str = g_strdup (priv->tentative_match->str);
++ ibus_engine_simple_commit_str (simple, str);
++ g_free (str);
+ ibus_engine_simple_update_preedit_text (simple);
+
+ for (i=0; i < n_compose - len - 1; i++) {
+ ibus_engine_simple_process_key_event (
+ (IBusEngine *)simple,
+- priv->compose_buffer[len + i],
++ compose_buffer[len + i],
+ 0, 0);
+ }
+
+@@ -571,6 +629,26 @@ no_sequence_matches (IBusEngineSimple *simple,
+ priv->compose_buffer[0] = 0;
+ ibus_engine_simple_update_preedit_text (simple);
+ } else {
++ if (n_compose == 2 && IS_DEAD_KEY (priv->compose_buffer[0])) {
++ gboolean need_space = FALSE;
++ GString *s = g_string_new ("");
++ /* dead keys are never *really* dead */
++ ch = ibus_keysym_to_unicode (priv->compose_buffer[0],
++ FALSE, &need_space);
++ if (ch) {
++ if (need_space)
++ g_string_append_c (s, ' ');
++ g_string_append_unichar (s, ch);
++ }
++ ch = ibus_keyval_to_unicode (priv->compose_buffer[1]);
++ if (ch != 0 && !g_unichar_iscntrl (ch))
++ g_string_append_unichar (s, ch);
++ ibus_engine_simple_commit_str (simple, s->str);
++ g_string_free (s, TRUE);
++ ibus_engine_simple_update_preedit_text (simple);
++ return TRUE;
++ }
++
+ priv->compose_buffer[0] = 0;
+ if (n_compose > 1) {
+ /* Invalid sequence */
+@@ -695,19 +773,46 @@ ibus_engine_simple_set_number_on_lookup_table (IBusEngineSimple *simple,
+ ibus_engine_simple_update_preedit_text (simple);
+ }
+
++
+ static gboolean
+ ibus_engine_simple_check_all_compose_table (IBusEngineSimple *simple,
+ int n_compose)
+ {
+ IBusEngineSimplePrivate *priv = simple->priv;
++ GSList *tmp_list;
+ gboolean compose_finish = FALSE;
+ gboolean compose_match = FALSE;
+ GString *output = g_string_new ("");
+- gboolean does_hit = FALSE;
++ gboolean success = FALSE;
+ gboolean is_32bit = FALSE;
+- GSList *tmp_list;
+ gunichar *output_chars = NULL;
+- gunichar output_char;
++ gunichar output_char = '\0';
++
++ if (n_compose == 2) {
++ /* Special-case deadkey-deadkey sequences.
++ * We are not doing chained deadkeys, so we
++ * want to commit the first key, and contine
++ * preediting with second.
++ */
++ if (IS_DEAD_KEY (priv->compose_buffer[0]) &&
++ IS_DEAD_KEY (priv->compose_buffer[1])) {
++ gboolean need_space = FALSE;
++ gunichar ch = ibus_keysym_to_unicode (priv->compose_buffer[0],
++ FALSE, &need_space);
++ guint16 next = priv->compose_buffer[1];
++ if (ch) {
++ if (need_space)
++ g_string_append_c (output, ' ');
++ g_string_append_unichar (output, ch);
++ ibus_engine_simple_commit_str (simple, output->str);
++ g_string_set_size (output, 0);
++
++ priv->compose_buffer[0] = next;
++ priv->compose_buffer[1] = 0;
++ n_compose = 1;
++ }
++ }
++ }
+
+ G_LOCK (global_tables);
+ tmp_list = global_tables;
+@@ -721,7 +826,7 @@ ibus_engine_simple_check_all_compose_table (IBusEngineSimple *simple,
+ &compose_match,
+ output,
+ is_32bit)) {
+- does_hit = TRUE;
++ success = TRUE;
+ break;
+ }
+ is_32bit = TRUE;
+@@ -733,14 +838,15 @@ ibus_engine_simple_check_all_compose_table (IBusEngineSimple *simple,
+ &compose_match,
+ output,
+ is_32bit)) {
+- does_hit = TRUE;
++ success = TRUE;
+ break;
+ }
+ tmp_list = tmp_list->next;
+ }
+ G_UNLOCK (global_tables);
+
+- if (does_hit) {
++ if (success) {
++ priv->in_compose_sequence = TRUE;
+ if (compose_finish) {
+ if (compose_match) {
+ if (is_32bit) {
+@@ -750,59 +856,89 @@ ibus_engine_simple_check_all_compose_table (IBusEngineSimple *simple,
+ g_utf8_get_char (output->str));
+ }
+ }
++ ibus_engine_simple_update_preedit_text (simple);
++ g_string_free (output, TRUE);
++ return TRUE;
+ } else if (compose_match) {
+- priv->tentative_match = g_utf8_get_char (output->str);
++ g_string_assign (priv->tentative_match, output->str);
+ priv->tentative_match_len = n_compose;
++ ibus_engine_simple_update_preedit_text (simple);
++ g_string_free (output, TRUE);
++ return TRUE;
+ }
+- ibus_engine_simple_update_preedit_text (simple);
+- g_string_free (output, TRUE);
+- return TRUE;
+ }
+ g_string_free (output, TRUE);
++ output = NULL;
+
+ if (ibus_compose_table_compact_check (&ibus_compose_table_compact,
+ priv->compose_buffer,
+ n_compose,
+ &compose_finish,
+ &output_chars)) {
++ priv->in_compose_sequence = TRUE;
+ if (compose_finish) {
+- ibus_engine_simple_commit_char (simple, *output_chars);
++ if (success) {
++ g_string_append_unichar (priv->tentative_match, *output_chars);
++ priv->tentative_match_len = n_compose;
++ } else {
++ ibus_engine_simple_commit_char (simple, *output_chars);
++ priv->compose_buffer[0] = 0;
++ }
+ g_free (output_chars);
+- priv->compose_buffer[0] = 0;
++ ibus_engine_simple_update_preedit_text (simple);
++ return TRUE;
+ }
+- ibus_engine_simple_update_preedit_text (simple);
+- return TRUE;
++ success = TRUE;
+ }
++ g_free (output_chars);
++ output_chars = NULL;
+ if (ibus_compose_table_compact_check (&ibus_compose_table_compact_32bit,
+ priv->compose_buffer,
+ n_compose,
+ &compose_finish,
+ &output_chars)) {
++ priv->in_compose_sequence = TRUE;
+ if (compose_finish) {
+ GError *error = NULL;
+ char *str = g_ucs4_to_utf8 (output_chars, -1, NULL, NULL, &error);
+- if (str) {
+- ibus_engine_simple_commit_str (simple, str);
+- g_free (str);
+- } else {
++ if (!str) {
+ g_warning ("Failed to output multiple characters: %s",
+ error->message);
+ g_error_free (error);
++ } else if (success) {
++ g_string_append (priv->tentative_match, str);
++ priv->tentative_match_len = n_compose;
++ } else {
++ ibus_engine_simple_commit_str (simple, str);
++ priv->compose_buffer[0] = 0;
+ }
+- priv->compose_buffer[0] = 0;
++ g_free (str);
++ g_free (output_chars);
++ ibus_engine_simple_update_preedit_text (simple);
++ return TRUE;
+ }
+- g_free (output_chars);
+- ibus_engine_simple_update_preedit_text (simple);
+- return TRUE;
++ success = TRUE;
+ }
+- g_assert (!output_chars);
++ g_free (output_chars);
++ output_chars = NULL;
+ if (ibus_check_algorithmically (priv->compose_buffer,
+ n_compose,
+ &output_char)) {
++ priv->in_compose_sequence = TRUE;
+ if (output_char) {
+- ibus_engine_simple_commit_char (simple, output_char);
+- priv->compose_buffer[0] = 0;
++ if (success) {
++ g_string_append_unichar (priv->tentative_match, output_char);
++ priv->tentative_match_len = n_compose;
++ } else {
++ ibus_engine_simple_commit_char (simple, output_char);
++ priv->compose_buffer[0] = 0;
++ }
++ ibus_engine_simple_update_preedit_text (simple);
++ return TRUE;
+ }
++ success = TRUE;
++ }
++ if (success) {
+ ibus_engine_simple_update_preedit_text (simple);
+ return TRUE;
+ }
+@@ -810,6 +946,7 @@ ibus_engine_simple_check_all_compose_table (IBusEngineSimple *simple,
+ return FALSE;
+ }
+
++
+ static gboolean
+ ibus_engine_simple_process_key_event (IBusEngine *engine,
+ guint keyval,
+@@ -841,16 +978,17 @@ ibus_engine_simple_process_key_event (IBusEngine *engine,
+ if (priv->in_hex_sequence &&
+ (keyval == IBUS_KEY_Control_L || keyval == IBUS_KEY_Control_R ||
+ keyval == IBUS_KEY_Shift_L || keyval == IBUS_KEY_Shift_R)) {
+- if (priv->tentative_match &&
+- g_unichar_validate (priv->tentative_match)) {
+- ibus_engine_simple_commit_char (simple, priv->tentative_match);
++ if (priv->tentative_match->len > 0) {
++ char *str = g_strdup (priv->tentative_match->str);
++ ibus_engine_simple_commit_str (simple, str);
++ g_free (str);
+ ibus_engine_simple_update_preedit_text (simple);
+ } else if (n_compose == 0) {
+ priv->modifiers_dropped = TRUE;
+ } else {
+ /* invalid hex sequence */
+ /* FIXME beep_window (event->window); */
+- priv->tentative_match = 0;
++ g_string_set_size (priv->tentative_match, 0);
+ g_clear_pointer (&priv->tentative_emoji, g_free);
+ priv->in_hex_sequence = FALSE;
+ priv->in_emoji_sequence = FALSE;
+@@ -873,7 +1011,7 @@ ibus_engine_simple_process_key_event (IBusEngine *engine,
+ } else {
+ /* invalid hex sequence */
+ /* FIXME beep_window (event->window); */
+- priv->tentative_match = 0;
++ g_string_set_size (priv->tentative_match, 0);
+ g_clear_pointer (&priv->tentative_emoji, g_free);
+ priv->in_hex_sequence = FALSE;
+ priv->in_emoji_sequence = FALSE;
+@@ -921,7 +1059,7 @@ ibus_engine_simple_process_key_event (IBusEngine *engine,
+ else if (is_hex_end) {
+ /* invalid hex sequence */
+ // beep_window (event->window);
+- priv->tentative_match = 0;
++ g_string_set_size (priv->tentative_match, 0);
+ g_clear_pointer (&priv->tentative_emoji, g_free);
+ priv->in_hex_sequence = FALSE;
+ priv->in_emoji_sequence = FALSE;
+@@ -999,7 +1137,8 @@ ibus_engine_simple_process_key_event (IBusEngine *engine,
+ if (n_compose > 0) {
+ n_compose--;
+ priv->compose_buffer[n_compose] = 0;
+- priv->tentative_match = 0;
++ g_string_set_size (priv->tentative_match, 0);
++ priv->tentative_match_len = 0;
+ ibus_engine_simple_check_all_compose_table (simple, n_compose);
+ return TRUE;
+ }
+@@ -1007,16 +1146,17 @@ ibus_engine_simple_process_key_event (IBusEngine *engine,
+
+ /* Check for hex sequence restart */
+ if (priv->in_hex_sequence && have_hex_mods && is_hex_start) {
+- if (priv->tentative_match &&
+- g_unichar_validate (priv->tentative_match)) {
+- ibus_engine_simple_commit_char (simple, priv->tentative_match);
++ if (priv->tentative_match->len > 0 ) {
++ char *str = g_strdup (priv->tentative_match->str);
++ ibus_engine_simple_commit_str (simple, str);
++ g_free (str);
+ ibus_engine_simple_update_preedit_text (simple);
+ }
+ else {
+ /* invalid hex sequence */
+ if (n_compose > 0) {
+ // FIXME beep_window (event->window);
+- priv->tentative_match = 0;
++ g_string_set_size (priv->tentative_match, 0);
+ priv->in_hex_sequence = FALSE;
+ priv->compose_buffer[0] = 0;
+ }
+@@ -1044,7 +1184,7 @@ ibus_engine_simple_process_key_event (IBusEngine *engine,
+ priv->in_hex_sequence = TRUE;
+ priv->in_emoji_sequence = FALSE;
+ priv->modifiers_dropped = FALSE;
+- priv->tentative_match = 0;
++ g_string_set_size (priv->tentative_match, 0);
+ g_clear_pointer (&priv->tentative_emoji, g_free);
+
+ // g_debug ("Start HEX MODE");
+@@ -1058,7 +1198,7 @@ ibus_engine_simple_process_key_event (IBusEngine *engine,
+ priv->in_hex_sequence = FALSE;
+ priv->in_emoji_sequence = TRUE;
+ priv->modifiers_dropped = FALSE;
+- priv->tentative_match = 0;
++ g_string_set_size (priv->tentative_match, 0);
+ g_clear_pointer (&priv->tentative_emoji, g_free);
+
+ // g_debug ("Start HEX MODE");
+@@ -1068,7 +1208,6 @@ ibus_engine_simple_process_key_event (IBusEngine *engine,
+ return TRUE;
+ }
+
+- /* Then, check for compose sequences */
+ if (priv->in_hex_sequence) {
+ if (hex_keyval) {
+ SET_COMPOSE_BUFFER_ELEMENT_NEXT (priv->compose_buffer,
+@@ -1133,17 +1272,17 @@ ibus_engine_simple_process_key_event (IBusEngine *engine,
+ if (have_hex_mods) {
+ /* space or return ends the sequence, and we eat the key */
+ if (n_compose > 0 && is_hex_end) {
+- if (priv->tentative_match &&
+- g_unichar_validate (priv->tentative_match)) {
+- ibus_engine_simple_commit_char (simple,
+- priv->tentative_match);
+- priv->compose_buffer[0] = 0;
++ if (priv->tentative_match->len > 0) {
++ char *str = g_strdup (priv->tentative_match->str);
++ ibus_engine_simple_commit_str (simple, str);
++ g_free (str);
+ ibus_engine_simple_update_preedit_text (simple);
++ return TRUE;
+ } else {
+ // FIXME
+ /* invalid hex sequence */
+ // beep_window (event->window);
+- priv->tentative_match = 0;
++ g_string_set_size (priv->tentative_match, 0);
+ priv->in_hex_sequence = FALSE;
+ priv->compose_buffer[0] = 0;
+ }
+@@ -1234,7 +1373,7 @@ ibus_engine_simple_process_key_event (IBusEngine *engine,
+
+ return TRUE;
+ }
+- } else {
++ } else { /* Then, check for compose sequences */
+ if (ibus_engine_simple_check_all_compose_table (simple, n_compose))
+ return TRUE;
+ }
+diff --git a/src/ibusenginesimpleprivate.h b/src/ibusenginesimpleprivate.h
+index 5479e553..ad93d2d2 100644
+--- a/src/ibusenginesimpleprivate.h
++++ b/src/ibusenginesimpleprivate.h
+@@ -26,6 +26,12 @@
+
+ G_BEGIN_DECLS
+
++/* Checks if a keysym is a dead key. Dead key keysym values are defined in
++ * ibuskeysyms.h and the first is GDK_KEY_dead_grave.
++ */
++#define IS_DEAD_KEY(k) \
++ ((k) >= IBUS_KEY_dead_grave && (k) <= IBUS_KEY_dead_greek)
++
+ extern const IBusComposeTableCompactEx ibus_compose_table_compact;
+ extern const IBusComposeTableCompactEx ibus_compose_table_compact_32bit;
+
+@@ -60,7 +66,8 @@ gboolean ibus_compose_table_compact_check
+ gboolean *compose_finish,
+ gunichar **output_chars);
+ gunichar ibus_keysym_to_unicode (guint16 keysym,
+- gboolean combining);
++ gboolean combining,
++ gboolean *need_space);
+
+ G_END_DECLS
+
+--
+2.28.0
+
+From 17ae266cade41e795534532acefc85716bb309ef Mon Sep 17 00:00:00 2001
+From: fujiwarat <takao.fujiwara1@gmail.com>
+Date: Mon, 26 Jul 2021 22:53:34 +0900
+Subject: [PATCH 6/6] Code reviews
+
+- Use fstat() and fchmod() but not stat() and chmod() to
+ fix race conditions
+- Avoid to use after free
+- Fix dereference of IBusComposeTable->priv
+- Fix to divide by zero
+---
+ src/ibusbus.c | 23 ++++++++++++++++++-----
+ src/ibuscomposetable.c | 8 ++++++--
+ src/tests/ibus-desktop-testing-runner.in | 6 +++---
+ 3 files changed, 27 insertions(+), 10 deletions(-)
+
+diff --git a/src/ibusbus.c b/src/ibusbus.c
+index e9b0bcbb..47400cb8 100644
+--- a/src/ibusbus.c
++++ b/src/ibusbus.c
+@@ -21,13 +21,14 @@
+ * USA
+ */
+
+-#include "ibusbus.h"
+ #include <errno.h>
++#include <fcntl.h>
+ #include <sys/types.h>
+ #include <sys/stat.h>
+ #include <unistd.h>
+ #include <glib/gstdio.h>
+ #include <gio/gio.h>
++#include "ibusbus.h"
+ #include "ibusmarshalers.h"
+ #include "ibusinternal.h"
+ #include "ibusshare.h"
+@@ -537,7 +538,8 @@ static void
+ ibus_bus_init (IBusBus *bus)
+ {
+ struct stat buf;
+- gchar *path;
++ char *path;
++ int fd;
+
+ bus->priv = IBUS_BUS_GET_PRIVATE (bus);
+
+@@ -562,20 +564,31 @@ ibus_bus_init (IBusBus *bus)
+ return;
+ }
+
+- if (stat (path, &buf) == 0) {
++ errno = 0;
++ if ((fd = open (path, O_RDONLY | O_DIRECTORY, S_IRWXU)) == -1) {
++ g_warning ("open %s failed: %s", path, g_strerror (errno));
++ g_free (path);
++ return;
++ }
++ /* TOCTOU: Use fstat() and fchmod() but not stat() and chmod().
++ * because it can cause a time-of-check, time-of-use race condition.
++ */
++ if (fstat (fd, &buf) == 0) {
+ if (buf.st_uid != getuid ()) {
+ g_warning ("The owner of %s is not %s!",
+ path, ibus_get_user_name ());
++ close (fd);
+ g_free (path);
+ return;
+ }
+ if (buf.st_mode != (S_IFDIR | S_IRWXU)) {
+ errno = 0;
+- if (g_chmod (path, 0700))
+- g_warning ("chmod failed: %s", errno ? g_strerror (errno) : "");
++ if (fchmod (fd, S_IRWXU))
++ g_warning ("chmod failed: %s", g_strerror (errno));
+ }
+ }
+
++ close (fd);
+ g_free (path);
+ }
+
+diff --git a/src/ibuscomposetable.c b/src/ibuscomposetable.c
+index 2ad21551..a0b0befc 100644
+--- a/src/ibuscomposetable.c
++++ b/src/ibuscomposetable.c
+@@ -406,7 +406,7 @@ get_en_compose_file (void)
+ path = g_build_filename (X11_DATADIR, *sys_lang, "Compose", NULL);
+ if (g_file_test (path, G_FILE_TEST_EXISTS))
+ break;
+- g_free (path);
++ g_clear_pointer (&path, g_free);
+ }
+ return path;
+ }
+@@ -975,7 +975,10 @@ ibus_compose_table_deserialize (const char *contents,
+ goto out_load_cache;
+ }
+ if (data_length) {
+- retval->priv = g_new0 (IBusComposeTablePrivate, 1);
++ if (!(retval->priv = g_new0 (IBusComposeTablePrivate, 1))) {
++ g_warning ("Failed g_new");
++ goto out_load_cache;
++ }
+ retval->priv->data_first = g_new (guint16, data_length);
+ memcpy (retval->priv->data_first,
+ data_32bit_first, data_length * sizeof (guint16));
+@@ -1565,6 +1568,7 @@ ibus_compose_table_compact_check (const IBusComposeTableCompactEx
+ row_stride = i + 2;
+
+ if (seq_index[i + 1] - seq_index[i] > 0) {
++ g_assert (row_stride);
+ seq = bsearch (compose_buffer + 1,
+ table->data + seq_index[i],
+ (seq_index[i + 1] - seq_index[i]) / row_stride,
+diff --git a/src/tests/ibus-desktop-testing-runner.in b/src/tests/ibus-desktop-testing-runner.in
+index 0ef72c03..c1016703 100755
+--- a/src/tests/ibus-desktop-testing-runner.in
++++ b/src/tests/ibus-desktop-testing-runner.in
+@@ -220,7 +220,7 @@ init_gnome()
+ # G_MESSAGES_DEBUG=all or G_MESSAGES_DEBUG=GLib-GIO-DEBUG would append
+ # debug messages to gsettings output and could not get the result correctly.
+ backup_G_MESSAGES_DEBUG="$G_MESSAGES_DEBUG"
+- export -n G_MESSAGES_DEBUG=''
++ unset G_MESSAGES_DEBUG
+ # Disable Tour dialog to get focus
+ V=`gsettings get org.gnome.shell welcome-dialog-last-shown-version`
+ if [ x"$V" = x"''" ] ; then
+@@ -231,8 +231,8 @@ init_gnome()
+ NO_SYS_DIR=/usr/share/gnome-shell/extensions/no-overview@fthx
+ NO_USER_DIR=$HOME/.local/share/gnome-shell/extensions/no-overview@fthx
+ if [ ! -d $NO_SYS_DIR ] && [ ! -d $NO_USER_DIR ] ; then
+- mkdir -p `dirname $NO_USER_DIR`
+- cp -R no-overview@fthx `dirname $NO_USER_DIR`
++ mkdir -p "`dirname $NO_USER_DIR`"
++ cp -R "no-overview@fthx" "`dirname $NO_USER_DIR`"
+ fi
+ V=`gsettings get org.gnome.shell disable-user-extensions`
+ if [ x"$V" = x"true" ] ; then
+--
+2.28.0
+
diff --git a/ibus.spec b/ibus.spec
index 41f4cb5..4ba4a52 100644
--- a/ibus.spec
+++ b/ibus.spec
@@ -38,7 +38,7 @@
Name: ibus
Version: 1.5.24
-Release: 11%{?dist}
+Release: 12%{?dist}
Summary: Intelligent Input Bus for Linux OS
License: LGPLv2+
URL: https://github.com/ibus/%name/wiki
@@ -510,6 +510,11 @@ dconf update || :
%{_datadir}/installed-tests/ibus
%changelog
+* Mon Jul 26 2021 Takao Fujiwara <tfujiwar@redhat.com> - 1.5.24-12
+- Search language name in engine list in ibus-setup
+- Set Multi_key to 0xB7 in compose preedit
+- Make Compose preedit less intrusive
+
* Thu Jul 22 2021 Fedora Release Engineering <releng@fedoraproject.org> - 1.5.24-11
- Rebuilt for https://fedoraproject.org/wiki/Fedora_35_Mass_Rebuild
^ permalink raw reply related [flat|nested] only message in thread
only message in thread, other threads:[~2026-05-31 2:07 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-05-31 2:07 [rpms/ibus] autotool: Make Compose preedit less intrusive Takao Fujiwara
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox