[gtk/im-context-work-for-3] imcontext: Backport compose sequence improvements




commit 85d34932f3888caecd4b50dcfce530c8b82a7a5b
Author: Matthias Clasen <mclasen redhat com>
Date:   Mon Feb 8 20:28:49 2021 -0500

    imcontext: Backport compose sequence improvements
    
    Improve compose sequence handling:
    - Show preedit for compose seqences
    - Support sequences of up to 20 code points
    - Warn when ignoring Compose file features
    - Support compose sequences producing multiple characters
    - Support hex escapes

 gtk/gtkcomposetable.c           |  823 +++++++++++++++++++++++---------
 gtk/gtkcomposetable.h           |   45 +-
 gtk/gtkimcontextsimple.c        | 1007 ++++++++++-----------------------------
 gtk/gtkimcontextsimpleprivate.h |   42 --
 4 files changed, 900 insertions(+), 1017 deletions(-)
---
diff --git a/gtk/gtkcomposetable.c b/gtk/gtkcomposetable.c
index fcc171b184..95cb16f9b8 100644
--- a/gtk/gtkcomposetable.c
+++ b/gtk/gtkcomposetable.c
@@ -26,16 +26,17 @@
 #include "gtkcomposetable.h"
 #include "gtkimcontextsimple.h"
 
-#include "gtkimcontextsimpleprivate.h"
-
 
 #define GTK_COMPOSE_TABLE_MAGIC "GtkComposeTable"
-#define GTK_COMPOSE_TABLE_VERSION (1)
+#define GTK_COMPOSE_TABLE_VERSION (2)
+
+/* Maximum length of sequences we parse */
+
+#define MAX_COMPOSE_LEN 20
 
 typedef struct {
-  gunichar     *sequence;
-  gunichar      value[2];
-  gchar        *comment;
+  gunichar *sequence;
+  char *value;
 } GtkComposeData;
 
 
@@ -43,7 +44,7 @@ static void
 gtk_compose_data_free (GtkComposeData *compose_data)
 {
   g_free (compose_data->sequence);
-  g_free (compose_data->comment);
+  g_free (compose_data->value);
   g_slice_free (GtkComposeData, compose_data);
 }
 
@@ -54,7 +55,7 @@ gtk_compose_list_element_free (GtkComposeData *compose_data, gpointer data)
 }
 
 static gboolean
