From e6f05da6ef93816ea747888cbf3269717a21ede3 Mon Sep 17 00:00:00 2001 From: Philip Zander Date: Tue, 9 Jul 2019 17:44:05 +0200 Subject: [PATCH 1/4] Use native Windows API for converting keystrokes to characters Instead of using the incomplete GTK-internal emulation, use the WM_CHAR messages sent by Windows. Make the IME input method the default for all languages on Windows. --- gdk/gdkevents.c | 12 +++- gdk/gdkinternals.h | 9 +++ gdk/win32/gdkevents-win32.c | 29 ++++++++ modules/input/gtkimcontextime.c | 122 ++++---------------------------- modules/input/imime.c | 2 +- 5 files changed, 61 insertions(+), 113 deletions(-) diff --git a/gdk/gdkevents.c b/gdk/gdkevents.c index b4c4c01986..95af90d698 100644 --- a/gdk/gdkevents.c +++ b/gdk/gdkevents.c @@ -585,7 +585,7 @@ gdk_event_new (GdkEventType type) return new_event; } -static gboolean +gboolean gdk_event_is_allocated (const GdkEvent *event) { if (event_hash) @@ -663,6 +663,11 @@ gdk_event_copy (const GdkEvent *event) new_private->source_device = private->source_device ? g_object_ref (private->source_device) : NULL; new_private->seat = private->seat; new_private->tool = private->tool; + +#ifdef GDK_WINDOWING_WIN32 + new_private->translation_len = private->translation_len; + new_private->translation = g_memdup(private->translation, private->translation_len*sizeof(private->translation[0])); +#endif } switch (event->any.type) @@ -767,6 +772,9 @@ gdk_event_free (GdkEvent *event) private = (GdkEventPrivate *) event; g_clear_object (&private->device); g_clear_object (&private->source_device); +#ifdef GDK_WINDOWING_WIN32 + g_free(private->translation); +#endif } switch (event->any.type) @@ -2564,4 +2572,4 @@ gdk_event_get_scancode (GdkEvent *event) private = (GdkEventPrivate *) event; return private->key_scancode; -} +} \ No newline at end of file diff --git a/gdk/gdkinternals.h b/gdk/gdkinternals.h index 50ebdaa235..e2c1d10c9c 100644 --- a/gdk/gdkinternals.h +++ b/gdk/gdkinternals.h @@ -192,6 +192,11 @@ struct _GdkEventPrivate GdkSeat *seat; GdkDeviceTool *tool; guint16 key_scancode; + +#ifdef GDK_WINDOWING_WIN32 + gunichar2 *translation; + guint translation_len; +#endif }; typedef struct _GdkWindowPaint GdkWindowPaint; @@ -413,6 +418,10 @@ void gdk_event_set_scancode (GdkEvent *event, void gdk_event_set_seat (GdkEvent *event, GdkSeat *seat); +/* The IME IM module needs this symbol exported. */ +_GDK_EXTERN +gboolean gdk_event_is_allocated (const GdkEvent *event); + void _gdk_event_emit (GdkEvent *event); GList* _gdk_event_queue_find_first (GdkDisplay *display); void _gdk_event_queue_remove_link (GdkDisplay *display, diff --git a/gdk/win32/gdkevents-win32.c b/gdk/win32/gdkevents-win32.c index 7aef277c27..dcb448b12f 100644 --- a/gdk/win32/gdkevents-win32.c +++ b/gdk/win32/gdkevents-win32.c @@ -2595,6 +2595,35 @@ gdk_event_translate (MSG *msg, gdk_event_set_device (event, device_manager_win32->core_keyboard); gdk_event_set_source_device (event, device_manager_win32->system_keyboard); gdk_event_set_seat (event, gdk_device_get_seat (device_manager_win32->core_keyboard)); + + /* Get the WinAPI translation of the WM_KEY messages to characters. + + The WM_CHAR messages are generated by a previous call to TranslateMessage() and always + follow directly after the corresponding WM_KEY* messages. + There could be 0 or more WM_CHAR messages following (for example dead keys don't generate + WM_CHAR messages - they generate WM_DEAD_CHAR instead, but we are not interested in those + messages). */ + + if (gdk_event_is_allocated(event)) { /* Should always be true */ + GdkEventPrivate *event_priv = (GdkEventPrivate*)event; + + MSG msg2; + while (PeekMessageW(&msg2, msg->hwnd, 0, 0, 0) && (msg2.message == WM_CHAR || msg2.message == WM_SYSCHAR)) { + /* The character is encoded in WPARAM as UTF-16. */ + gunichar2 c = msg2.wParam; + + if (!g_unichar_iscntrl(c)) { /* Ignore control sequences like Backspace */ + /* Append character to translation string. */ + event_priv->translation_len++; + event_priv->translation = g_realloc(event_priv->translation, event_priv->translation_len*sizeof(event_priv->translation[0])); + event_priv->translation[event_priv->translation_len-1] = c; + } + + /* Remove message from queue */ + GetMessageW(&msg2, msg->hwnd, 0, 0); + } + } + if (HIWORD (msg->lParam) & KF_EXTENDED) { switch (msg->wParam) diff --git a/modules/input/gtkimcontextime.c b/modules/input/gtkimcontextime.c index 3ca7b5e661..d893114747 100644 --- a/modules/input/gtkimcontextime.c +++ b/modules/input/gtkimcontextime.c @@ -33,7 +33,9 @@ #include "gdk/gdkkeysyms-compat.h" #include "gdk/win32/gdkwin32.h" +#include "gdk/win32/gdkprivate-win32.h" #include "gdk/gdkkeysyms.h" +#include "gdk/gdkinternals.h" #include @@ -59,10 +61,6 @@ typedef enum { GTK_WIN32_IME_FOCUS_BEHAVIOR_FOLLOW, } GtkWin32IMEFocusBehavior; -#define IS_DEAD_KEY(k) \ - ((k) >= GDK_dead_grave && (k) <= (GDK_dead_dasia+1)) - - struct _GtkIMContextIMEPrivate { /* When pretend_empty_preedit is set to TRUE, @@ -80,7 +78,6 @@ struct _GtkIMContextIMEPrivate * https://gitlab.gnome.org/GNOME/gtk/commit/c255ba68fc2c918dd84da48a472e7973d3c00b03 */ gboolean pretend_empty_preedit; - guint32 dead_key_keyval; GtkWin32IMEFocusBehavior focus_behavior; }; @@ -318,125 +315,30 @@ gtk_im_context_ime_set_client_window (GtkIMContext *context, g_return_if_fail (GDK_IS_WINDOW (context_ime->toplevel)); } -static gunichar -_gtk_im_context_ime_dead_key_unichar (guint keyval, - gboolean spacing) -{ - switch (keyval) - { -#define CASE(keysym, unicode, spacing_unicode) \ - case GDK_dead_##keysym: return (spacing) ? spacing_unicode : unicode; - - CASE (grave, 0x0300, 0x0060); - CASE (acute, 0x0301, 0x00b4); - CASE (circumflex, 0x0302, 0x005e); - CASE (tilde, 0x0303, 0x007e); /* Also used with perispomeni, 0x342. */ - CASE (macron, 0x0304, 0x00af); - CASE (breve, 0x0306, 0x02d8); - CASE (abovedot, 0x0307, 0x02d9); - CASE (diaeresis, 0x0308, 0x00a8); - CASE (hook, 0x0309, 0); - CASE (abovering, 0x030A, 0x02da); - CASE (doubleacute, 0x030B, 0x2dd); - CASE (caron, 0x030C, 0x02c7); - CASE (abovecomma, 0x0313, 0); /* Equivalent to psili */ - CASE (abovereversedcomma, 0x0314, 0); /* Equivalent to dasia */ - CASE (horn, 0x031B, 0); /* Legacy use for psili, 0x313 (or 0x343). */ - CASE (belowdot, 0x0323, 0); - CASE (cedilla, 0x0327, 0x00b8); - CASE (ogonek, 0x0328, 0); /* Legacy use for dasia, 0x314.*/ - CASE (iota, 0x0345, 0); - -#undef CASE - default: - return 0; - } -} - -static void -_gtk_im_context_ime_commit_unichar (GtkIMContextIME *context_ime, - gunichar c) -{ - gchar utf8[10]; - int len; - - if (context_ime->priv->dead_key_keyval != 0) - { - gunichar combining; - - combining = - _gtk_im_context_ime_dead_key_unichar (context_ime->priv->dead_key_keyval, - FALSE); - g_unichar_compose (c, combining, &c); - } - - len = g_unichar_to_utf8 (c, utf8); - utf8[len] = 0; - - g_signal_emit_by_name (context_ime, "commit", utf8); - context_ime->priv->dead_key_keyval = 0; -} - static gboolean gtk_im_context_ime_filter_keypress (GtkIMContext *context, GdkEventKey *event) { GtkIMContextIME *context_ime; - gboolean retval = FALSE; - guint32 c; + GdkEventPrivate *event_priv; + gchar *utf8; g_return_val_if_fail (GTK_IS_IM_CONTEXT_IME (context), FALSE); g_return_val_if_fail (event, FALSE); - if (event->type == GDK_KEY_RELEASE) - return FALSE; - - if (event->state & GDK_CONTROL_MASK) - return FALSE; - context_ime = GTK_IM_CONTEXT_IME (context); - if (!context_ime->focus) + g_return_val_if_fail (gdk_event_is_allocated(event), FALSE); + + event_priv = (GdkEventPrivate*)event; + if (event_priv->translation_len == 0) return FALSE; - if (!GDK_IS_WINDOW (context_ime->client_window)) - return FALSE; + utf8 = g_utf16_to_utf8(event_priv->translation, event_priv->translation_len, NULL, NULL, NULL); + g_signal_emit_by_name (context_ime, "commit", utf8); + g_free(utf8); - if (event->keyval == GDK_space && - context_ime->priv->dead_key_keyval != 0) - { - c = _gtk_im_context_ime_dead_key_unichar (context_ime->priv->dead_key_keyval, TRUE); - context_ime->priv->dead_key_keyval = 0; - _gtk_im_context_ime_commit_unichar (context_ime, c); - return TRUE; - } - - c = gdk_keyval_to_unicode (event->keyval); - - if (c) - { - _gtk_im_context_ime_commit_unichar (context_ime, c); - retval = TRUE; - } - else if (IS_DEAD_KEY (event->keyval)) - { - gunichar dead_key; - - dead_key = _gtk_im_context_ime_dead_key_unichar (event->keyval, FALSE); - - /* Emulate double input of dead keys */ - if (dead_key && event->keyval == context_ime->priv->dead_key_keyval) - { - c = _gtk_im_context_ime_dead_key_unichar (context_ime->priv->dead_key_keyval, TRUE); - context_ime->priv->dead_key_keyval = 0; - _gtk_im_context_ime_commit_unichar (context_ime, c); - _gtk_im_context_ime_commit_unichar (context_ime, c); - } - else - context_ime->priv->dead_key_keyval = event->keyval; - } - - return retval; + return TRUE; } diff --git a/modules/input/imime.c b/modules/input/imime.c index d2943cf9cd..dea7ae12c7 100644 --- a/modules/input/imime.c +++ b/modules/input/imime.c @@ -30,7 +30,7 @@ static const GtkIMContextInfo ime_info = { NC_("input method menu", "Windows IME"), GETTEXT_PACKAGE, "", - "ja:ko:zh", + "*", }; static const GtkIMContextInfo *info_list[] = { From 5413892b7dab75959c00b1c40391c25a1056d03a Mon Sep 17 00:00:00 2001 From: Philip Zander Date: Fri, 12 Jul 2019 15:00:17 +0200 Subject: [PATCH 2/4] Formatting --- gdk/gdkevents.c | 4 ++-- gdk/win32/gdkevents-win32.c | 32 +++++++++++++++++--------------- modules/input/gtkimcontextime.c | 8 ++++---- 3 files changed, 23 insertions(+), 21 deletions(-) diff --git a/gdk/gdkevents.c b/gdk/gdkevents.c index 95af90d698..12af9ccb96 100644 --- a/gdk/gdkevents.c +++ b/gdk/gdkevents.c @@ -666,7 +666,7 @@ gdk_event_copy (const GdkEvent *event) #ifdef GDK_WINDOWING_WIN32 new_private->translation_len = private->translation_len; - new_private->translation = g_memdup(private->translation, private->translation_len*sizeof(private->translation[0])); + new_private->translation = g_memdup (private->translation, private->translation_len * sizeof (private->translation[0])); #endif } @@ -773,7 +773,7 @@ gdk_event_free (GdkEvent *event) g_clear_object (&private->device); g_clear_object (&private->source_device); #ifdef GDK_WINDOWING_WIN32 - g_free(private->translation); + g_free (private->translation); #endif } diff --git a/gdk/win32/gdkevents-win32.c b/gdk/win32/gdkevents-win32.c index dcb448b12f..a427f39463 100644 --- a/gdk/win32/gdkevents-win32.c +++ b/gdk/win32/gdkevents-win32.c @@ -2604,25 +2604,27 @@ gdk_event_translate (MSG *msg, WM_CHAR messages - they generate WM_DEAD_CHAR instead, but we are not interested in those messages). */ - if (gdk_event_is_allocated(event)) { /* Should always be true */ - GdkEventPrivate *event_priv = (GdkEventPrivate*)event; + if (gdk_event_is_allocated (event)) /* Should always be true */ + { + GdkEventPrivate *event_priv = (GdkEventPrivate*) event; - MSG msg2; - while (PeekMessageW(&msg2, msg->hwnd, 0, 0, 0) && (msg2.message == WM_CHAR || msg2.message == WM_SYSCHAR)) { - /* The character is encoded in WPARAM as UTF-16. */ - gunichar2 c = msg2.wParam; + MSG msg2; + while (PeekMessageW (&msg2, msg->hwnd, 0, 0, 0) && (msg2.message == WM_CHAR || msg2.message == WM_SYSCHAR)) + { + /* The character is encoded in WPARAM as UTF-16. */ + gunichar2 c = msg2.wParam; - if (!g_unichar_iscntrl(c)) { /* Ignore control sequences like Backspace */ - /* Append character to translation string. */ - event_priv->translation_len++; - event_priv->translation = g_realloc(event_priv->translation, event_priv->translation_len*sizeof(event_priv->translation[0])); - event_priv->translation[event_priv->translation_len-1] = c; - } + if (!g_unichar_iscntrl(c)) { /* Ignore control sequences like Backspace */ + /* Append character to translation string. */ + event_priv->translation_len ++; + event_priv->translation = g_realloc (event_priv->translation, event_priv->translation_len * sizeof (event_priv->translation[0])); + event_priv->translation[event_priv->translation_len - 1] = c; + } - /* Remove message from queue */ - GetMessageW(&msg2, msg->hwnd, 0, 0); + /* Remove message from queue */ + GetMessageW (&msg2, msg->hwnd, 0, 0); + } } - } if (HIWORD (msg->lParam) & KF_EXTENDED) { diff --git a/modules/input/gtkimcontextime.c b/modules/input/gtkimcontextime.c index d893114747..1881b18a08 100644 --- a/modules/input/gtkimcontextime.c +++ b/modules/input/gtkimcontextime.c @@ -328,15 +328,15 @@ gtk_im_context_ime_filter_keypress (GtkIMContext *context, context_ime = GTK_IM_CONTEXT_IME (context); - g_return_val_if_fail (gdk_event_is_allocated(event), FALSE); + g_return_val_if_fail (gdk_event_is_allocated (event), FALSE); - event_priv = (GdkEventPrivate*)event; + event_priv = (GdkEventPrivate*) event; if (event_priv->translation_len == 0) return FALSE; - utf8 = g_utf16_to_utf8(event_priv->translation, event_priv->translation_len, NULL, NULL, NULL); + utf8 = g_utf16_to_utf8 (event_priv->translation, event_priv->translation_len, NULL, NULL, NULL); g_signal_emit_by_name (context_ime, "commit", utf8); - g_free(utf8); + g_free (utf8); return TRUE; } From 9bf9c76365840c66ee775bcfbe2a1ca4301d5807 Mon Sep 17 00:00:00 2001 From: Philip Zander Date: Tue, 20 Aug 2019 15:14:10 +0200 Subject: [PATCH 3/4] Remove VK_PACKET handling code that has become unnecessary --- gdk/win32/gdkevents-win32.c | 67 ++++--------------------------------- gdk/win32/gdkwindow-win32.h | 8 ----- 2 files changed, 6 insertions(+), 69 deletions(-) diff --git a/gdk/win32/gdkevents-win32.c b/gdk/win32/gdkevents-win32.c index a427f39463..0e568ae505 100644 --- a/gdk/win32/gdkevents-win32.c +++ b/gdk/win32/gdkevents-win32.c @@ -2556,31 +2556,6 @@ gdk_event_translate (MSG *msg, API_CALL (GetKeyboardState, (key_state)); - ccount = 0; - - if (msg->wParam == VK_PACKET) - { - ccount = ToUnicode (VK_PACKET, HIWORD (msg->lParam), key_state, wbuf, 1, 0); - if (ccount == 1) - { - if (wbuf[0] >= 0xD800 && wbuf[0] < 0xDC00) - { - if (msg->message == WM_KEYDOWN) - impl->leading_surrogate_keydown = wbuf[0]; - else - impl->leading_surrogate_keyup = wbuf[0]; - - /* don't emit an event */ - return_val = TRUE; - break; - } - else - { - /* wait until an event is created */; - } - } - } - event = gdk_event_new ((msg->message == WM_KEYDOWN || msg->message == WM_SYSKEYDOWN) ? GDK_KEY_PRESS : GDK_KEY_RELEASE); @@ -2654,42 +2629,12 @@ gdk_event_translate (MSG *msg, build_key_event_state (event, key_state); - if (msg->wParam == VK_PACKET && ccount == 1) - { - if (wbuf[0] >= 0xD800 && wbuf[0] < 0xDC00) - { - g_assert_not_reached (); - } - else if (wbuf[0] >= 0xDC00 && wbuf[0] < 0xE000) - { - wchar_t leading; - - if (msg->message == WM_KEYDOWN) - leading = impl->leading_surrogate_keydown; - else - leading = impl->leading_surrogate_keyup; - - event->key.keyval = gdk_unicode_to_keyval ((leading - 0xD800) * 0x400 + wbuf[0] - 0xDC00 + 0x10000); - } - else - { - event->key.keyval = gdk_unicode_to_keyval (wbuf[0]); - } - } - else - { - gdk_keymap_translate_keyboard_state (_gdk_win32_display_get_keymap (display), - event->key.hardware_keycode, - event->key.state, - event->key.group, - &event->key.keyval, - NULL, NULL, NULL); - } - - if (msg->message == WM_KEYDOWN) - impl->leading_surrogate_keydown = 0; - else - impl->leading_surrogate_keyup = 0; + gdk_keymap_translate_keyboard_state (_gdk_win32_display_get_keymap (display), + event->key.hardware_keycode, + event->key.state, + event->key.group, + &event->key.keyval, + NULL, NULL, NULL); fill_key_event_string (event); diff --git a/gdk/win32/gdkwindow-win32.h b/gdk/win32/gdkwindow-win32.h index e1ef92096d..23f5b8d86c 100644 --- a/gdk/win32/gdkwindow-win32.h +++ b/gdk/win32/gdkwindow-win32.h @@ -240,14 +240,6 @@ struct _GdkWindowImplWin32 HICON hicon_big; HICON hicon_small; - /* When VK_PACKET sends us a leading surrogate, it's stashed here. - * Later, when another VK_PACKET sends a tailing surrogate, we make up - * a full unicode character from them, or discard the leading surrogate, - * if the next key is not a tailing surrogate. - */ - wchar_t leading_surrogate_keydown; - wchar_t leading_surrogate_keyup; - /* Window size hints */ gint hint_flags; GdkGeometry hints; From 5b818642111231c7f22e5d4d2bf60b449ec69e64 Mon Sep 17 00:00:00 2001 From: Philip Zander Date: Thu, 16 Jul 2020 15:44:08 +0200 Subject: [PATCH 4/4] Fix 2 warnings, formatting --- gdk/win32/gdkevents-win32.c | 14 ++++++++------ modules/input/gtkimcontextime.c | 3 ++- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/gdk/win32/gdkevents-win32.c b/gdk/win32/gdkevents-win32.c index 0e568ae505..327d288bc7 100644 --- a/gdk/win32/gdkevents-win32.c +++ b/gdk/win32/gdkevents-win32.c @@ -2589,12 +2589,14 @@ gdk_event_translate (MSG *msg, /* The character is encoded in WPARAM as UTF-16. */ gunichar2 c = msg2.wParam; - if (!g_unichar_iscntrl(c)) { /* Ignore control sequences like Backspace */ - /* Append character to translation string. */ - event_priv->translation_len ++; - event_priv->translation = g_realloc (event_priv->translation, event_priv->translation_len * sizeof (event_priv->translation[0])); - event_priv->translation[event_priv->translation_len - 1] = c; - } + /* Ignore control sequences like Backspace */ + if (!g_unichar_iscntrl(c)) + { + /* Append character to translation string. */ + event_priv->translation_len ++; + event_priv->translation = g_realloc (event_priv->translation, event_priv->translation_len * sizeof (event_priv->translation[0])); + event_priv->translation[event_priv->translation_len - 1] = c; + } /* Remove message from queue */ GetMessageW (&msg2, msg->hwnd, 0, 0); diff --git a/modules/input/gtkimcontextime.c b/modules/input/gtkimcontextime.c index 1881b18a08..8973b70113 100644 --- a/modules/input/gtkimcontextime.c +++ b/modules/input/gtkimcontextime.c @@ -27,6 +27,7 @@ #undef GTK_DISABLE_DEPRECATED #endif +#include "config.h" #include "gtkimcontextime.h" #include "imm-extra.h" @@ -328,7 +329,7 @@ gtk_im_context_ime_filter_keypress (GtkIMContext *context, context_ime = GTK_IM_CONTEXT_IME (context); - g_return_val_if_fail (gdk_event_is_allocated (event), FALSE); + g_return_val_if_fail (gdk_event_is_allocated ((GdkEvent*)event), FALSE); event_priv = (GdkEventPrivate*) event; if (event_priv->translation_len == 0)