diff options
author | Matthias Clasen <mclasen@redhat.com> | 2021-02-02 17:29:14 +0000 |
---|---|---|
committer | Matthias Clasen <mclasen@redhat.com> | 2021-02-02 17:29:14 +0000 |
commit | e9b06b63467a594f917e94da657ceb984eb10e4c (patch) | |
tree | 81fe2c729427702455578d8c0e13fd4033ca97e5 | |
parent | 86b437a1b6f160c24e57e60403b996bb3e29bd04 (diff) | |
parent | 949c7831871b396ef71db7b1360e3027ad79ccf7 (diff) | |
download | gtk+-e9b06b63467a594f917e94da657ceb984eb10e4c.tar.gz |
Merge branch 'im-context-work' into 'master'
Some im context work
Closes #1004, #186, and #3521
See merge request GNOME/gtk!3143
-rw-r--r-- | docs/reference/gtk/gtk4-sections.txt | 1 | ||||
-rw-r--r-- | gtk/gtkcomposetable.c | 748 | ||||
-rw-r--r-- | gtk/gtkcomposetable.h | 35 | ||||
-rw-r--r-- | gtk/gtkimcontextsimple.c | 578 | ||||
-rw-r--r-- | gtk/gtkimcontextsimple.h | 7 | ||||
-rw-r--r-- | gtk/gtkimcontextsimpleprivate.h | 42 | ||||
-rw-r--r-- | testsuite/gtk/compose/basic | 1 | ||||
-rw-r--r-- | testsuite/gtk/compose/basic.expected | 3 | ||||
-rw-r--r-- | testsuite/gtk/compose/codepoint | 1 | ||||
-rw-r--r-- | testsuite/gtk/compose/codepoint.expected | 3 | ||||
-rw-r--r-- | testsuite/gtk/compose/hex | 1 | ||||
-rw-r--r-- | testsuite/gtk/compose/hex.expected | 3 | ||||
-rw-r--r-- | testsuite/gtk/compose/long | 1 | ||||
-rw-r--r-- | testsuite/gtk/compose/long.expected | 3 | ||||
-rw-r--r-- | testsuite/gtk/compose/match | 3 | ||||
-rw-r--r-- | testsuite/gtk/compose/multi | 3 | ||||
-rw-r--r-- | testsuite/gtk/compose/multi.expected | 5 | ||||
-rw-r--r-- | testsuite/gtk/compose/octal | 1 | ||||
-rw-r--r-- | testsuite/gtk/compose/octal.expected | 3 | ||||
-rw-r--r-- | testsuite/gtk/compose/strings | 4 | ||||
-rw-r--r-- | testsuite/gtk/compose/strings.expected | 6 | ||||
-rw-r--r-- | testsuite/gtk/composetable.c | 320 | ||||
-rw-r--r-- | testsuite/gtk/meson.build | 7 |
23 files changed, 1086 insertions, 693 deletions
diff --git a/docs/reference/gtk/gtk4-sections.txt b/docs/reference/gtk/gtk4-sections.txt index d51b9d0e9f..302d1ad0be 100644 --- a/docs/reference/gtk/gtk4-sections.txt +++ b/docs/reference/gtk/gtk4-sections.txt @@ -1831,7 +1831,6 @@ GtkIMContextSimple gtk_im_context_simple_new gtk_im_context_simple_add_table gtk_im_context_simple_add_compose_file -GTK_MAX_COMPOSE_LEN <SUBSECTION Standard> GTK_IM_CONTEXT_SIMPLE GTK_IS_IM_CONTEXT_SIMPLE diff --git a/gtk/gtkcomposetable.c b/gtk/gtkcomposetable.c index 901a7ecbf9..a07f5da06f 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]; - char *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); } @@ -76,58 +77,82 @@ parse_compose_value (GtkComposeData *compose_data, const char *val, const char *line) { - char **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; } @@ -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 length of compose 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; } @@ -214,7 +239,10 @@ parse_compose_line (GList **compose_list, 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,6 +272,8 @@ fail: gtk_compose_data_free (compose_data); } +extern const GtkComposeTableCompact gtk_compose_table_compact; + static GList * gtk_compose_list_parse_file (const char *compose_file) { @@ -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 (>k_compose_table_compact, - keysyms, - n_compose, - &compose_finish, - NULL, - &output_char) && + if (gtk_compose_table_compact_check (>k_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,17 +433,6 @@ 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; } @@ -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 char *comment; - const char *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) @@ -546,6 +512,7 @@ gtk_compose_table_serialize (GtkComposeTable *compose_table, 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,44 +520,34 @@ 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; } @@ -614,16 +571,17 @@ gtk_compose_table_load_cache (const char *compose_file) 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 char *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 char *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 char *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 char *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 char *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 char *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; @@ -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,6 +767,8 @@ 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; } @@ -816,9 +800,6 @@ gtk_compose_table_new_with_file (const char *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, @@ -841,9 +822,10 @@ gtk_compose_table_list_add_array (GSList *compose_tables, 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 = MIN (max_seq_len, GTK_MAX_COMPOSE_LEN) + 2; + 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"); @@ -864,12 +846,14 @@ 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, +gtk_compose_table_list_add_file (GSList *compose_tables, const char *compose_file) { guint32 hash; @@ -891,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 (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; +} + +/* 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 885ec6306d..101aa94359 100644 --- a/gtk/gtkcomposetable.h +++ b/gtk/gtkcomposetable.h @@ -29,8 +29,10 @@ typedef struct _GtkComposeTableCompact GtkComposeTableCompact; struct _GtkComposeTable { guint16 *data; + char *char_data; int max_seq_len; int n_seqs; + int n_chars; guint32 id; }; @@ -42,13 +44,32 @@ struct _GtkComposeTableCompact int n_index_stride; }; -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); +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 76afa8c1ee..701e433c19 100644 --- a/gtk/gtkimcontextsimple.c +++ b/gtk/gtkimcontextsimple.c @@ -32,7 +32,6 @@ #include "gtkcomposetable.h" #include "gtkimmoduleprivate.h" -#include "gtkimcontextsimpleprivate.h" #include "gtkimcontextsimpleseqs.h" #include "gdk/gdkprofilerprivate.h" @@ -61,8 +60,9 @@ struct _GtkIMContextSimplePrivate { - guint16 compose_buffer[GTK_MAX_COMPOSE_LEN + 1]; - gunichar tentative_match; + guint16 *compose_buffer; + int compose_buffer_len; + GString *tentative_match; int tentative_match_len; guint in_hex_sequence : 1; @@ -174,8 +174,7 @@ gtk_im_context_simple_init_compose_table (void) g_free (path); return; } - g_free (path); - path = NULL; + g_clear_pointer (&path, g_free); home = g_get_home_dir (); if (home == NULL) @@ -190,8 +189,7 @@ gtk_im_context_simple_init_compose_table (void) g_free (path); return; } - g_free (path); - path = NULL; + g_clear_pointer (&path, g_free); locale = g_getenv ("LC_CTYPE"); if (locale == NULL) @@ -224,8 +222,7 @@ gtk_im_context_simple_init_compose_table (void) 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); @@ -237,8 +234,7 @@ gtk_im_context_simple_init_compose_table (void) global_tables = gtk_compose_table_list_add_file (global_tables, path); G_UNLOCK (global_tables); } - g_free (path); - path = NULL; + g_clear_pointer (&path, g_free); } static void @@ -271,14 +267,27 @@ init_compose_table_async (GCancellable *cancellable, } 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); } @@ -296,413 +305,29 @@ gtk_im_context_simple_new (void) } static void -gtk_im_context_simple_commit_char (GtkIMContext *context, - gunichar ch) +gtk_im_context_simple_commit_string (GtkIMContextSimple *context_simple, + const char *str) { - GtkIMContextSimple *context_simple = GTK_IM_CONTEXT_SIMPLE (context); GtkIMContextSimplePrivate *priv = context_simple->priv; - char buf[10]; - int len; - - g_return_if_fail (g_unichar_validate (ch)); - - len = g_unichar_to_utf8 (ch, buf); - buf[len] = '\0'; - priv->in_hex_sequence = FALSE; - priv->tentative_match = 0; + g_string_set_size (priv->tentative_match, 0); priv->tentative_match_len = 0; priv->compose_buffer[0] = 0; - g_signal_emit_by_name (context, "preedit-changed"); - g_signal_emit_by_name (context, "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, - int n_compose) -{ - GtkIMContextSimplePrivate *priv = context_simple->priv; - int 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); - - return TRUE; - } - - g_signal_emit_by_name (context_simple, "preedit-changed"); - - 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); } -/* 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) - -gboolean -gtk_check_compact_table (const GtkComposeTableCompact *table, - 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 (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, int n_compose) +static void +gtk_im_context_simple_commit_char (GtkIMContextSimple *context_simple, + gunichar ch) { - gunichar combination_buffer_temp[GTK_MAX_COMPOSE_LEN]; - char *combination_utf8_temp = NULL; - char *nfc_temp = NULL; - int n_combinations; - gunichar temp_swap; - int 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; - } + char buf[8] = { 0, }; - g_free (combination_utf8_temp); - g_free (nfc_temp); + g_unichar_to_utf8 (ch, buf); - 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[GTK_MAX_COMPOSE_LEN]; - char *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 (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; + gtk_im_context_simple_commit_string (context_simple, buf); } /* In addition to the table-driven sequences, we allow Unicode hex @@ -733,7 +358,7 @@ check_hex (GtkIMContextSimple *context_simple, 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); @@ -773,7 +398,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; } @@ -809,18 +435,23 @@ no_sequence_matches (GtkIMContextSimple *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) { int len = priv->tentative_match_len; int i; - guint16 compose_buffer[GTK_MAX_COMPOSE_LEN + 1]; + guint16 *compose_buffer; + char *str; - memcpy (compose_buffer, priv->compose_buffer, sizeof (compose_buffer)); + 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); - gtk_im_context_simple_commit_char (context, priv->tentative_match); - for (i = 0; i < n_compose - len - 1; i++) - { + { GdkTranslatedKey translated; translated.keyval = compose_buffer[len + i]; translated.consumed = 0; @@ -858,7 +489,7 @@ no_sequence_matches (GtkIMContextSimple *context_simple, ch = gdk_keyval_to_unicode (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 @@ -942,7 +573,7 @@ gtk_im_context_simple_filter_keypress (GtkIMContext *context, gunichar output_char; guint keyval, state; - while (priv->compose_buffer[n_compose] != 0) + while (priv->compose_buffer[n_compose] != 0 && n_compose < priv->compose_buffer_len) n_compose++; keyval = gdk_key_event_get_keyval (event); @@ -954,10 +585,11 @@ gtk_im_context_simple_filter_keypress (GtkIMContext *context, (keyval == GDK_KEY_Control_L || keyval == GDK_KEY_Control_R || keyval == GDK_KEY_Shift_L || 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); + char *str = g_strdup (priv->tentative_match->str); + gtk_im_context_simple_commit_string (context_simple, str); + g_free (str); return TRUE; } @@ -972,7 +604,7 @@ gtk_im_context_simple_filter_keypress (GtkIMContext *context, /* invalid hex sequence */ beep_surface (surface); - priv->tentative_match = 0; + g_string_set_size (priv->tentative_match, 0); priv->in_hex_sequence = FALSE; priv->compose_buffer[0] = 0; @@ -1072,10 +704,11 @@ gtk_im_context_simple_filter_keypress (GtkIMContext *context, /* 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); + char *str = g_strdup (priv->tentative_match->str); + gtk_im_context_simple_commit_string (context_simple, str); + g_free (str); } else { @@ -1083,7 +716,7 @@ gtk_im_context_simple_filter_keypress (GtkIMContext *context, if (n_compose > 0) beep_surface (surface); - priv->tentative_match = 0; + g_string_set_size (priv->tentative_match, 0); priv->in_hex_sequence = FALSE; priv->compose_buffer[0] = 0; } @@ -1095,7 +728,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"); @@ -1106,7 +739,7 @@ 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) { @@ -1115,13 +748,21 @@ gtk_im_context_simple_filter_keypress (GtkIMContext *context, } else if (!is_hex_end) { - /* non-hex character in hex sequence */ + /* non-hex character in hex sequence, or sequence too long */ beep_surface (surface); return TRUE; } } else - priv->compose_buffer[n_compose++] = 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++] = keyval; + } priv->compose_buffer[n_compose] = 0; @@ -1133,17 +774,18 @@ 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); + 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_surface (surface); - priv->tentative_match = 0; + g_string_set_size (priv->tentative_match, 0); priv->in_hex_sequence = FALSE; priv->compose_buffer[0] = 0; } @@ -1162,43 +804,65 @@ gtk_im_context_simple_filter_keypress (GtkIMContext *context, else { gboolean success = FALSE; + GString *output; + + 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); + g_string_free (output, TRUE); + if (success) return TRUE; - if (gtk_check_compact_table (>k_compose_table_compact, - priv->compose_buffer, - n_compose, &compose_finish, - &compose_match, &output_char)) + if (gtk_compose_table_compact_check (>k_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); - } + 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; } g_signal_emit_by_name (context_simple, "preedit-changed"); @@ -1206,18 +870,15 @@ gtk_im_context_simple_filter_keypress (GtkIMContext *context, 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); - } - 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, (GdkEvent *)event); } @@ -1230,10 +891,10 @@ 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"); @@ -1260,9 +921,9 @@ gtk_im_context_simple_get_preedit_string (GtkIMContext *context, 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 && priv->compose_buffer[0] != 0) + else if (priv->tentative_match->len > 0 && priv->compose_buffer[0] != 0) { - g_string_append_unichar (s, priv->tentative_match); + g_string_append (s, priv->tentative_match->str); } else { @@ -1300,7 +961,6 @@ gtk_im_context_simple_get_preedit_string (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. @@ -1320,7 +980,6 @@ gtk_im_context_simple_add_table (GtkIMContextSimple *context_simple, int n_seqs) { g_return_if_fail (GTK_IS_IM_CONTEXT_SIMPLE (context_simple)); - g_return_if_fail (max_seq_len <= GTK_MAX_COMPOSE_LEN); G_LOCK (global_tables); @@ -1345,8 +1004,7 @@ gtk_im_context_simple_add_compose_file (GtkIMContextSimple *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); } diff --git a/gtk/gtkimcontextsimple.h b/gtk/gtkimcontextsimple.h index e25cd4d216..7bd4454b1c 100644 --- a/gtk/gtkimcontextsimple.h +++ b/gtk/gtkimcontextsimple.h @@ -27,10 +27,9 @@ G_BEGIN_DECLS -/** - * GTK_MAX_COMPOSE_LEN: - * - * The maximum length of sequences in compose tables. +/* + * No longer used by GTK, just left here on the off chance that some + * 3rd party code used this define. */ #define GTK_MAX_COMPOSE_LEN 7 diff --git a/gtk/gtkimcontextsimpleprivate.h b/gtk/gtkimcontextsimpleprivate.h deleted file mode 100644 index 5b79ed2f44..0000000000 --- a/gtk/gtkimcontextsimpleprivate.h +++ /dev/null @@ -1,42 +0,0 @@ -/* GTK - The GIMP Toolkit - * Copyright (C) 2000 Red Hat Software - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see <http://www.gnu.org/licenses/>. - */ - -#ifndef __GTK_IM_CONTEXT_SIMPLE_PRIVATE_H__ -#define __GTK_IM_CONTEXT_SIMPLE_PRIVATE_H__ - -#include <glib.h> - -#include "gdk/gdkkeysyms.h" - -G_BEGIN_DECLS - -extern const GtkComposeTableCompact gtk_compose_table_compact; - -gboolean gtk_check_algorithmically (const guint16 *compose_buffer, - int n_compose, - gunichar *output); -gboolean gtk_check_compact_table (const GtkComposeTableCompact *table, - guint16 *compose_buffer, - int n_compose, - gboolean *compose_finish, - gboolean *compose_match, - gunichar *output_char); - -G_END_DECLS - - -#endif /* __GTK_IM_CONTEXT_SIMPLE_PRIVATE_H__ */ diff --git a/testsuite/gtk/compose/basic b/testsuite/gtk/compose/basic new file mode 100644 index 0000000000..536fb013fc --- /dev/null +++ b/testsuite/gtk/compose/basic @@ -0,0 +1 @@ +<Multi_key> <s> <e> <q> : "!" diff --git a/testsuite/gtk/compose/basic.expected b/testsuite/gtk/compose/basic.expected new file mode 100644 index 0000000000..ab7a0d2b94 --- /dev/null +++ b/testsuite/gtk/compose/basic.expected @@ -0,0 +1,3 @@ +# n_seqs: 1 +# max_seq_len: 4 +<Uff20> <U73> <U65> <U71> : "!" # U21 diff --git a/testsuite/gtk/compose/codepoint b/testsuite/gtk/compose/codepoint new file mode 100644 index 0000000000..22f44be8f9 --- /dev/null +++ b/testsuite/gtk/compose/codepoint @@ -0,0 +1 @@ +<Multi_key> <U73> <U6F> <U7a> : "!" diff --git a/testsuite/gtk/compose/codepoint.expected b/testsuite/gtk/compose/codepoint.expected new file mode 100644 index 0000000000..d2c09f6c3f --- /dev/null +++ b/testsuite/gtk/compose/codepoint.expected @@ -0,0 +1,3 @@ +# n_seqs: 1 +# max_seq_len: 4 +<Uff20> <U73> <U6f> <U7a> : "!" # U21 diff --git a/testsuite/gtk/compose/hex b/testsuite/gtk/compose/hex new file mode 100644 index 0000000000..267972168f --- /dev/null +++ b/testsuite/gtk/compose/hex @@ -0,0 +1 @@ +<Multi_key> <s> <e> <q> : "\x23fe\X23F3" diff --git a/testsuite/gtk/compose/hex.expected b/testsuite/gtk/compose/hex.expected new file mode 100644 index 0000000000..603344216c --- /dev/null +++ b/testsuite/gtk/compose/hex.expected @@ -0,0 +1,3 @@ +# n_seqs: 1 +# max_seq_len: 4 +<Uff20> <U73> <U65> <U71> : "⏾⏳" diff --git a/testsuite/gtk/compose/long b/testsuite/gtk/compose/long new file mode 100644 index 0000000000..84ef7acee8 --- /dev/null +++ b/testsuite/gtk/compose/long @@ -0,0 +1 @@ +<Multi_key> <e> <m> <m> <e> <n> <t> <a> <l> <e> <r> : "🧀" diff --git a/testsuite/gtk/compose/long.expected b/testsuite/gtk/compose/long.expected new file mode 100644 index 0000000000..17de9b5575 --- /dev/null +++ b/testsuite/gtk/compose/long.expected @@ -0,0 +1,3 @@ +# n_seqs: 1 +# max_seq_len: 11 +<Uff20> <U65> <U6d> <U6d> <U65> <U6e> <U74> <U61> <U6c> <U65> <U72> : "🧀" # U1f9c0 diff --git a/testsuite/gtk/compose/match b/testsuite/gtk/compose/match new file mode 100644 index 0000000000..0554ac02c2 --- /dev/null +++ b/testsuite/gtk/compose/match @@ -0,0 +1,3 @@ +<Multi_key> <s> <e> <q> : "!" +<Multi_key> <s> <e> <q> <u> : "?" +<Multi_key> <z> <w> <i> <n> <e> <s> : "🥂" diff --git a/testsuite/gtk/compose/multi b/testsuite/gtk/compose/multi new file mode 100644 index 0000000000..ee8416ea1b --- /dev/null +++ b/testsuite/gtk/compose/multi @@ -0,0 +1,3 @@ +<Multi_key> <s> <e> <q> : "!" +<Multi_key> <u> <b> <2> <3> : "/" +<Multi_key> <s> <a> <s> : "_" diff --git a/testsuite/gtk/compose/multi.expected b/testsuite/gtk/compose/multi.expected new file mode 100644 index 0000000000..21b97a0fe5 --- /dev/null +++ b/testsuite/gtk/compose/multi.expected @@ -0,0 +1,5 @@ +# n_seqs: 3 +# max_seq_len: 5 +<Uff20> <U73> <U61> <U73> <U0> : "_" # U5f +<Uff20> <U73> <U65> <U71> <U0> : "!" # U21 +<Uff20> <U75> <U62> <U32> <U33> : "/" # U2f diff --git a/testsuite/gtk/compose/octal b/testsuite/gtk/compose/octal new file mode 100644 index 0000000000..350ecf8951 --- /dev/null +++ b/testsuite/gtk/compose/octal @@ -0,0 +1 @@ +<Multi_key> <s> <e> <q> : "\041" diff --git a/testsuite/gtk/compose/octal.expected b/testsuite/gtk/compose/octal.expected new file mode 100644 index 0000000000..ab7a0d2b94 --- /dev/null +++ b/testsuite/gtk/compose/octal.expected @@ -0,0 +1,3 @@ +# n_seqs: 1 +# max_seq_len: 4 +<Uff20> <U73> <U65> <U71> : "!" # U21 diff --git a/testsuite/gtk/compose/strings b/testsuite/gtk/compose/strings new file mode 100644 index 0000000000..36d76785ac --- /dev/null +++ b/testsuite/gtk/compose/strings @@ -0,0 +1,4 @@ +<Multi_key> <s> <e> <q> : "!a" +<Multi_key> <s> <e> <q> <u> : "?" +<Multi_key> <u> <b> <2> <3> : "\121\122" +<Multi_key> <s> <a> <s> : "\"\\" diff --git a/testsuite/gtk/compose/strings.expected b/testsuite/gtk/compose/strings.expected new file mode 100644 index 0000000000..e1dfdc6dc1 --- /dev/null +++ b/testsuite/gtk/compose/strings.expected @@ -0,0 +1,6 @@ +# n_seqs: 4 +# max_seq_len: 5 +<Uff20> <U73> <U61> <U73> <U0> : "\"\\" +<Uff20> <U73> <U65> <U71> <U0> : "!a" +<Uff20> <U73> <U65> <U71> <U75> : "?" # U3f +<Uff20> <U75> <U62> <U32> <U33> : "QR" diff --git a/testsuite/gtk/composetable.c b/testsuite/gtk/composetable.c new file mode 100644 index 0000000000..b41faf08f2 --- /dev/null +++ b/testsuite/gtk/composetable.c @@ -0,0 +1,320 @@ +#include <gtk/gtk.h> +#include <locale.h> + +#include "../gtk/gtkcomposetable.h" +#include "../gtk/gtkimcontextsimpleseqs.h" +#include "testsuite/testutils.h" + +static void +append_escaped (GString *str, + const char *s) +{ + for (const char *p = s; *p; p = g_utf8_next_char (p)) + { + gunichar ch = g_utf8_get_char (p); + if (ch == '"') + g_string_append (str, "\\\""); + else if (ch == '\\') + g_string_append (str, "\\\\"); + else if (g_unichar_isprint (ch)) + g_string_append_unichar (str, ch); + else + { + guint n[8] = { 0, }; + int i = 0; + while (ch != 0) + { + n[i++] = ch & 7; + ch = ch >> 3; + } + for (; i >= 0; i--) + g_string_append_printf (str, "\\%o", n[i]); + } + } +} + +static char * +gtk_compose_table_print (GtkComposeTable *table) +{ + int i, j; + guint16 *seq; + GString *str; + + str = g_string_new (""); + + g_string_append_printf (str, "# n_seqs: %d\n# max_seq_len: %d\n", + table->n_seqs, + table->max_seq_len); + + for (i = 0, seq = table->data; i < table->n_seqs; i++, seq += table->max_seq_len + 2) + { + gunichar value; + char buf[8] = { 0 }; + + for (j = 0; j < table->max_seq_len; j++) + g_string_append_printf (str, "<U%x> ", seq[j]); + + value = (seq[table->max_seq_len] << 16) | seq[table->max_seq_len + 1]; + if ((value & (1 << 31)) != 0) + { + const char *out = &table->char_data[value & ~(1 << 31)]; + + g_string_append (str, ": \""); + append_escaped (str, out); + g_string_append (str, "\"\n"); + } + else + { + g_unichar_to_utf8 (value, buf); + g_string_append_printf (str, ": \"%s\" # U%x\n", buf, value); + } + + } + + return g_string_free (str, FALSE); +} + +static void +generate_output (const char *file) +{ + GSList *tables = NULL; + GtkComposeTable *table; + char *output; + + tables = gtk_compose_table_list_add_file (tables, file); + table = tables->data; + output = gtk_compose_table_print (table); + + g_print ("%s", output); +} + +static void +compose_table_compare (gconstpointer data) +{ + const char *basename = data; + GSList *tables = NULL; + GtkComposeTable *table; + char *file; + char *expected; + char *output; + char *diff; + GError *error = NULL; + + file = g_build_filename (g_test_get_dir (G_TEST_DIST), "compose", basename, NULL); + expected = g_strconcat (file, ".expected", NULL); + + tables = gtk_compose_table_list_add_file (tables, file); + + g_assert_true (g_slist_length (tables) == 1); + + table = tables->data; + + output = gtk_compose_table_print (table); + diff = diff_with_file (expected, output, -1, &error); + g_assert_no_error (error); + + if (diff && diff[0]) + { + g_print ("Resulting output doesn't match reference:\n%s", diff); + g_test_fail (); + } + + g_free (output); + g_free (file); + g_free (expected); +} + +/* Check matching against a small table */ +static void +compose_table_match (void) +{ + GSList *tables = NULL; + GtkComposeTable *table; + char *file; + guint16 buffer[8] = { 0, }; + gboolean finish, match, ret; + GString *output; + + output = g_string_new (""); + + file = g_build_filename (g_test_get_dir (G_TEST_DIST), "compose", "match", NULL); + + tables = gtk_compose_table_list_add_file (tables, file); + + g_assert_true (g_slist_length (tables) == 1); + + table = tables->data; + + buffer[0] = GDK_KEY_Multi_key; + buffer[1] = 0; + ret = gtk_compose_table_check (table, buffer, 1, &finish, &match, output); + g_assert_true (ret); + g_assert_false (finish); + g_assert_false (match); + g_assert_true (output->len == 0); + + buffer[0] = GDK_KEY_a; + buffer[1] = 0; + ret = gtk_compose_table_check (table, buffer, 1, &finish, &match, output); + g_assert_false (ret); + g_assert_false (finish); + g_assert_false (match); + g_assert_true (output->len == 0); + + buffer[0] = GDK_KEY_Multi_key; + buffer[1] = GDK_KEY_s; + buffer[2] = GDK_KEY_e; + ret = gtk_compose_table_check (table, buffer, 3, &finish, &match, output); + g_assert_true (ret); + g_assert_false (finish); + g_assert_false (match); + g_assert_true (output->len == 0); + + buffer[0] = GDK_KEY_Multi_key; + buffer[1] = GDK_KEY_s; + buffer[2] = GDK_KEY_e; + buffer[3] = GDK_KEY_q; + ret = gtk_compose_table_check (table, buffer, 4, &finish, &match, output); + g_assert_true (ret); + g_assert_false (finish); + g_assert_true (match); + g_assert_cmpstr (output->str, ==, "!"); + + g_string_set_size (output, 0); + + buffer[0] = GDK_KEY_Multi_key; + buffer[1] = GDK_KEY_s; + buffer[2] = GDK_KEY_e; + buffer[3] = GDK_KEY_q; + buffer[4] = GDK_KEY_u; + ret = gtk_compose_table_check (table, buffer, 5, &finish, &match, output); + g_assert_true (ret); + g_assert_true (finish); + g_assert_true (match); + g_assert_cmpstr (output->str, ==, "?"); + + g_string_free (output, TRUE); + g_free (file); +} + +/* just check some random sequences */ +static void +compose_table_match_compact (void) +{ + const GtkComposeTableCompact table = { + gtk_compose_seqs_compact, + 5, + 30, + 6 + }; + guint16 buffer[8] = { 0, }; + gboolean finish, match, ret; + gunichar ch; + + buffer[0] = GDK_KEY_Multi_key; + + ret = gtk_compose_table_compact_check (&table, buffer, 1, &finish, &match, &ch); + g_assert_true (ret); + g_assert_false (finish); + g_assert_false (match); + g_assert_true (ch == 0); + + buffer[0] = GDK_KEY_a; + buffer[1] = GDK_KEY_b; + buffer[2] = GDK_KEY_c; + + ret = gtk_compose_table_compact_check (&table, buffer, 3, &finish, &match, &ch); + g_assert_false (ret); + g_assert_false (finish); + g_assert_false (match); + g_assert_true (ch == 0); + + buffer[0] = GDK_KEY_Multi_key; + buffer[1] = GDK_KEY_parenleft; + buffer[2] = GDK_KEY_j; + buffer[3] = GDK_KEY_parenright; + + ret = gtk_compose_table_compact_check (&table, buffer, 4, &finish, &match, &ch); + g_assert_true (ret); + g_assert_true (finish); + g_assert_true (match); + g_assert_true (ch == 0x24d9); /* CIRCLED LATIN SMALL LETTER J */ +} + +static void +match_algorithmic (void) +{ + guint16 buffer[8] = { 0, }; + gboolean ret; + gunichar ch; + + buffer[0] = GDK_KEY_a; + buffer[1] = GDK_KEY_b; + + ret = gtk_check_algorithmically (buffer, 2, &ch); + g_assert_false (ret); + g_assert_true (ch == 0); + + buffer[0] = GDK_KEY_dead_abovering; + buffer[1] = GDK_KEY_A; + + ret = gtk_check_algorithmically (buffer, 2, &ch); + g_assert_true (ret); + g_assert_true (ch == 0xc5); + + buffer[0] = GDK_KEY_A; + buffer[1] = GDK_KEY_dead_abovering; + + ret = gtk_check_algorithmically (buffer, 2, &ch); + g_assert_false (ret); + g_assert_true (ch == 0); + + buffer[0] = GDK_KEY_dead_dasia; + buffer[1] = GDK_KEY_dead_perispomeni; + buffer[2] = GDK_KEY_Greek_alpha; + + ret = gtk_check_algorithmically (buffer, 3, &ch); + g_assert_true (ret); + g_assert_true (ch == 0x1f07); + + buffer[0] = GDK_KEY_dead_perispomeni; + buffer[1] = GDK_KEY_dead_dasia; + buffer[2] = GDK_KEY_Greek_alpha; + + ret = gtk_check_algorithmically (buffer, 3, &ch); + g_assert_true (ret); + g_assert_true (ch == 0x1f07); +} + +int +main (int argc, char *argv[]) +{ + char *dir; + + dir = g_dir_make_tmp ("composetableXXXXXX", NULL); + g_setenv ("XDG_CACHE_HOME", dir, TRUE); + g_free (dir); + + if (argc == 3 && strcmp (argv[1], "--generate") == 0) + { + setlocale (LC_ALL, ""); + + generate_output (argv[2]); + return 0; + } + + gtk_test_init (&argc, &argv, NULL); + + g_test_add_data_func ("/compose-table/basic", "basic", compose_table_compare); + g_test_add_data_func ("/compose-table/long", "long", compose_table_compare); + g_test_add_data_func ("/compose-table/octal", "octal", compose_table_compare); + g_test_add_data_func ("/compose-table/hex", "hex", compose_table_compare); + g_test_add_data_func ("/compose-table/codepoint", "codepoint", compose_table_compare); + g_test_add_data_func ("/compose-table/multi", "multi", compose_table_compare); + g_test_add_data_func ("/compose-table/strings", "strings", compose_table_compare); + g_test_add_func ("/compose-table/match", compose_table_match); + g_test_add_func ("/compose-table/match-compact", compose_table_match_compact); + g_test_add_func ("/compose-table/match-algorithmic", match_algorithmic); + + return g_test_run (); +} diff --git a/testsuite/gtk/meson.build b/testsuite/gtk/meson.build index 1ba0bfd9d8..b0af6dc642 100644 --- a/testsuite/gtk/meson.build +++ b/testsuite/gtk/meson.build @@ -103,6 +103,13 @@ tests = [ # Tests that test private apis and therefore are linked against libgtk-4.a internal_tests = [ { 'name': 'bitmask' }, + { + 'name': 'composetable', + 'sources': [ + 'composetable.c', + '../testutils.c' + ], + }, { 'name': 'constraint-solver' }, { 'name': 'rbtree-crash' }, { 'name': 'propertylookuplistmodel' }, |