-is_codepoint (const gchar *str)
+is_codepoint (const char *str)
 {
   int i;
 
@@ -73,70 +74,94 @@ is_codepoint (const gchar *str)
 
 static gboolean
 parse_compose_value (GtkComposeData *compose_data,
-                     const gchar    *val,
-                     const gchar    *line)
+                     const char     *val,
+                     const char     *line)
 {
-  gchar **words = g_strsplit (val, "\"", 3);
-  gunichar uch;
-
-  if (g_strv_length (words) < 3)
+  char *word;
+  const char *p;
+  gsize len;
+  GString *value;
+  gunichar ch;
+  char *endp;
+
+  len = strlen (val);
+  if (val[0] != '"' || val[len - 1] != '"')
     {
       g_warning ("Need to double-quote the value: %s: %s", val, line);
       goto fail;
     }
 
-  uch = g_utf8_get_char (words[1]);
+  word = g_strndup (val + 1, len - 2);
 
-  if (uch == 0)
-    {
-      g_warning ("Invalid value: %s: %s", val, line);
-      goto fail;
-    }
-  else if (uch == '\\')
-    {
-      uch = words[1][1];
+  value = g_string_new ("");
 
-      /* The escaped string "\"" is separated with '\\' and '"'. */
-      if (uch == '\0' && words[2][0] == '"')
-        uch = '"';
-      /* The escaped octal */
-      else if (uch >= '0' && uch <= '8')
-        uch = g_ascii_strtoll(words[1] + 1, NULL, 8);
-      /* If we need to handle other escape sequences. */
-      else if (uch != '\\')
+  p = word;
+  while (*p)
+    {
+      if (*p == '\\')
         {
-          g_warning ("Invalid escape sequence: %s: %s", val, line);
+          if (p[1] == '"')
+            {
+              g_string_append_c (value, '"');
+              p += 2;
+            }
+          else if (p[1] == '\\')
+            {
+              g_string_append_c (value, '\\');
+              p += 2;
+            }
+          else if (p[1] >= '0' && p[1] < '8')
+            {
+              ch = g_ascii_strtoll (p + 1, &endp, 8);
+              if (ch == 0)
+                {
+                  g_warning ("Invalid escape sequence: %s: %s", val, line);
+                  goto fail;
+                }
+              g_string_append_unichar (value, ch);
+              p = endp;
+            }
+          else if (p[1] == 'x' || p[1] == 'X')
+            {
+              ch = g_ascii_strtoll (p + 2, &endp, 16);
+              if (ch == 0)
+                {
+                  g_warning ("Invalid escape sequence: %s: %s", val, line);
+                  goto fail;
+                }
+              g_string_append_unichar (value, ch);
+              p = endp;
+            }
+          else
+            {
+              g_warning ("Invalid escape sequence: %s: %s", val, line);
+              goto fail;
+            }
+        }
+      else
+        {
+          ch = g_utf8_get_char (p);
+          g_string_append_unichar (value, ch);
+          p = g_utf8_next_char (p);
         }
     }
 
-  if (g_utf8_get_char (g_utf8_next_char (words[1])) > 0)
-    {
-      g_warning ("GTK+ supports to output one char only: %s: %s", val, line);
-      goto fail;
-    }
-
-  compose_data->value[1] = uch;
+  compose_data->value = g_string_free (value, FALSE);
 
-  if (uch == '"')
-    compose_data->comment = g_strdup (g_strstrip (words[2] + 1));
-  else
-    compose_data->comment = g_strdup (g_strstrip (words[2]));
-
-  g_strfreev (words);
+  g_free (word);
 
   return TRUE;
 
 fail:
-  g_strfreev (words);
   return FALSE;
 }
 
 static gboolean
 parse_compose_sequence (GtkComposeData *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;
 
@@ -148,9 +173,9 @@ parse_compose_sequence (GtkComposeData *compose_data,
 
   for (i = 1; words[i] != NULL; i++)
     {
-      gchar *start = words[i];
-      gchar *end = strchr (words[i], '>');
-      gchar *match;
+      char *start = words[i];
+      char *end = strchr (words[i], '>');
+      char *match;
       gunichar codepoint;
 
       if (words[i][0] == '\0')
@@ -189,10 +214,10 @@ parse_compose_sequence (GtkComposeData *compose_data,
     }
 
   g_strfreev (words);
-  if (0 == n || n >= GTK_MAX_COMPOSE_LEN)
+  if (0 == n || n > MAX_COMPOSE_LEN)
     {
-      g_warning ("The max number of sequences is %d: %s",
-                 GTK_MAX_COMPOSE_LEN, line);
+      g_warning ("Suspicious compose sequence length (%d). Are you sure this is right?: %s",
+                 n, line);
       return FALSE;
     }
 
@@ -205,16 +230,19 @@ fail:
 
 static void
 parse_compose_line (GList       **compose_list,
-                    const gchar  *line)
+                    const char   *line)
 {
-  gchar **components = NULL;
+  char **components = NULL;
   GtkComposeData *compose_data = NULL;
 
   if (line[0] == '\0' || line[0] == '#')
     return;
 
   if (g_str_has_prefix (line, "include "))
-    return;
+    {
+      g_warning ("include in Compose files not supported: %s", line);
+      return;
+    }
 
   components = g_strsplit (line, ":", 2);
 
@@ -244,11 +272,13 @@ fail:
     gtk_compose_data_free (compose_data);
 }
 
+extern const GtkComposeTableCompact gtk_compose_table_compact;
+
 static GList *
-gtk_compose_list_parse_file (const gchar *compose_file)
+gtk_compose_list_parse_file (const char *compose_file)
 {
-  gchar *contents = NULL;
-  gchar **lines = NULL;
+  char *contents = NULL;
+  char **lines = NULL;
   gsize length = 0;
   GError *error = NULL;
   GList *compose_list = NULL;
@@ -279,18 +309,19 @@ gtk_compose_list_check_duplicated (GList *compose_list)
 
   for (list = compose_list; list != NULL; list = list->next)
     {
-      static guint16 keysyms[GTK_MAX_COMPOSE_LEN + 1];
+      static guint16 keysyms[MAX_COMPOSE_LEN + 1];
       int i;
       int n_compose = 0;
       gboolean compose_finish;
       gunichar output_char;
+      char buf[8] = { 0, };
 
       compose_data = list->data;
 
-      for (i = 0; i < GTK_MAX_COMPOSE_LEN + 1; i++)
+      for (i = 0; i < MAX_COMPOSE_LEN + 1; i++)
         keysyms[i] = 0;
 
-      for (i = 0; i < GTK_MAX_COMPOSE_LEN + 1; i++)
+      for (i = 0; i < MAX_COMPOSE_LEN + 1; i++)
         {
           gunichar codepoint = compose_data->sequence[i];
           keysyms[i] = (guint16) codepoint;
@@ -301,20 +332,21 @@ gtk_compose_list_check_duplicated (GList *compose_list)
           n_compose++;
         }
 
-      if (gtk_check_compact_table (&gtk_compose_table_compact,
-                                   keysyms,
-                                   n_compose,
-                                   &compose_finish,
-                                   NULL,
-                                   &output_char) &&
+      if (gtk_compose_table_compact_check (&gtk_compose_table_compact,
+                                           keysyms, n_compose,
+                                           &compose_finish,
+                                           NULL,
+                                           &output_char) &&
           compose_finish)
         {
-          if (compose_data->value[1] == output_char)
+          g_unichar_to_utf8 (output_char, buf);
+          if (strcmp (compose_data->value, buf) == 0)
             removed_list = g_list_prepend (removed_list, compose_data);
         }
       else if (gtk_check_algorithmically (keysyms, n_compose, &output_char))
         {
-          if (compose_data->value[1] == output_char)
+          g_unichar_to_utf8 (output_char, buf);
+          if (strcmp (compose_data->value, buf) == 0)
             removed_list = g_list_prepend (removed_list, compose_data);
         }
     }
@@ -343,7 +375,7 @@ gtk_compose_list_check_uint16 (GList *compose_list)
       int i;
 
       compose_data = list->data;
-      for (i = 0; i < GTK_MAX_COMPOSE_LEN; i++)
+      for (i = 0; i < MAX_COMPOSE_LEN; i++)
         {
           gunichar codepoint = compose_data->sequence[i];
 
@@ -384,7 +416,7 @@ gtk_compose_list_format_for_gtk (GList *compose_list,
   for (list = compose_list; list != NULL; list = list->next)
     {
       compose_data = list->data;
-      for (i = 0; i < GTK_MAX_COMPOSE_LEN + 1; i++)
+      for (i = 0; i < MAX_COMPOSE_LEN + 1; i++)
         {
           codepoint = compose_data->sequence[i];
           if (codepoint == 0)
@@ -401,21 +433,10 @@ gtk_compose_list_format_for_gtk (GList *compose_list,
   if (p_n_index_stride)
     *p_n_index_stride = max_compose_len + 2;
 
-  for (list = compose_list; list != NULL; list = list->next)
-    {
-      compose_data = list->data;
-      codepoint = compose_data->value[1];
-      if (codepoint > 0xffff)
-        {
-          compose_data->value[0] = codepoint / 0x10000;
-          compose_data->value[1] = codepoint - codepoint / 0x10000 * 0x10000;
-        }
-    }
-
   return compose_list;
 }
 
-static gint
+static int
 gtk_compose_data_compare (gpointer a,
                           gpointer b,
                           gpointer data)
@@ -436,61 +457,6 @@ gtk_compose_data_compare (gpointer a,
   return 0;
 }
 
-static void
-gtk_compose_list_print (GList *compose_list,
-                        int    max_compose_len,
-                        int    n_index_stride)
-{
-  GList *list;
-  int i, j;
-  GtkComposeData *compose_data;
-  int total_size = 0;
-  gunichar upper;
-  gunichar lower;
-  const gchar *comment;
-  const gchar *keyval;
-
-  for (list = compose_list; list != NULL; list = list->next)
-    {
-      compose_data = list->data;
-      g_printf ("  ");
-
-      for (i = 0; i < max_compose_len; i++)
-        {
-          if (compose_data->sequence[i] == 0)
-            {
-              for (j = i; j < max_compose_len; j++)
-                {
-                    if (j == max_compose_len - 1)
-                      g_printf ("0,\n");
-                    else
-                      g_printf ("0, ");
-                }
-              break;
-            }
-
-          keyval = gdk_keyval_name (compose_data->sequence[i]);
-          if (i == max_compose_len - 1)
-            g_printf ("%s,\n", keyval ? keyval : "(null)");
-          else
-            g_printf ("%s, ", keyval ? keyval : "(null)");
-        }
-      upper = compose_data->value[0];
-      lower = compose_data->value[1];
-      comment = compose_data->comment;
-
-      if (list == g_list_last (compose_list))
-        g_printf ("    %#06X, %#06X  /* %s */\n", upper, lower, comment);
-      else
-        g_printf ("    %#06X, %#06X, /* %s */\n", upper, lower, comment);
-
-      total_size += n_index_stride;
-    }
-
-  g_printerr ("TOTAL_SIZE: %d\nMAX_COMPOSE_LEN: %d\nN_INDEX_STRIDE: %d\n",
-              total_size, max_compose_len, n_index_stride);
-}
-
 /* Implemented from g_str_hash() */
 static guint32
 gtk_compose_table_data_hash (gconstpointer v, int length)
@@ -510,12 +476,12 @@ gtk_compose_table_data_hash (gconstpointer v, int length)
   return h;
 }
 
-static gchar *
+static char *
 gtk_compose_hash_get_cache_path (guint32 hash)
 {
-  gchar *basename = NULL;
-  gchar *dir = NULL;
-  gchar *path = NULL;
+  char *basename = NULL;
+  char *dir = NULL;
+  char *path = NULL;
 
   basename = g_strdup_printf ("%08x.cache", hash);
 
@@ -534,18 +500,19 @@ gtk_compose_hash_get_cache_path (guint32 hash)
   return path;
 }
 
-static gchar *
+static char *
 gtk_compose_table_serialize (GtkComposeTable *compose_table,
                              gsize           *count)
 {
-  gchar *p, *contents;
+  char *p, *contents;
   gsize length, total_length;
   guint16 bytes;
-  const gchar *header = GTK_COMPOSE_TABLE_MAGIC;
+  const char *header = GTK_COMPOSE_TABLE_MAGIC;
   const guint16 version = GTK_COMPOSE_TABLE_VERSION;
   guint16 max_seq_len = compose_table->max_seq_len;
   guint16 index_stride = max_seq_len + 2;
   guint16 n_seqs = compose_table->n_seqs;
+  guint16 n_chars = compose_table->n_chars;
   guint32 i;
 
   g_return_val_if_fail (compose_table != NULL, NULL);
@@ -553,49 +520,39 @@ gtk_compose_table_serialize (GtkComposeTable *compose_table,
   g_return_val_if_fail (index_stride > 0, NULL);
 
   length = strlen (header);
-  total_length = length + sizeof (guint16) * (3 +  index_stride * n_seqs);
+  total_length = length + sizeof (guint16) * (4 + index_stride * n_seqs) + n_chars;
   if (count)
     *count = total_length;
 
-  p = contents = g_slice_alloc (total_length);
+  p = contents = g_malloc (total_length);
 
   memcpy (p, header, length);
   p += length;
 
-  /* Copy by byte for endian */
-#define BYTE_COPY_FROM_BUF(element)                                           \
-  bytes = GUINT16_TO_BE ((element));                                          \
-  memcpy (p, &bytes, length);                                                 \
-  p += length;                                                                \
-  if (p - contents > total_length)                                            \
-    {                                                                         \
-      g_warning ("data size %lld is bigger than %" G_GSIZE_FORMAT,            \
-                 (long long) (p - contents), total_length);                   \
-      g_free (contents);                                                      \
-      if (count)                                                              \
-        {                                                                     \
-          *count = 0;                                                         \
-        }                                                                     \
-      return NULL;                                                            \
-    }
-
-  length = sizeof (guint16);
-
-  BYTE_COPY_FROM_BUF (version);
-  BYTE_COPY_FROM_BUF (max_seq_len);
-  BYTE_COPY_FROM_BUF (n_seqs);
+#define APPEND_GUINT16(elt) \
+  bytes = GUINT16_TO_BE (elt); \
+  memcpy (p, &bytes, sizeof (guint16)); \
+  p += sizeof (guint16);
+
+  APPEND_GUINT16 (version);
+  APPEND_GUINT16 (max_seq_len);
+  APPEND_GUINT16 (n_seqs);
+  APPEND_GUINT16 (n_chars);
 
   for (i = 0; i < (guint32) index_stride * n_seqs; i++)
     {
-      BYTE_COPY_FROM_BUF (compose_table->data[i]);
+      APPEND_GUINT16 (compose_table->data[i]);
     }
 
-#undef BYTE_COPY_FROM_BUF
+  if (compose_table->n_chars > 0)
+    memcpy (p, compose_table->char_data, compose_table->n_chars);
+
+#undef APPEND_GUINT16
 
   return contents;
 }
 
-static gint
+static int
 gtk_compose_table_find (gconstpointer data1,
                         gconstpointer data2)
 {
@@ -605,25 +562,26 @@ gtk_compose_table_find (gconstpointer data1,
 }
 
 static GtkComposeTable *
-gtk_compose_table_load_cache (const gchar *compose_file)
+gtk_compose_table_load_cache (const char *compose_file)
 {
   guint32 hash;
-  gchar *path = NULL;
-  gchar *contents = NULL;
-  gchar *p;
+  char *path = NULL;
+  char *contents = NULL;
+  char *p;
   GStatBuf original_buf;
   GStatBuf cache_buf;
   gsize total_length;
-  gsize length;
   GError *error = NULL;
   guint16 bytes;
   guint16 version;
   guint16 max_seq_len;
   guint16 index_stride;
   guint16 n_seqs;
+  guint16 n_chars;
   guint32 i;
   guint16 *gtk_compose_seqs = NULL;
   GtkComposeTable *retval;
+  char *char_data = NULL;
 
   hash = g_str_hash (compose_file);
   if ((path = gtk_compose_hash_get_cache_path (hash)) == NULL)
@@ -642,16 +600,10 @@ gtk_compose_table_load_cache (const gchar *compose_file)
       goto out_load_cache;
     }
 
-  /* Copy by byte for endian */
-#define BYTE_COPY_TO_BUF(element)                                       \
-  memcpy (&bytes, p, length);                                           \
-  element = GUINT16_FROM_BE (bytes);                                    \
-  p += length;                                                          \
-  if (p - contents > total_length)                                      \
-    {                                                                   \
-      g_warning ("Broken cache content %s in %s", path, #element);      \
-      goto out_load_cache;                                              \
-    }
+#define GET_GUINT16(elt) \
+  memcpy (&bytes, p, sizeof (guint16)); \
+  elt = GUINT16_FROM_BE (bytes); \
+  p += sizeof (guint16);
 
   p = contents;
   if (g_ascii_strncasecmp (p, GTK_COMPOSE_TABLE_MAGIC,
@@ -660,6 +612,7 @@ gtk_compose_table_load_cache (const gchar *compose_file)
       g_warning ("The file is not a GtkComposeTable cache file %s", path);
       goto out_load_cache;
     }
+
   p += strlen (GTK_COMPOSE_TABLE_MAGIC);
   if (p - contents > total_length)
     {
@@ -667,9 +620,7 @@ gtk_compose_table_load_cache (const gchar *compose_file)
       goto out_load_cache;
     }
 
-  length = sizeof (guint16);
-
-  BYTE_COPY_TO_BUF (version);
+  GET_GUINT16 (version);
   if (version != GTK_COMPOSE_TABLE_VERSION)
     {
       g_warning ("cache version is different %u != %u",
@@ -677,8 +628,9 @@ gtk_compose_table_load_cache (const gchar *compose_file)
       goto out_load_cache;
     }
 
-  BYTE_COPY_TO_BUF (max_seq_len);
-  BYTE_COPY_TO_BUF (n_seqs);
+  GET_GUINT16 (max_seq_len);
+  GET_GUINT16 (n_seqs);
+  GET_GUINT16 (n_chars);
 
   if (max_seq_len == 0 || n_seqs == 0)
     {
@@ -691,13 +643,22 @@ gtk_compose_table_load_cache (const gchar *compose_file)
 
   for (i = 0; i < (guint32) index_stride * n_seqs; i++)
     {
-      BYTE_COPY_TO_BUF (gtk_compose_seqs[i]);
+      GET_GUINT16 (gtk_compose_seqs[i]);
+    }
+
+  if (n_chars > 0)
+    {
+      char_data = g_new (char, n_chars + 1);
+      memcpy (char_data, p, n_chars);
+      char_data[n_chars] = '\0';
     }
 
   retval = g_new0 (GtkComposeTable, 1);
   retval->data = gtk_compose_seqs;
   retval->max_seq_len = max_seq_len;
   retval->n_seqs = n_seqs;
+  retval->char_data = char_data;
+  retval->n_chars = n_chars;
   retval->id = hash;
 
   g_free (contents);
@@ -705,10 +666,11 @@ gtk_compose_table_load_cache (const gchar *compose_file)
 
   return retval;
 
-#undef BYTE_COPY_TO_BUF
+#undef GET_GUINT16
 
 out_load_cache:
   g_free (gtk_compose_seqs);
+  g_free (char_data);
   g_free (contents);
   g_free (path);
   return NULL;
@@ -717,8 +679,8 @@ out_load_cache:
 static void
 gtk_compose_table_save_cache (GtkComposeTable *compose_table)
 {
-  gchar *path = NULL;
-  gchar *contents = NULL;
+  char *path = NULL;
+  char *contents = NULL;
   GError *error = NULL;
   gsize length = 0;
 
@@ -739,7 +701,7 @@ gtk_compose_table_save_cache (GtkComposeTable *compose_table)
     }
 
 out_save_cache:
-  g_slice_free1 (length, contents);
+  g_free (contents);
   g_free (path);
 }
 
@@ -756,6 +718,8 @@ gtk_compose_table_new_with_list (GList   *compose_list,
   GList *list;
   GtkComposeData *compose_data;
   GtkComposeTable *retval = NULL;
+  gunichar codepoint;
+  GString *char_data;
 
   g_return_val_if_fail (compose_list != NULL, NULL);
 
@@ -763,6 +727,8 @@ gtk_compose_table_new_with_list (GList   *compose_list,
 
   gtk_compose_seqs = g_new0 (guint16, length * n_index_stride);
 
+  char_data = g_string_new ("");
+
   for (list = compose_list; list != NULL; list = list->next)
     {
       compose_data = list->data;
@@ -776,8 +742,24 @@ gtk_compose_table_new_with_list (GList   *compose_list,
             }
           gtk_compose_seqs[n++] = (guint16) compose_data->sequence[i];
         }
-      gtk_compose_seqs[n++] = (guint16) compose_data->value[0];
-      gtk_compose_seqs[n++] = (guint16) compose_data->value[1];
+
+      if (g_utf8_strlen (compose_data->value, -1) > 1)
+        {
+          if (char_data->len > 0)
+            g_string_append_c (char_data, 0);
+
+          codepoint = char_data->len | (1 << 31);
+
+          g_string_append (char_data, compose_data->value);
+        }
+      else
+        {
+          codepoint = g_utf8_get_char (compose_data->value);
+          g_assert ((codepoint & (1 << 31)) == 0);
+        }
+
+      gtk_compose_seqs[n++] = (codepoint & 0xffff0000) >> 16;
+      gtk_compose_seqs[n++] = codepoint & 0xffff;
     }
 
   retval = g_new0 (GtkComposeTable, 1);
@@ -785,12 +767,14 @@ gtk_compose_table_new_with_list (GList   *compose_list,
   retval->max_seq_len = max_compose_len;
   retval->n_seqs = length;
   retval->id = hash;
+  retval->n_chars = char_data->len;
+  retval->char_data = g_string_free (char_data, FALSE);
 
   return retval;
 }
 
 GtkComposeTable *
-gtk_compose_table_new_with_file (const gchar *compose_file)
+gtk_compose_table_new_with_file (const char *compose_file)
 {
   GList *compose_list = NULL;
   GtkComposeTable *compose_table;
@@ -816,9 +800,6 @@ gtk_compose_table_new_with_file (const gchar *compose_file)
       return NULL;
     }
 
-  if (g_getenv ("GTK_COMPOSE_TABLE_PRINT") != NULL)
-    gtk_compose_list_print (compose_list, max_compose_len, n_index_stride);
-
   compose_table = gtk_compose_table_new_with_list (compose_list,
                                                    max_compose_len,
                                                    n_index_stride,
@@ -830,18 +811,26 @@ gtk_compose_table_new_with_file (const gchar *compose_file)
 GSList *
 gtk_compose_table_list_add_array (GSList        *compose_tables,
                                   const guint16 *data,
-                                  gint           max_seq_len,
-                                  gint           n_seqs)
+                                  int            max_seq_len,
+                                  int            n_seqs)
 {
   guint32 hash;
   GtkComposeTable *compose_table;
-  int n_index_stride = max_seq_len + 2;
-  int length = n_index_stride * n_seqs;
+  gsize n_index_stride;
+  gsize length;
   int i;
   guint16 *gtk_compose_seqs = NULL;
 
   g_return_val_if_fail (data != NULL, compose_tables);
-  g_return_val_if_fail (max_seq_len <= GTK_MAX_COMPOSE_LEN, compose_tables);
+  g_return_val_if_fail (max_seq_len >= 0, compose_tables);
+  g_return_val_if_fail (n_seqs >= 0, compose_tables);
+
+  n_index_stride = max_seq_len + 2;
+  if (!g_size_checked_mul (&length, n_index_stride, n_seqs))
+    {
+      g_critical ("Overflow in the compose sequences");
+      return compose_tables;
+    }
 
   hash = gtk_compose_table_data_hash (data, length);
 
@@ -857,13 +846,15 @@ gtk_compose_table_list_add_array (GSList        *compose_tables,
   compose_table->max_seq_len = max_seq_len;
   compose_table->n_seqs = n_seqs;
   compose_table->id = hash;
+  compose_table->char_data = NULL;
+  compose_table->n_chars = 0;
 
   return g_slist_prepend (compose_tables, compose_table);
 }
 
 GSList *
-gtk_compose_table_list_add_file (GSList      *compose_tables,
-                                 const gchar *compose_file)
+gtk_compose_table_list_add_file (GSList     *compose_tables,
+                                 const char *compose_file)
 {
   guint32 hash;
   GtkComposeTable *compose_table;
@@ -884,3 +875,409 @@ gtk_compose_table_list_add_file (GSList      *compose_tables,
   gtk_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;
+}
+
+/*
+ * gtk_compose_table_check:
+ * @table: the table to check
+ * @compose_buffer: the key vals to match
+ * @n_compose: number of non-zero key vals in @compose_buffer
+ * @compose_finish: (out): return location for whether there may be longer matches
+ * @compose_match: (out): return location for whether there is a match
+ * @output: (out) (caller-allocates): return location for the match values
+ *
+ * Looks for matches for a key sequence in @table.
+ *
+ * Returns: %TRUE if there were any matches, %FALSE otherwise
+ */
+gboolean
+gtk_compose_table_check (const GtkComposeTable *table,
+                         const guint16         *compose_buffer,
+                         int                    n_compose,
+                         gboolean              *compose_finish,
+                         gboolean              *compose_match,
+                         GString               *output)
+{
+  int row_stride = table->max_seq_len + 2;
+  guint16 *seq;
+
+  *compose_finish = FALSE;
+  *compose_match = FALSE;
+
+  g_string_set_size (output, 0);
+
+  /* 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 = bsearch (compose_buffer,
+                 table->data, table->n_seqs,
+                 sizeof (guint16) * row_stride,
+                 compare_seq);
+
+  if (seq)
+    {
+      guint16 *prev_seq;
+
+      /* Back up to the first sequence that matches to make sure
+       * we find the exact match if there is one.
+       */
+      while (seq > table->data)
+        {
+          prev_seq = seq - row_stride;
+          if (compare_seq (compose_buffer, prev_seq) != 0)
+            break;
+          seq = prev_seq;
+        }
+
+      if (n_compose == table->max_seq_len ||
+          seq[n_compose] == 0) /* complete sequence */
+        {
+          guint16 *next_seq;
+          gunichar value;
+
+          value = (seq[table->max_seq_len] << 16) | seq[table->max_seq_len + 1];
+          if ((value & (1 << 31)) != 0)
+            g_string_append (output, &table->char_data[value & ~(1 << 31)]);
+          else
+            g_string_append_unichar (output, value);
+
+          *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 < table->data + row_stride * table->n_seqs)
+            {
+              if (compare_seq (compose_buffer, next_seq) == 0)
+                return TRUE;
+            }
+
+          *compose_finish = TRUE;
+          return TRUE;
+        }
+
+      return TRUE;
+    }
+
+  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;
+}
+
+gboolean
+gtk_compose_table_compact_check (const GtkComposeTableCompact  *table,
+                                 const guint16                 *compose_buffer,
+                                 int                            n_compose,
+                                 gboolean                      *compose_finish,
+                                 gboolean                      *compose_match,
+                                 gunichar                      *output_char)
+{
+  int row_stride;
+  guint16 *seq_index;
+  guint16 *seq;
+  int i;
+  gboolean match;
+  gunichar value;
+
+  if (compose_finish)
+    *compose_finish = FALSE;
+  if (compose_match)
+    *compose_match = FALSE;
+  if (output_char)
+    *output_char = 0;
+
+  /* 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)
+    return FALSE;
+
+  if (n_compose == 1)
+    return TRUE;
+
+  seq = NULL;
+  match = FALSE;
+  value = 0;
+
+  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)
+                {
+                  value = seq[row_stride - 1];
+                  match = TRUE;
+                }
+              else
+                {
+                  if (output_char)
+                    *output_char = value;
+                  if (match)
+                    {
+                      if (compose_match)
+                        *compose_match = TRUE;
+                    }
+
+                  return TRUE;
+                }
+            }
+        }
+    }
+
+  if (match)
+    {
+      if (compose_match)
+        *compose_match = TRUE;
+      if (compose_finish)
+        *compose_finish = TRUE;
+      if (output_char)
+        *output_char = value;
+
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+/* 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.
+ */
+#define IS_DEAD_KEY(k) \
+    ((k) >= GDK_KEY_dead_grave && (k) <= GDK_KEY_dead_greek)
+
+/* This function receives a sequence of Unicode characters and tries to
+ * normalize it (NFC). We check for the case where 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,
+                     int       n_compose)
+{
+  gunichar *combination_buffer_temp;
+  char *combination_utf8_temp = NULL;
+  char *nfc_temp = NULL;
+  int n_combinations;
+  gunichar temp_swap;
+  int i;
+
+  combination_buffer_temp = g_alloca (n_compose * sizeof (gunichar));
+
+  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, n_compose * 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, n_compose, 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, n_compose * 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)
+        {
+          temp_swap = combination_buffer_temp[i % (n_compose - 1) + 1];
+          combination_buffer_temp[i % (n_compose - 1) + 1] = combination_buffer_temp[(i+1) % (n_compose - 1) 
+ 1];
+          combination_buffer_temp[(i+1) % (n_compose - 1) + 1] = temp_swap;
+        }
+      else
+        break;
+    }
+
+  return FALSE;
+}
+
+gboolean
+gtk_check_algorithmically (const guint16 *compose_buffer,
+                           int            n_compose,
+                           gunichar      *output_char)
+
+{
+  int i;
+  gunichar *combination_buffer;
+  char *combination_utf8, *nfc;
+
+  combination_buffer = alloca (sizeof (gunichar) * (n_compose + 1));
+
+  if (output_char)
+    *output_char = 0;
+
+  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] = gdk_keyval_to_unicode (compose_buffer[i]);
+      combination_buffer[n_compose] = 0;
+      i--;
+      while (i >= 0)
+        {
+          switch (compose_buffer[i])
+            {
+#define CASE(keysym, unicode) \
+            case GDK_KEY_dead_##keysym: combination_buffer[i+1] = unicode; break
+
+            CASE (grave, 0x0300);
+            CASE (acute, 0x0301);
+            CASE (circumflex, 0x0302);
+            CASE (tilde, 0x0303);       /* Also used with perispomeni, 0x342. */
+            CASE (macron, 0x0304);
+            CASE (breve, 0x0306);
+            CASE (abovedot, 0x0307);
+            CASE (diaeresis, 0x0308);
+            CASE (abovering, 0x30A);
+            CASE (hook, 0x0309);
+            CASE (doubleacute, 0x030B);
+            CASE (caron, 0x030C);
+            CASE (cedilla, 0x0327);
+            CASE (ogonek, 0x0328);      /* Legacy use for dasia, 0x314.*/
+            CASE (iota, 0x0345);
+            CASE (voiced_sound, 0x3099);        /* Per Markus Kuhn keysyms.txt file. */
+            CASE (semivoiced_sound, 0x309A);    /* Per Markus Kuhn keysyms.txt file. */
+            CASE (belowdot, 0x0323);
+            CASE (horn, 0x031B);        /* Legacy use for psili, 0x313 (or 0x343). */
+            CASE (stroke, 0x335);
+            CASE (abovecomma, 0x0313);  /* Equivalent to psili */
+            CASE (abovereversedcomma, 0x0314); /* Equivalent to dasia */
+            CASE (doublegrave, 0x30F);
+            CASE (belowring, 0x325);
+            CASE (belowmacron, 0x331);
+            CASE (belowcircumflex, 0x32D);
+            CASE (belowtilde, 0x330);
+            CASE (belowbreve, 0x32e);
+            CASE (belowdiaeresis, 0x324);
+            CASE (invertedbreve, 0x32f);
+            CASE (belowcomma, 0x326);
+            CASE (lowline, 0x332);
+            CASE (aboveverticalline, 0x30D);
+            CASE (belowverticalline, 0x329);
+            CASE (longsolidusoverlay, 0x338);
+            CASE (a, 0x363);
+            CASE (A, 0x363);
+            CASE (e, 0x364);
+            CASE (E, 0x364);
+            CASE (i, 0x365);
+            CASE (I, 0x365);
+            CASE (o, 0x366);
+            CASE (O, 0x366);
+            CASE (u, 0x367);
+            CASE (U, 0x367);
+            CASE (small_schwa, 0x1DEA);
+            CASE (capital_schwa, 0x1DEA);
+#undef CASE
+            default:
+              combination_buffer[i+1] = gdk_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;
+}
+
diff --git a/gtk/gtkcomposetable.h b/gtk/gtkcomposetable.h
index f2e8e8de3d..101aa94359 100644
--- a/gtk/gtkcomposetable.h
+++ b/gtk/gtkcomposetable.h
@@ -29,26 +29,47 @@ typedef struct _GtkComposeTableCompact GtkComposeTableCompact;
 struct _GtkComposeTable
 {
   guint16 *data;
-  gint max_seq_len;
-  gint n_seqs;
+  char *char_data;
+  int max_seq_len;
+  int n_seqs;
+  int n_chars;
   guint32 id;
 };
 
 struct _GtkComposeTableCompact
 {
   const guint16 *data;
-  gint max_seq_len;
-  gint n_index_size;
-  gint n_index_stride;
+  int max_seq_len;
+  int n_index_size;
+  int n_index_stride;
 };
 
-GtkComposeTable * gtk_compose_table_new_with_file (const gchar   *compose_file);
-GSList *gtk_compose_table_list_add_array          (GSList        *compose_tables,
-                                                   const guint16 *data,
-                                                   gint           max_seq_len,
-                                                   gint           n_seqs);
-GSList *gtk_compose_table_list_add_file           (GSList        *compose_tables,
-                                                   const gchar   *compose_file);
+GtkComposeTable * gtk_compose_table_new_with_file  (const char      *compose_file);
+GSList          * gtk_compose_table_list_add_array (GSList          *compose_tables,
+                                                    const guint16   *data,
+                                                    int              max_seq_len,
+                                                    int              n_seqs);
+GSList          * gtk_compose_table_list_add_file  (GSList          *compose_tables,
+                                                    const char      *compose_file);
+
+gboolean          gtk_compose_table_check          (const GtkComposeTable *table,
+                                                    const guint16         *compose_buffer,
+                                                    int                    n_compose,
+                                                    gboolean              *compose_finish,
+                                                    gboolean              *compose_match,
+                                                    GString               *output);
+
+gboolean          gtk_compose_table_compact_check  (const GtkComposeTableCompact  *table,
+                                                    const guint16                 *compose_buffer,
+                                                    int                            n_compose,
+                                                    gboolean                      *compose_finish,
+                                                    gboolean                      *compose_match,
+                                                    gunichar                      *output_char);
+
+gboolean          gtk_check_algorithmically        (const guint16                 *compose_buffer,
+                                                    int                            n_compose,
+                                                    gunichar                      *output);
+
 
 G_END_DECLS
 
diff --git a/gtk/gtkimcontextsimple.c b/gtk/gtkimcontextsimple.c
index 5dc724cf70..e73f39215e 100644
--- a/gtk/gtkimcontextsimple.c
+++ b/gtk/gtkimcontextsimple.c
@@ -19,16 +19,6 @@
 
 #include <gdk/gdk.h>
 
-#ifdef GDK_WINDOWING_X11
-#include <gdk/gdkx.h>
-#endif
-#ifdef GDK_WINDOWING_WAYLAND
-#include <wayland/gdkwayland.h>
-#endif
-#ifdef GDK_WINDOWING_WIN32
-#include <win32/gdkwin32.h>
-#endif
-
 #include <stdlib.h>
 #include <string.h>
 
@@ -40,8 +30,8 @@
 #include "gtkdebug.h"
 #include "gtkintl.h"
 #include "gtkcomposetable.h"
+#include "gtkimmoduleprivate.h"
 
-#include "gtkimcontextsimpleprivate.h"
 #include "gtkimcontextsimpleseqs.h"
 
 /**
@@ -59,6 +49,8 @@
  * Compose file). The syntax of these files is described in the Compose(5)
  * manual page.
  *
+ * ## Unicode characters
+ *
  * GtkIMContextSimple also supports numeric entry of Unicode characters
  * by typing Ctrl-Shift-u, followed by a hexadecimal Unicode codepoint.
  * For example, Ctrl-Shift-u 1 2 3 Enter yields U+0123 LATIN SMALL LETTER
@@ -67,9 +59,10 @@
 
 struct _GtkIMContextSimplePrivate
 {
-  guint16        compose_buffer[GTK_MAX_COMPOSE_LEN + 1];
-  gunichar       tentative_match;
-  gint           tentative_match_len;
+  guint16       *compose_buffer;
+  int            compose_buffer_len;
+  GString       *tentative_match;
+  int            tentative_match_len;
 
   guint          in_hex_sequence : 1;
   guint          modifiers_dropped : 1;
@@ -114,11 +107,13 @@ static gboolean gtk_im_context_simple_filter_keypress    (GtkIMContext
                                                          GdkEventKey              *key);
 static void     gtk_im_context_simple_reset              (GtkIMContext             *context);
 static void     gtk_im_context_simple_get_preedit_string (GtkIMContext             *context,
-                                                         gchar                   **str,
+                                                         char                    **str,
                                                          PangoAttrList           **attrs,
-                                                         gint                     *cursor_pos);
-static void     gtk_im_context_simple_set_client_window  (GtkIMContext             *context,
-                                                          GdkWindow                *window);
+                                                         int                      *cursor_pos);
+
+static void init_compose_table_async (GCancellable         *cancellable,
+                                      GAsyncReadyCallback   callback,
+                                      gpointer              user_data);
 
 G_DEFINE_TYPE_WITH_PRIVATE (GtkIMContextSimple, gtk_im_context_simple, GTK_TYPE_IM_CONTEXT)
 
@@ -131,16 +126,17 @@ gtk_im_context_simple_class_init (GtkIMContextSimpleClass *class)
   im_context_class->filter_keypress = gtk_im_context_simple_filter_keypress;
   im_context_class->reset = gtk_im_context_simple_reset;
   im_context_class->get_preedit_string = gtk_im_context_simple_get_preedit_string;
-  im_context_class->set_client_window = gtk_im_context_simple_set_client_window;
   gobject_class->finalize = gtk_im_context_simple_finalize;
+
+  init_compose_table_async (NULL, NULL, NULL);
 }
 
-static gchar*
+static char *
 get_x11_compose_file_dir (void)
 {
-  gchar* compose_file_dir;
+  char * compose_file_dir;
 
-#if defined (GDK_WINDOWING_X11)
+#if defined (X11_DATA_PREFIX)
   compose_file_dir = g_strdup (X11_DATA_PREFIX "/share/X11/locale");
 #else
   compose_file_dir = g_build_filename (_gtk_get_datadir (), "X11", "locale", NULL);
@@ -150,26 +146,28 @@ get_x11_compose_file_dir (void)
 }
 
 static void
-gtk_im_context_simple_init_compose_table (GtkIMContextSimple *im_context_simple)
+gtk_im_context_simple_init_compose_table (void)
 {
-  gchar *path = NULL;
-  const gchar *home;
-  const gchar *locale;
-  gchar **langs = NULL;
-  gchar **lang = NULL;
-  gchar * const sys_langs[] = { "el_gr", "fi_fi", "pt_br", NULL };
-  gchar * const *sys_lang = NULL;
-  gchar *x11_compose_file_dir = get_x11_compose_file_dir ();
-
-  path = g_build_filename (g_get_user_config_dir (), "gtk-3.0", "Compose", NULL);
+  char *path = NULL;
+  const char *home;
+  const char *locale;
+  char **langs = NULL;
+  char **lang = NULL;
+  const char * const sys_langs[] = { "el_gr", "fi_fi", "pt_br", NULL };
+  const char * const *sys_lang = NULL;
+  char *x11_compose_file_dir = get_x11_compose_file_dir ();
+
+  path = g_build_filename (g_get_user_config_dir (), "gtk-4.0", "Compose", NULL);
   if (g_file_test (path, G_FILE_TEST_EXISTS))
     {
-      gtk_im_context_simple_add_compose_file (im_context_simple, path);
+      G_LOCK (global_tables);
+      global_tables = gtk_compose_table_list_add_file (global_tables, path);
+      G_UNLOCK (global_tables);
+
       g_free (path);
       return;
     }
-  g_free (path);
-  path = NULL;
+  g_clear_pointer (&path, g_free);
 
   home = g_get_home_dir ();
   if (home == NULL)
@@ -178,12 +176,13 @@ gtk_im_context_simple_init_compose_table (GtkIMContextSimple *im_context_simple)
   path = g_build_filename (home, ".XCompose", NULL);
   if (g_file_test (path, G_FILE_TEST_EXISTS))
     {
-      gtk_im_context_simple_add_compose_file (im_context_simple, path);
+      G_LOCK (global_tables);
+      global_tables = gtk_compose_table_list_add_file (global_tables, path);
+      G_UNLOCK (global_tables);
       g_free (path);
       return;
     }
-  g_free (path);
-  path = NULL;
+  g_clear_pointer (&path, g_free);
 
   locale = g_getenv ("LC_CTYPE");
   if (locale == NULL)
@@ -216,17 +215,19 @@ gtk_im_context_simple_init_compose_table (GtkIMContextSimple *im_context_simple)
 
       if (g_file_test (path, G_FILE_TEST_EXISTS))
         break;
-      g_free (path);
-      path = NULL;
+      g_clear_pointer (&path, g_free);
     }
 
   g_free (x11_compose_file_dir);
   g_strfreev (langs);
 
   if (path != NULL)
-    gtk_im_context_simple_add_compose_file (im_context_simple, path);
-  g_free (path);
-  path = NULL;
+    {
+      G_LOCK (global_tables);
+      global_tables = gtk_compose_table_list_add_file (global_tables, path);
+      G_UNLOCK (global_tables);
+    }
+  g_clear_pointer (&path, g_free);
 }
 
 static void
@@ -235,36 +236,47 @@ init_compose_table_thread_cb (GTask            *task,
                               gpointer          task_data,
                               GCancellable     *cancellable)
 {
+  gint64 before G_GNUC_UNUSED;
+
   if (g_task_return_error_if_cancelled (task))
     return;
 
-  g_return_if_fail (GTK_IS_IM_CONTEXT_SIMPLE (task_data));
-
-  gtk_im_context_simple_init_compose_table (GTK_IM_CONTEXT_SIMPLE (task_data));
+  gtk_im_context_simple_init_compose_table ();
 }
 
-void
-init_compose_table_async (GtkIMContextSimple   *im_context_simple,
-                          GCancellable         *cancellable,
+static void
+init_compose_table_async (GCancellable         *cancellable,
                           GAsyncReadyCallback   callback,
                           gpointer              user_data)
 {
   GTask *task = g_task_new (NULL, cancellable, callback, user_data);
   g_task_set_source_tag (task, init_compose_table_async);
-  g_task_set_task_data (task, g_object_ref (im_context_simple), g_object_unref);
   g_task_run_in_thread (task, init_compose_table_thread_cb);
   g_object_unref (task);
 }
 
 static void
-gtk_im_context_simple_init (GtkIMContextSimple *im_context_simple)
+gtk_im_context_simple_init (GtkIMContextSimple *context_simple)
 {
-  im_context_simple->priv = gtk_im_context_simple_get_instance_private (im_context_simple); 
+  GtkIMContextSimplePrivate *priv;
+
+  priv = context_simple->priv = gtk_im_context_simple_get_instance_private (context_simple);
+
+  priv->compose_buffer_len = gtk_compose_table_compact.max_seq_len + 1;
+  priv->compose_buffer = g_new0 (guint16, priv->compose_buffer_len);
+  priv->tentative_match = g_string_new ("");
+  priv->tentative_match_len = 0;
 }
 
 static void
 gtk_im_context_simple_finalize (GObject *obj)
 {
+  GtkIMContextSimple *context_simple = GTK_IM_CONTEXT_SIMPLE (obj);
+  GtkIMContextSimplePrivate *priv = context_simple->priv;;
+
+  g_free (priv->compose_buffer);
+  g_string_free (priv->tentative_match, TRUE);
+
   G_OBJECT_CLASS (gtk_im_context_simple_parent_class)->finalize (obj);
 }
 
@@ -282,512 +294,29 @@ gtk_im_context_simple_new (void)
 }
 
 static void
-gtk_im_context_simple_commit_char (GtkIMContext *context,
-                                  gunichar ch)
-{
-  GtkIMContextSimple *context_simple = GTK_IM_CONTEXT_SIMPLE (context);
-  GtkIMContextSimplePrivate *priv = context_simple->priv;
-  gchar buf[10];
-  gint len;
-
-  g_return_if_fail (g_unichar_validate (ch));
-
-  len = g_unichar_to_utf8 (ch, buf);
-  buf[len] = '\0';
-
-  if (priv->tentative_match || priv->in_hex_sequence)
-    {
-      priv->in_hex_sequence = FALSE;
-      priv->tentative_match = 0;
-      priv->tentative_match_len = 0;
-      g_signal_emit_by_name (context_simple, "preedit-changed");
-      g_signal_emit_by_name (context_simple, "preedit-end");
-    }
-
-  g_signal_emit_by_name (context, "commit", &buf);
-}
-
-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 (GtkIMContextSimple    *context_simple,
-            const GtkComposeTable *table,
-            gint                   n_compose)
+gtk_im_context_simple_commit_string (GtkIMContextSimple *context_simple,
+                                     const char         *str)
 {
   GtkIMContextSimplePrivate *priv = context_simple->priv;
-  gint row_stride = table->max_seq_len + 2; 
-  guint16 *seq; 
-  
-  /* 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 = bsearch (priv->compose_buffer,
-                table->data, table->n_seqs,
-                sizeof (guint16) *  row_stride, 
-                compare_seq);
-
-  if (seq)
-    {
-      guint16 *prev_seq;
-
-      /* Back up to the first sequence that matches to make sure
-       * we find the exact match if there is one.
-       */
-      while (seq > table->data)
-       {
-         prev_seq = seq - row_stride;
-         if (compare_seq (priv->compose_buffer, prev_seq) != 0)
-           break;
-         seq = prev_seq;
-       }
-      
-      if (n_compose == table->max_seq_len ||
-         seq[n_compose] == 0) /* complete sequence */
-       {
-         guint16 *next_seq;
-         gunichar value = 
-           0x10000 * seq[table->max_seq_len] + seq[table->max_seq_len + 1];
-
-         /* We found a tentative match. See if there are any longer
-          * sequences containing this subsequence
-          */
-         next_seq = seq + row_stride;
-         if (next_seq < table->data + row_stride * table->n_seqs)
-           {
-             if (compare_seq (priv->compose_buffer, next_seq) == 0)
-               {
-                 priv->tentative_match = value;
-                 priv->tentative_match_len = n_compose;
-
-                 g_signal_emit_by_name (context_simple, "preedit-changed");
-
-                 return TRUE;
-               }
-           }
-
-         gtk_im_context_simple_commit_char (GTK_IM_CONTEXT (context_simple), value);
-         priv->compose_buffer[0] = 0;
-       }
-      
-      return TRUE;
-    }
-
-  return FALSE;
-}
-
-/* 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) >= GDK_KEY_dead_grave && (k) <= (GDK_KEY_dead_dasia+1))
-
-#ifdef GDK_WINDOWING_WIN32
-
-/* On Windows, user expectation is that typing a dead accent followed
- * by space will input the corresponding spacing character. The X
- * compose tables are different for dead acute and diaeresis, which
- * when followed by space produce a plain ASCII apostrophe and double
- * quote respectively. So special-case those.
- */
-
-static gboolean
-check_win32_special_cases (GtkIMContextSimple    *context_simple,
-                          gint                   n_compose)
-{
-  GtkIMContextSimplePrivate *priv = context_simple->priv;
-  if (n_compose == 2 &&
-      priv->compose_buffer[1] == GDK_KEY_space)
-    {
-      gunichar value = 0;
-
-      switch (priv->compose_buffer[0])
-       {
-       case GDK_KEY_dead_acute:
-         value = 0x00B4; break;
-       case GDK_KEY_dead_diaeresis:
-         value = 0x00A8; break;
-       }
-      if (value > 0)
-       {
-         gtk_im_context_simple_commit_char (GTK_IM_CONTEXT (context_simple), value);
-         priv->compose_buffer[0] = 0;
+  priv->in_hex_sequence = FALSE;
+  g_string_set_size (priv->tentative_match, 0);
+  priv->tentative_match_len = 0;
+  priv->compose_buffer[0] = 0;
 
-         return TRUE;
-       }
-    }
-  return FALSE;
+  g_signal_emit_by_name (context_simple, "preedit-changed");
+  g_signal_emit_by_name (context_simple, "preedit-end");
+  g_signal_emit_by_name (context_simple, "commit", str);
 }
 
 static void
-check_win32_special_case_after_compact_match (GtkIMContextSimple    *context_simple,
-                                             gint                   n_compose,
-                                             guint                  value)
-{
-  GtkIMContextSimplePrivate *priv = context_simple->priv;
-
-  /* On Windows user expectation is that typing two dead accents will input
-   * two corresponding spacing accents.
-   */
-  if (n_compose == 2 &&
-      priv->compose_buffer[0] == priv->compose_buffer[1] &&
-      IS_DEAD_KEY (priv->compose_buffer[0]))
-    {
-      gtk_im_context_simple_commit_char (GTK_IM_CONTEXT (context_simple), value);
-    }
-}
-
-#endif
-
-#ifdef GDK_WINDOWING_QUARTZ
-
-static gboolean
-check_quartz_special_cases (GtkIMContextSimple *context_simple,
-                            gint                n_compose)
-{
-  GtkIMContextSimplePrivate *priv = context_simple->priv;
-  guint value = 0;
-
-  if (n_compose == 2)
-    {
-      switch (priv->compose_buffer[0])
-        {
-        case GDK_KEY_dead_doubleacute:
-          switch (priv->compose_buffer[1])
-            {
-            case GDK_KEY_dead_doubleacute:
-            case GDK_KEY_space:
-              value = GDK_KEY_quotedbl; break;
-
-            case 'a': value = GDK_KEY_adiaeresis; break;
-            case 'A': value = GDK_KEY_Adiaeresis; break;
-            case 'e': value = GDK_KEY_ediaeresis; break;
-            case 'E': value = GDK_KEY_Ediaeresis; break;
-            case 'i': value = GDK_KEY_idiaeresis; break;
-            case 'I': value = GDK_KEY_Idiaeresis; break;
-            case 'o': value = GDK_KEY_odiaeresis; break;
-            case 'O': value = GDK_KEY_Odiaeresis; break;
-            case 'u': value = GDK_KEY_udiaeresis; break;
-            case 'U': value = GDK_KEY_Udiaeresis; break;
-            case 'y': value = GDK_KEY_ydiaeresis; break;
-            case 'Y': value = GDK_KEY_Ydiaeresis; break;
-            }
-          break;
-
-        case GDK_KEY_dead_acute:
-          switch (priv->compose_buffer[1])
-            {
-            case 'c': value = GDK_KEY_ccedilla; break;
-            case 'C': value = GDK_KEY_Ccedilla; break;
-            }
-          break;
-        }
-    }
-
-  if (value > 0)
-    {
-      gtk_im_context_simple_commit_char (GTK_IM_CONTEXT (context_simple),
-                                         gdk_keyval_to_unicode (value));
-      priv->compose_buffer[0] = 0;
-
-      return TRUE;
-    }
-
-  return FALSE;
-}
-
-#endif
-
-gboolean
-gtk_check_compact_table (const GtkComposeTableCompact  *table,
-                         guint16                       *compose_buffer,
-                         gint                           n_compose,
-                         gboolean                      *compose_finish,
-                         gboolean                      *compose_match,
-                         gunichar                      *output_char)
-{
-  gint row_stride;
-  guint16 *seq_index;
-  guint16 *seq;
-  gint i;
-  gboolean match;
-  gunichar value;
-
-  if (compose_finish)
-    *compose_finish = FALSE;
-  if (compose_match)
-    *compose_match = FALSE;
-  if (output_char)
-    *output_char = 0;
-
-  /* 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)
-    return FALSE;
-
-  if (seq_index && n_compose == 1)
-    return TRUE;
-
-  seq = NULL;
-  match = FALSE;
-  value = 0;
-
-  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)
-                {
-                  value = seq[row_stride - 1];
-                  match = TRUE;
-                }
-              else
-                {
-                  if (output_char)
-                    *output_char = value;
-                  if (match)
-                    {
-                      if (compose_match)
-                        *compose_match = TRUE;
-                    }
-
-                  return TRUE;
-                }
-            }
-        }
-    }
-
-  if (match)
-    {
-      if (compose_match)
-        *compose_match = TRUE;
-      if (compose_finish)
-        *compose_finish = TRUE;
-      if (output_char)
-        *output_char = value;
-
-      return TRUE;
-    }
-
-  return FALSE;
-}
-
-/* This function receives a sequence of Unicode characters and tries to
- * normalize it (NFC). We check for the case where 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)
+gtk_im_context_simple_commit_char (GtkIMContextSimple *context_simple,
+                                   gunichar            ch)
 {
-  gunichar combination_buffer_temp[GTK_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, GTK_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, GTK_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)
-        {
-          temp_swap = combination_buffer_temp[i % (n_compose - 1) + 1];
-          combination_buffer_temp[i % (n_compose - 1) + 1] = combination_buffer_temp[(i+1) % (n_compose - 1) 
+ 1];
-          combination_buffer_temp[(i+1) % (n_compose - 1) + 1] = temp_swap;
-        }
-      else
-        break;
-    }
-
-  return FALSE;
-}
-
-gboolean
-gtk_check_algorithmically (const guint16       *compose_buffer,
-                           gint                 n_compose,
-                           gunichar            *output_char)
-
-{
-  gint i;
-  gunichar combination_buffer[GTK_MAX_COMPOSE_LEN];
-  gchar *combination_utf8, *nfc;
-
-  if (output_char)
-    *output_char = 0;
-
-  if (n_compose >= GTK_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] = gdk_keyval_to_unicode (compose_buffer[i]);
-      combination_buffer[n_compose] = 0;
-      i--;
-      while (i >= 0)
-       {
-         switch (compose_buffer[i])
-           {
-#define CASE(keysym, unicode) \
-           case GDK_KEY_dead_##keysym: combination_buffer[i+1] = unicode; break
-
-           CASE (grave, 0x0300);
-           CASE (acute, 0x0301);
-           CASE (circumflex, 0x0302);
-           CASE (tilde, 0x0303);       /* Also used with perispomeni, 0x342. */
-           CASE (macron, 0x0304);
-           CASE (breve, 0x0306);
-           CASE (abovedot, 0x0307);
-           CASE (diaeresis, 0x0308);
-           CASE (hook, 0x0309);
-           CASE (abovering, 0x030A);
-           CASE (doubleacute, 0x030B);
-           CASE (caron, 0x030C);
-           CASE (abovecomma, 0x0313);         /* Equivalent to psili */
-           CASE (abovereversedcomma, 0x0314); /* Equivalent to dasia */
-           CASE (horn, 0x031B);        /* Legacy use for psili, 0x313 (or 0x343). */
-           CASE (belowdot, 0x0323);
-           CASE (cedilla, 0x0327);
-           CASE (ogonek, 0x0328);      /* Legacy use for dasia, 0x314.*/
-           CASE (iota, 0x0345);
-           CASE (voiced_sound, 0x3099);        /* Per Markus Kuhn keysyms.txt file. */
-           CASE (semivoiced_sound, 0x309A);    /* Per Markus Kuhn keysyms.txt file. */
-
-           /* The following cases are to be removed once xkeyboard-config,
-            * xorg are fully updated.
-            */
-            /* Workaround for typo in 1.4.x xserver-xorg */
-           case 0xfe66: combination_buffer[i+1] = 0x314; break;
-           /* CASE (dasia, 0x314); */
-           /* CASE (perispomeni, 0x342); */
-           /* CASE (psili, 0x343); */
-#undef CASE
-           default:
-             combination_buffer[i+1] = gdk_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);
+  char buf[8] = { 0, };
 
-          return TRUE;
-        }
-    }
+  g_unichar_to_utf8 (ch, buf);
 
-  return FALSE;
+  gtk_im_context_simple_commit_string (context_simple, buf);
 }
 
 /* In addition to the table-driven sequences, we allow Unicode hex
@@ -808,17 +337,17 @@ gtk_check_algorithmically (const guint16       *compose_buffer,
 
 static gboolean
 check_hex (GtkIMContextSimple *context_simple,
-           gint                n_compose)
+           int                 n_compose)
 {
   GtkIMContextSimplePrivate *priv = context_simple->priv;
   /* See if this is a hex sequence, return TRUE if so */
-  gint i;
+  int i;
   GString *str;
   gulong n;
-  gchar *nptr = NULL;
-  gchar buf[7];
+  char *nptr = NULL;
+  char buf[7];
 
-  priv->tentative_match = 0;
+  g_string_set_size (priv->tentative_match, 0);
   priv->tentative_match_len = 0;
 
   str = g_string_new (NULL);
@@ -858,7 +387,8 @@ check_hex (GtkIMContextSimple *context_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;
     }
   
@@ -881,30 +411,37 @@ beep_window (GdkWindow *window)
 
 static gboolean
 no_sequence_matches (GtkIMContextSimple *context_simple,
-                     gint                n_compose,
+                     int                 n_compose,
                      GdkEventKey        *event)
 {
   GtkIMContextSimplePrivate *priv = context_simple->priv;
   GtkIMContext *context;
   gunichar ch;
-  
+
   context = GTK_IM_CONTEXT (context_simple);
   
   /* No compose sequences found, check first if we have a partial
    * match pending.
    */
-  if (priv->tentative_match)
+  if (priv->tentative_match_len > 0)
     {
-      gint len = priv->tentative_match_len;
+      int len = priv->tentative_match_len;
       int i;
-      
-      gtk_im_context_simple_commit_char (context, priv->tentative_match);
-      priv->compose_buffer[0] = 0;
-      
-      for (i=0; i < n_compose - len - 1; i++)
-       {
-         GdkEvent *tmp_event = gdk_event_copy ((GdkEvent *)event);
-         tmp_event->key.keyval = priv->compose_buffer[len + i];
+      guint16 *compose_buffer;
+      char *str;
+
+      compose_buffer = alloca (sizeof (guint16) * priv->compose_buffer_len);
+
+      memcpy (compose_buffer, priv->compose_buffer, sizeof (guint16) * priv->compose_buffer_len);
+
+      str = g_strdup (priv->tentative_match->str);
+      gtk_im_context_simple_commit_string (context_simple, str);
+      g_free (str);
+
+      for (i = 0; i < n_compose - len - 1; i++)
+        {
+          GdkEvent *tmp_event = gdk_event_copy ((GdkEvent *)event);
+          tmp_event->key.keyval = compose_buffer[len + i];
          
          gtk_im_context_filter_keypress (context, (GdkEventKey *)tmp_event);
          gdk_event_free (tmp_event);
@@ -918,18 +455,21 @@ no_sequence_matches (GtkIMContextSimple *context_simple,
       if (n_compose > 1)               /* Invalid sequence */
        {
          beep_window (event->window);
+          g_signal_emit_by_name (context, "preedit-changed");
+          g_signal_emit_by_name (context, "preedit-end");
          return TRUE;
        }
   
       ch = gdk_keyval_to_unicode (event->keyval);
       if (ch != 0 && !g_unichar_iscntrl (ch))
        {
-         gtk_im_context_simple_commit_char (context, ch);
+         gtk_im_context_simple_commit_char (context_simple, ch);
          return TRUE;
        }
       else
        return FALSE;
     }
+  return FALSE;
 }
 
 static gboolean
@@ -946,9 +486,9 @@ canonical_hex_keyval (GdkEventKey *event)
   GdkKeymap *keymap = gdk_keymap_get_for_display (gdk_window_get_display (event->window));
   guint keyval;
   guint *keyvals = NULL;
-  gint n_vals = 0;
-  gint i;
-  
+  int n_vals = 0;
+  int i;
+
   /* See if the keyval is already a hex digit */
   if (is_hex_keyval (event->keyval))
     return event->keyval;
@@ -957,9 +497,9 @@ canonical_hex_keyval (GdkEventKey *event)
    * any other state, and return that hex keyval if so
    */
   gdk_keymap_get_entries_for_keycode (keymap,
-                                     event->hardware_keycode,
-                                     NULL,
-                                     &keyvals, &n_vals);
+                                      event->hardware_keycode,
+                                      NULL,
+                                      &keyvals, &n_vals);
 
   keyval = 0;
   i = 0;
@@ -991,7 +531,7 @@ gtk_im_context_simple_filter_keypress (GtkIMContext *context,
   GtkIMContextSimple *context_simple = GTK_IM_CONTEXT_SIMPLE (context);
   GtkIMContextSimplePrivate *priv = context_simple->priv;
   GdkDisplay *display = gdk_window_get_display (event->window);
-  GSList *tmp_list;  
+  GSList *tmp_list;
   int n_compose = 0;
   GdkModifierType hex_mod_mask;
   gboolean have_hex_mods;
@@ -1005,43 +545,46 @@ gtk_im_context_simple_filter_keypress (GtkIMContext *context,
   gboolean compose_match;
   gunichar output_char;
 
-  while (priv->compose_buffer[n_compose] != 0)
+  while (priv->compose_buffer[n_compose] != 0 && n_compose < priv->compose_buffer_len)
     n_compose++;
 
   if (event->type == GDK_KEY_RELEASE)
     {
       if (priv->in_hex_sequence &&
-         (event->keyval == GDK_KEY_Control_L || event->keyval == GDK_KEY_Control_R ||
+          (event->keyval == GDK_KEY_Control_L || event->keyval == GDK_KEY_Control_R ||
           event->keyval == GDK_KEY_Shift_L || event->keyval == GDK_KEY_Shift_R))
        {
-         if (priv->tentative_match &&
-             g_unichar_validate (priv->tentative_match))
+          if (priv->tentative_match->len > 0)
            {
-             gtk_im_context_simple_commit_char (context, priv->tentative_match);
-             priv->compose_buffer[0] = 0;
+              char *str = g_strdup (priv->tentative_match->str);
+             gtk_im_context_simple_commit_string (context_simple, str);
+              g_free (str);
 
+             return TRUE;
            }
          else if (n_compose == 0)
            {
              priv->modifiers_dropped = TRUE;
+
+             return TRUE;
            }
-         else
+         else if (priv->in_hex_sequence)
            {
              /* 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;
-             
+
              g_signal_emit_by_name (context_simple, "preedit-changed");
              g_signal_emit_by_name (context_simple, "preedit-end");
-           }
 
-         return TRUE;
+             return TRUE;
+           }
        }
-      else
-       return FALSE;
+
+      return FALSE;
     }
 
   /* Ignore modifier key presses */
@@ -1049,9 +592,7 @@ gtk_im_context_simple_filter_keypress (GtkIMContext *context,
     if (event->keyval == gtk_compose_ignore[i])
       return FALSE;
 
-  hex_mod_mask = gdk_keymap_get_modifier_mask (gdk_keymap_get_for_display (display),
-                                               GDK_MODIFIER_INTENT_PRIMARY_ACCELERATOR);
-  hex_mod_mask |= GDK_SHIFT_MASK;
+  hex_mod_mask = GDK_CONTROL_MASK|GDK_SHIFT_MASK;
 
   if (priv->in_hex_sequence && priv->modifiers_dropped)
     have_hex_mods = TRUE;
@@ -1059,10 +600,10 @@ gtk_im_context_simple_filter_keypress (GtkIMContext *context,
     have_hex_mods = (event->state & (hex_mod_mask)) == hex_mod_mask;
   is_hex_start = event->keyval == GDK_KEY_U;
   is_hex_end = (event->keyval == GDK_KEY_space ||
-               event->keyval == GDK_KEY_KP_Space ||
-               event->keyval == GDK_KEY_Return ||
-               event->keyval == GDK_KEY_ISO_Enter ||
-               event->keyval == GDK_KEY_KP_Enter);
+                event->keyval == GDK_KEY_KP_Space ||
+                event->keyval == GDK_KEY_Return ||
+                event->keyval == GDK_KEY_ISO_Enter ||
+                event->keyval == GDK_KEY_KP_Enter);
   is_backspace = event->keyval == GDK_KEY_BackSpace;
   is_escape = event->keyval == GDK_KEY_Escape;
   hex_keyval = canonical_hex_keyval (event);
@@ -1118,22 +659,35 @@ gtk_im_context_simple_filter_keypress (GtkIMContext *context,
       return TRUE;
     }
 
+  if (!priv->in_hex_sequence && n_compose > 0 && is_backspace)
+    {
+      n_compose--;
+      priv->compose_buffer[n_compose] = 0;
+
+      g_signal_emit_by_name (context_simple, "preedit-changed");
+
+      if (n_compose == 0)
+        g_signal_emit_by_name (context_simple, "preedit-end");
+
+      return TRUE;
+    }
+
   /* 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))
+      if (priv->tentative_match->len > 0)
        {
-         gtk_im_context_simple_commit_char (context, priv->tentative_match);
-         priv->compose_buffer[0] = 0;
+          char *str = g_strdup (priv->tentative_match->str);
+         gtk_im_context_simple_commit_string (context_simple, str);
+          g_free (str);
        }
-      else 
+      else
        {
          /* invalid hex sequence */
          if (n_compose > 0)
            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;
        }
@@ -1145,7 +699,7 @@ gtk_im_context_simple_filter_keypress (GtkIMContext *context,
       priv->compose_buffer[0] = 0;
       priv->in_hex_sequence = TRUE;
       priv->modifiers_dropped = FALSE;
-      priv->tentative_match = 0;
+      g_string_set_size (priv->tentative_match, 0);
 
       g_signal_emit_by_name (context_simple, "preedit-start");
       g_signal_emit_by_name (context_simple, "preedit-changed");
@@ -1156,24 +710,30 @@ gtk_im_context_simple_filter_keypress (GtkIMContext *context,
   /* Then, check for compose sequences */
   if (priv->in_hex_sequence)
     {
-      if (hex_keyval)
+      if (hex_keyval && n_compose < 6)
        priv->compose_buffer[n_compose++] = hex_keyval;
       else if (is_escape)
        {
          gtk_im_context_simple_reset (context);
-         
          return TRUE;
        }
       else if (!is_hex_end)
        {
-         /* non-hex character in hex sequence */
+         /* non-hex character in hex sequence, or sequence too long */
          beep_window (event->window);
-         
          return TRUE;
        }
     }
   else
-    priv->compose_buffer[n_compose++] = event->keyval;
+    {
+      if (n_compose + 1 == priv->compose_buffer_len)
+        {
+          priv->compose_buffer_len += 1;
+          priv->compose_buffer = g_renew (guint16, priv->compose_buffer, priv->compose_buffer_len);
+        }
+
+      priv->compose_buffer[n_compose++] = event->keyval;
+    }
 
   priv->compose_buffer[n_compose] = 0;
 
@@ -1185,25 +745,25 @@ gtk_im_context_simple_filter_keypress (GtkIMContext *context,
           /* 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))
+             if (priv->tentative_match->len > 0)
                {
-                 gtk_im_context_simple_commit_char (context, priv->tentative_match);
-                 priv->compose_buffer[0] = 0;
+                  char *str = g_strdup (priv->tentative_match->str);
+                 gtk_im_context_simple_commit_string (context_simple, str);
+                  g_free (str);
                }
              else
                {
                  /* 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;
                }
             }
           else if (!check_hex (context_simple, n_compose))
            beep_window (event->window);
-         
+
          g_signal_emit_by_name (context_simple, "preedit-changed");
 
          if (!priv->in_hex_sequence)
@@ -1215,109 +775,81 @@ gtk_im_context_simple_filter_keypress (GtkIMContext *context,
   else
     {
       gboolean success = FALSE;
+      GString *output;
 
-#ifdef GDK_WINDOWING_WIN32
-      if (GDK_IS_WIN32_DISPLAY (display))
-        {
-          guint16  output[2];
-          gsize    output_size = 2;
-
-          switch (gdk_win32_keymap_check_compose (GDK_WIN32_KEYMAP (gdk_keymap_get_default ()),
-                                                  priv->compose_buffer,
-                                                  n_compose,
-                                                  output, &output_size))
-            {
-            case GDK_WIN32_KEYMAP_MATCH_NONE:
-              break;
-            case GDK_WIN32_KEYMAP_MATCH_EXACT:
-            case GDK_WIN32_KEYMAP_MATCH_PARTIAL:
-              for (i = 0; i < output_size; i++)
-                {
-                  output_char = gdk_keyval_to_unicode (output[i]);
-                  gtk_im_context_simple_commit_char (GTK_IM_CONTEXT (context_simple),
-                                                     output_char);
-                }
-              priv->compose_buffer[0] = 0;
-              return TRUE;
-            case GDK_WIN32_KEYMAP_MATCH_INCOMPLETE:
-              return TRUE;
-            }
-        }
-#endif
+      output = g_string_new ("");
 
       G_LOCK (global_tables);
 
       tmp_list = global_tables;
       while (tmp_list)
         {
-          if (check_table (context_simple, tmp_list->data, n_compose))
+          if (gtk_compose_table_check ((GtkComposeTable *)tmp_list->data,
+                                       priv->compose_buffer, n_compose,
+                                       &compose_finish, &compose_match,
+                                       output))
             {
+              if (compose_finish)
+                {
+                  if (compose_match)
+                    gtk_im_context_simple_commit_string (context_simple, output->str);
+                }
+              else
+                {
+                  if (compose_match)
+                    {
+                      g_string_assign (priv->tentative_match, output->str);
+                      priv->tentative_match_len = n_compose;
+                    }
+                  g_signal_emit_by_name (context_simple, "preedit-changed");
+                }
+
               success = TRUE;
               break;
             }
+
           tmp_list = tmp_list->next;
         }
 
       G_UNLOCK (global_tables);
 
-      if (success)
-        return TRUE;
-
-#ifdef GDK_WINDOWING_WIN32
-      if (check_win32_special_cases (context_simple, n_compose))
-       return TRUE;
-#endif
+      g_string_free (output, TRUE);
 
-#ifdef GDK_WINDOWING_QUARTZ
-      if (check_quartz_special_cases (context_simple, n_compose))
+      if (success)
         return TRUE;
-#endif
 
-      if (gtk_check_compact_table (&gtk_compose_table_compact,
-                                   priv->compose_buffer,
-                                   n_compose, &compose_finish,
-                                   &compose_match, &output_char))
+      if (gtk_compose_table_compact_check (&gtk_compose_table_compact,
+                                           priv->compose_buffer, n_compose,
+                                           &compose_finish, &compose_match,
+                                           &output_char))
         {
           if (compose_finish)
             {
               if (compose_match)
-                {
-                  gtk_im_context_simple_commit_char (GTK_IM_CONTEXT (context_simple),
-                                                     output_char);
-#ifdef G_OS_WIN32
-                  check_win32_special_case_after_compact_match (context_simple,
-                                                                n_compose,
-                                                                output_char);
-#endif
-                  priv->compose_buffer[0] = 0;
-                }
+                gtk_im_context_simple_commit_char (context_simple, output_char);
             }
           else
             {
               if (compose_match)
                 {
-                  priv->tentative_match = output_char;
+                  g_string_set_size (priv->tentative_match, 0);
+                  g_string_append_unichar (priv->tentative_match, output_char);
                   priv->tentative_match_len = n_compose;
                 }
-              if (output_char)
-                g_signal_emit_by_name (context_simple, "preedit-changed");
+              g_signal_emit_by_name (context_simple, "preedit-changed");
             }
 
           return TRUE;
         }
-  
+
       if (gtk_check_algorithmically (priv->compose_buffer, n_compose, &output_char))
         {
           if (output_char)
-            {
-              gtk_im_context_simple_commit_char (GTK_IM_CONTEXT (context_simple),
-                                                 output_char);
-              priv->compose_buffer[0] = 0;
-            }
-         return TRUE;
+            gtk_im_context_simple_commit_char (context_simple, output_char);
+          return TRUE;
         }
     }
-  
+
   /* The current compose_buffer doesn't match anything */
   return no_sequence_matches (context_simple, n_compose, event);
 }
@@ -1330,90 +862,69 @@ gtk_im_context_simple_reset (GtkIMContext *context)
 
   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;
       g_signal_emit_by_name (context_simple, "preedit-changed");
       g_signal_emit_by_name (context_simple, "preedit-end");
     }
 }
 
-static void     
+static void
 gtk_im_context_simple_get_preedit_string (GtkIMContext   *context,
-                                         gchar         **str,
-                                         PangoAttrList **attrs,
-                                         gint           *cursor_pos)
+                                          char          **str,
+                                          PangoAttrList **attrs,
+                                          int            *cursor_pos)
 {
   GtkIMContextSimple *context_simple = GTK_IM_CONTEXT_SIMPLE (context);
   GtkIMContextSimplePrivate *priv = context_simple->priv;
-  char outbuf[37]; /* up to 6 hex digits */
-  int len = 0;
+  GString *s;
+  int i;
+
+  s = g_string_new ("");
 
   if (priv->in_hex_sequence)
     {
-      int hexchars = 0;
-         
-      outbuf[0] = 'u';
-      len = 1;
+      g_string_append_c (s, 'u');
 
-      while (priv->compose_buffer[hexchars] != 0)
-       {
-         len += g_unichar_to_utf8 (gdk_keyval_to_unicode (priv->compose_buffer[hexchars]),
-                                   outbuf + len);
-         ++hexchars;
-       }
-
-      g_assert (len < 25);
+      for (i = 0; priv->compose_buffer[i]; i++)
+        g_string_append_unichar (s, gdk_keyval_to_unicode (priv->compose_buffer[i]));
     }
-  else if (priv->tentative_match)
-    len = g_unichar_to_utf8 (priv->tentative_match, outbuf);
-      
-  outbuf[len] = '\0';      
-
-  if (str)
-    *str = g_strdup (outbuf);
-
-  if (attrs)
+  else if (priv->tentative_match->len > 0 && priv->compose_buffer[0] != 0)
     {
-      *attrs = pango_attr_list_new ();
-      
-      if (len)
-       {
-         PangoAttribute *attr = pango_attr_underline_new (PANGO_UNDERLINE_SINGLE);
-         attr->start_index = 0;
-          attr->end_index = len;
-         pango_attr_list_insert (*attrs, attr);
-       }
+       g_string_append (s, priv->tentative_match->str);
+    }
+  else
+    {
+      for (i = 0; priv->compose_buffer[i]; i++)
+        {
+          if (priv->compose_buffer[i] == GDK_KEY_Multi_key)
+            g_string_append_unichar (s, 0x2384); /* U+2384 COMPOSITION SYMBOL */
+          else
+            g_string_append_unichar (s, gdk_keyval_to_unicode (priv->compose_buffer[i]));
+        }
     }
 
   if (cursor_pos)
-    *cursor_pos = len;
-}
+    *cursor_pos = s->len;
 
-static void
-gtk_im_context_simple_set_client_window  (GtkIMContext *context,
-                                          GdkWindow    *window)
-{
-  GtkIMContextSimple *im_context_simple = GTK_IM_CONTEXT_SIMPLE (context);
-  gboolean run_compose_table = FALSE;
-
-  if (!window)
-    return;
+  if (attrs)
+    {
+      *attrs = pango_attr_list_new ();
 
-  /* Load compose table for X11 or Wayland. */
-#ifdef GDK_WINDOWING_X11
-  if (GDK_IS_X11_DISPLAY (gdk_window_get_display (window)))
-    run_compose_table = TRUE;
-#endif
-#ifdef GDK_WINDOWING_WAYLAND
-  if (GDK_IS_WAYLAND_DISPLAY (gdk_window_get_display (window)))
-    run_compose_table = TRUE;
-#endif
+      if (s->len)
+        {
+          PangoAttribute *attr = pango_attr_underline_new (PANGO_UNDERLINE_SINGLE);
+          attr->start_index = 0;
+          attr->end_index = s->len;
+          pango_attr_list_insert (*attrs, attr);
+        }
+    }
 
-  if (run_compose_table)
-    init_compose_table_async (im_context_simple, NULL, NULL, NULL);
+  if (str)
+    *str = g_string_free (s, FALSE);
 }
 
 /**
@@ -1421,9 +932,8 @@ gtk_im_context_simple_set_client_window  (GtkIMContext *context,
  * @context_simple: A #GtkIMContextSimple
  * @data: (array): the table
  * @max_seq_len: Maximum length of a sequence in the table
- *               (cannot be greater than #GTK_MAX_COMPOSE_LEN)
  * @n_seqs: number of sequences in the table
- * 
+ *
  * Adds an additional table to search to the input context.
  * Each row of the table consists of @max_seq_len key symbols
  * followed by two #guint16 interpreted as the high and low
@@ -1437,8 +947,8 @@ gtk_im_context_simple_set_client_window  (GtkIMContext *context,
 void
 gtk_im_context_simple_add_table (GtkIMContextSimple *context_simple,
                                 guint16            *data,
-                                gint                max_seq_len,
-                                gint                n_seqs)
+                                int                 max_seq_len,
+                                int                 n_seqs)
 {
   g_return_if_fail (GTK_IS_IM_CONTEXT_SIMPLE (context_simple));
 
@@ -1450,25 +960,22 @@ gtk_im_context_simple_add_table (GtkIMContextSimple *context_simple,
   G_UNLOCK (global_tables);
 }
 
-/*
+/**
  * gtk_im_context_simple_add_compose_file:
  * @context_simple: A #GtkIMContextSimple
  * @compose_file: The path of compose file
  *
  * Adds an additional table from the X11 compose file.
- *
- * Since: 3.20
  */
 void
 gtk_im_context_simple_add_compose_file (GtkIMContextSimple *context_simple,
-                                        const gchar        *compose_file)
+                                        const char         *compose_file)
 {
   g_return_if_fail (GTK_IS_IM_CONTEXT_SIMPLE (context_simple));
 
   G_LOCK (global_tables);
 
-  global_tables = gtk_compose_table_list_add_file (global_tables,
-                                                   compose_file);
+  global_tables = gtk_compose_table_list_add_file (global_tables, compose_file);
 
   G_UNLOCK (global_tables);
 }


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]