From 89511eecf1d9d48ad705592a024b739dbc9fcf43 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 31 Jan 2021 20:55:29 -0500 Subject: [PATCH 1/5] imcontext: Update our check for dead keys A bunch of keysyms for dead keys have been added since this code was last touched. Update the check to cover the full range from dead_grave to dead_greek. --- gtk/gtkimcontextsimple.c | 109 ++++++++++++++++++++++----------------- 1 file changed, 62 insertions(+), 47 deletions(-) diff --git a/gtk/gtkimcontextsimple.c b/gtk/gtkimcontextsimple.c index a090ea3e08..c43329a80c 100644 --- a/gtk/gtkimcontextsimple.c +++ b/gtk/gtkimcontextsimple.c @@ -424,15 +424,13 @@ check_table (GtkIMContextSimple *context_simple, 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). +/* 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_dasia+1)) + ((k) >= GDK_KEY_dead_grave && (k) <= GDK_KEY_dead_greek) gboolean gtk_check_compact_table (const GtkComposeTableCompact *table, @@ -624,49 +622,66 @@ gtk_check_algorithmically (const guint16 *compose_buffer, combination_buffer[n_compose] = 0; i--; while (i >= 0) - { - switch (compose_buffer[i]) - { + { + switch (compose_buffer[i]) + { #define CASE(keysym, unicode) \ - case GDK_KEY_dead_##keysym: combination_buffer[i+1] = unicode; break + 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); */ + 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--; - } - + 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. */ From 8883243aaa27dd4bffe64b1d02b987a0561f6298 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Mon, 1 Feb 2021 00:37:43 -0500 Subject: [PATCH 2/5] imcontext: Show preedit for compose sequences MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Show the sequences as they are entered, using ⎄ for the compose key, to match what IBus does nowadays. Also handle backspace to allow corrections. --- gtk/gtkimcontextsimple.c | 126 ++++++++++++++++++++++----------------- 1 file changed, 71 insertions(+), 55 deletions(-) diff --git a/gtk/gtkimcontextsimple.c b/gtk/gtkimcontextsimple.c index c43329a80c..21db7d04b3 100644 --- a/gtk/gtkimcontextsimple.c +++ b/gtk/gtkimcontextsimple.c @@ -297,7 +297,7 @@ gtk_im_context_simple_new (void) static void gtk_im_context_simple_commit_char (GtkIMContext *context, - gunichar ch) + gunichar ch) { GtkIMContextSimple *context_simple = GTK_IM_CONTEXT_SIMPLE (context); GtkIMContextSimplePrivate *priv = context_simple->priv; @@ -309,14 +309,13 @@ gtk_im_context_simple_commit_char (GtkIMContext *context, 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"); - } + priv->in_hex_sequence = FALSE; + 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); } @@ -415,9 +414,12 @@ check_table (GtkIMContextSimple *context_simple, } gtk_im_context_simple_commit_char (GTK_IM_CONTEXT (context_simple), value); - priv->compose_buffer[0] = 0; + + return TRUE; } + g_signal_emit_by_name (context_simple, "preedit-changed"); + return TRUE; } @@ -811,14 +813,16 @@ no_sequence_matches (GtkIMContextSimple *context_simple, { int len = priv->tentative_match_len; int i; - + guint16 compose_buffer[GTK_MAX_COMPOSE_LEN + 1]; + + memcpy (compose_buffer, priv->compose_buffer, sizeof (compose_buffer)); + gtk_im_context_simple_commit_char (context, priv->tentative_match); - priv->compose_buffer[0] = 0; for (i = 0; i < n_compose - len - 1; i++) { GdkTranslatedKey translated; - translated.keyval = priv->compose_buffer[len + i]; + translated.keyval = compose_buffer[len + i]; translated.consumed = 0; translated.layout = 0; translated.level = 0; @@ -826,7 +830,7 @@ no_sequence_matches (GtkIMContextSimple *context_simple, gdk_event_get_surface (event), gdk_event_get_device (event), gdk_event_get_time (event), - priv->compose_buffer[len + i], + compose_buffer[len + i], gdk_event_get_modifier_state (event), FALSE, &translated, @@ -846,6 +850,8 @@ no_sequence_matches (GtkIMContextSimple *context_simple, if (n_compose > 1) /* Invalid sequence */ { beep_surface (gdk_event_get_surface (event)); + g_signal_emit_by_name (context, "preedit-changed"); + g_signal_emit_by_name (context, "preedit-end"); return TRUE; } @@ -952,7 +958,6 @@ gtk_im_context_simple_filter_keypress (GtkIMContext *context, g_unichar_validate (priv->tentative_match)) { gtk_im_context_simple_commit_char (context, priv->tentative_match); - priv->compose_buffer[0] = 0; return TRUE; } @@ -1051,6 +1056,19 @@ 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) { @@ -1058,7 +1076,6 @@ gtk_im_context_simple_filter_keypress (GtkIMContext *context, g_unichar_validate (priv->tentative_match)) { gtk_im_context_simple_commit_char (context, priv->tentative_match); - priv->compose_buffer[0] = 0; } else { @@ -1120,7 +1137,6 @@ gtk_im_context_simple_filter_keypress (GtkIMContext *context, g_unichar_validate (priv->tentative_match)) { gtk_im_context_simple_commit_char (context, priv->tentative_match); - priv->compose_buffer[0] = 0; } else { @@ -1176,7 +1192,6 @@ gtk_im_context_simple_filter_keypress (GtkIMContext *context, { gtk_im_context_simple_commit_char (GTK_IM_CONTEXT (context_simple), output_char); - priv->compose_buffer[0] = 0; } } else @@ -1186,8 +1201,7 @@ gtk_im_context_simple_filter_keypress (GtkIMContext *context, 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; @@ -1199,7 +1213,6 @@ gtk_im_context_simple_filter_keypress (GtkIMContext *context, { gtk_im_context_simple_commit_char (GTK_IM_CONTEXT (context_simple), output_char); - priv->compose_buffer[0] = 0; } return TRUE; } @@ -1227,56 +1240,59 @@ gtk_im_context_simple_reset (GtkIMContext *context) } } -static void +static void gtk_im_context_simple_get_preedit_string (GtkIMContext *context, - char **str, - PangoAttrList **attrs, - int *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 && priv->compose_buffer[0] != 0) + { + g_string_append_unichar (s, priv->tentative_match); + } + 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])); + } } - else if (priv->tentative_match) - len = g_unichar_to_utf8 (priv->tentative_match, outbuf); - outbuf[len] = '\0'; - - if (str) - *str = g_strdup (outbuf); + if (cursor_pos) + *cursor_pos = s->len; if (attrs) { *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); - } + + 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 (cursor_pos) - *cursor_pos = len; + if (str) + *str = g_string_free (s, FALSE); } /** From e39b5c99f1592b792206fb3b5da857b0ed41855a Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Mon, 1 Feb 2021 00:43:44 -0500 Subject: [PATCH 3/5] imcontext: Add a precondition check --- gtk/gtkimcontextsimple.c | 1 + 1 file changed, 1 insertion(+) diff --git a/gtk/gtkimcontextsimple.c b/gtk/gtkimcontextsimple.c index 21db7d04b3..76afa8c1ee 100644 --- a/gtk/gtkimcontextsimple.c +++ b/gtk/gtkimcontextsimple.c @@ -1320,6 +1320,7 @@ 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); From 162814f96999a41466ee167494e6b422cdd5be16 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Mon, 1 Feb 2021 00:44:08 -0500 Subject: [PATCH 4/5] imcontext: Improve an error message This error message was misleading, as pointed out by Ralf Jung. --- gtk/gtkcomposetable.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gtk/gtkcomposetable.c b/gtk/gtkcomposetable.c index b2914807de..710367b9f1 100644 --- a/gtk/gtkcomposetable.c +++ b/gtk/gtkcomposetable.c @@ -191,7 +191,7 @@ parse_compose_sequence (GtkComposeData *compose_data, g_strfreev (words); if (0 == n || n >= GTK_MAX_COMPOSE_LEN) { - g_warning ("The max number of sequences is %d: %s", + g_warning ("The max length of compose sequences is %d: %s", GTK_MAX_COMPOSE_LEN, line); return FALSE; } From 094a346539c606ae4a9e4bfcb4250b7741a4e665 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Mon, 1 Feb 2021 00:55:25 -0500 Subject: [PATCH 5/5] imcontext: Allow sequences of length GTK_MAX_COMPOSE_LEN There was an off-by-one error, making us reject sequences of this length. But the rest of the code handles them just fine. Fixes: #2319 --- gtk/gtkcomposetable.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gtk/gtkcomposetable.c b/gtk/gtkcomposetable.c index 710367b9f1..901a7ecbf9 100644 --- a/gtk/gtkcomposetable.c +++ b/gtk/gtkcomposetable.c @@ -189,7 +189,7 @@ parse_compose_sequence (GtkComposeData *compose_data, } g_strfreev (words); - if (0 == n || n >= GTK_MAX_COMPOSE_LEN) + if (0 == n || n > GTK_MAX_COMPOSE_LEN) { g_warning ("The max length of compose sequences is %d: %s", GTK_MAX_COMPOSE_LEN, line);