diff --git a/gdk/broadway/gdkeventsource.c b/gdk/broadway/gdkeventsource.c index 045c8f3040..7aa496d370 100644 --- a/gdk/broadway/gdkeventsource.c +++ b/gdk/broadway/gdkeventsource.c @@ -262,7 +262,8 @@ _gdk_broadway_events_got_input (GdkDisplay *display, message->key.state, FALSE, &translated, - &translated); + &translated, + NULL); node = _gdk_event_queue_append (display, event); _gdk_windowing_got_event (display, node, event, message->base.serial); diff --git a/gdk/gdkevents.c b/gdk/gdkevents.c index 3e68b26579..890dbf01a1 100644 --- a/gdk/gdkevents.c +++ b/gdk/gdkevents.c @@ -1517,6 +1517,16 @@ gdk_button_event_get_button (GdkEvent *event) * An event related to a key-based device. */ +static void +gdk_key_event_finalize (GdkEvent *event) +{ + GdkKeyEvent *self = (GdkKeyEvent *) event; + + g_free (self->compose_sequence); + + GDK_EVENT_SUPER (event)->finalize (event); +} + static GdkModifierType gdk_key_event_get_state (GdkEvent *event) { @@ -1528,7 +1538,7 @@ gdk_key_event_get_state (GdkEvent *event) static const GdkEventTypeInfo gdk_key_event_info = { sizeof (GdkKeyEvent), NULL, - NULL, + gdk_key_event_finalize, gdk_key_event_get_state, NULL, NULL, @@ -1552,6 +1562,10 @@ GDK_DEFINE_EVENT_TYPE (GdkKeyEvent, gdk_key_event, * @is_modifier: whether the event is a modifiers only event * @translated: the translated key data for the given @state * @no_lock: the translated key data without the given @state + * @compose_sequence: (transfer none) (nullable): + * The compose sequence string, either partial or only the + * final composed string, if that can be determined at event + * creation time. Used by selected IM modules. * * Creates a new `GdkKeyEvent`. * @@ -1566,7 +1580,8 @@ gdk_key_event_new (GdkEventType type, GdkModifierType state, gboolean is_modifier, GdkTranslatedKey *translated, - GdkTranslatedKey *no_lock) + GdkTranslatedKey *no_lock, + char *compose_sequence) { g_return_val_if_fail (type == GDK_KEY_PRESS || type == GDK_KEY_RELEASE, NULL); @@ -1579,6 +1594,7 @@ gdk_key_event_new (GdkEventType type, self->key_is_modifier = is_modifier; self->translated[0] = *translated; self->translated[1] = *no_lock; + self->compose_sequence = g_strdup (compose_sequence); return event; } @@ -1609,6 +1625,26 @@ gdk_key_event_get_translated_key (GdkEvent *event, return &(self->translated[0]); } +/*< private > + * gdk_key_event_get_compose_sequence: + * @event: (type GdkKeyEvent): a key event + * + * Extracts the compose sequence string from a key event. + * + * Returns: (transfer none): the compose sequence string + */ +char * +gdk_key_event_get_compose_sequence (GdkEvent *event) +{ + GdkKeyEvent *self = (GdkKeyEvent *) event; + + g_return_val_if_fail (GDK_IS_EVENT (event), 0); + g_return_val_if_fail (GDK_IS_EVENT_TYPE (event, GDK_KEY_PRESS) || + GDK_IS_EVENT_TYPE (event, GDK_KEY_RELEASE), FALSE); + + return self->compose_sequence; +} + /** * gdk_key_event_get_keyval: * @event: (type GdkKeyEvent): a key event diff --git a/gdk/gdkeventsprivate.h b/gdk/gdkeventsprivate.h index a36c5d100b..aad8218b37 100644 --- a/gdk/gdkeventsprivate.h +++ b/gdk/gdkeventsprivate.h @@ -259,6 +259,9 @@ typedef struct { * @keycode: the raw code of the key that was pressed or released. * @translated: the result of translating @keycode. First with the full * @state, then while ignoring Caps Lock. + * @compose_sequence: optional string for use by selected IM modules. + * Contains either partial compose sequences or the final composed + * string of the keystroke sequence. * * Describes a key press or key release event. */ @@ -270,6 +273,7 @@ struct _GdkKeyEvent guint32 keycode; gboolean key_is_modifier; GdkTranslatedKey translated[2]; + char *compose_sequence; }; /* @@ -470,7 +474,8 @@ GdkEvent * gdk_key_event_new (GdkEventType type, GdkModifierType modifiers, gboolean is_modifier, GdkTranslatedKey *translated, - GdkTranslatedKey *no_lock); + GdkTranslatedKey *no_lock, + char *compose_sequence); GdkEvent * gdk_focus_event_new (GdkSurface *surface, GdkDevice *device, @@ -597,6 +602,8 @@ GdkEvent * gdk_grab_broken_event_new (GdkSurface *surface, GdkTranslatedKey * gdk_key_event_get_translated_key (GdkEvent *event, gboolean no_lock); +char * gdk_key_event_get_compose_sequence (GdkEvent *event); + typedef enum { /* Following flag is set for events on the event queue during @@ -626,7 +633,6 @@ void _gdk_event_queue_flush (GdkDisplay *display); double * gdk_event_dup_axes (GdkEvent *event); - G_END_DECLS #endif /* __GDK_EVENTS_PRIVATE_H__ */ diff --git a/gdk/macos/gdkmacosdisplay-translate.c b/gdk/macos/gdkmacosdisplay-translate.c index 7eaa6dca03..502a9a8cac 100644 --- a/gdk/macos/gdkmacosdisplay-translate.c +++ b/gdk/macos/gdkmacosdisplay-translate.c @@ -431,7 +431,8 @@ fill_key_event (GdkMacosDisplay *display, state, is_modifier, &translated, - &no_lock); + &no_lock, + NULL); } static GdkEvent * diff --git a/gdk/macos/gdkmacossurface.c b/gdk/macos/gdkmacossurface.c index 46074a5d9d..b847ea0ff4 100644 --- a/gdk/macos/gdkmacossurface.c +++ b/gdk/macos/gdkmacossurface.c @@ -944,7 +944,8 @@ _gdk_macos_surface_synthesize_null_key (GdkMacosSurface *self) 0, FALSE, &translated, - &no_lock); + &no_lock, + NULL); _gdk_event_queue_append (display, event); } diff --git a/gdk/wayland/gdkdevice-wayland.c b/gdk/wayland/gdkdevice-wayland.c index cd314b6d67..98e08ecb5f 100644 --- a/gdk/wayland/gdkdevice-wayland.c +++ b/gdk/wayland/gdkdevice-wayland.c @@ -2205,7 +2205,8 @@ deliver_key_event (GdkWaylandSeat *seat, device_get_modifiers (seat->logical_pointer), _gdk_wayland_keymap_key_is_modifier (keymap, key), &translated, - &no_lock); + &no_lock, + NULL); _gdk_wayland_display_deliver_event (seat->display, event); diff --git a/gdk/win32/gdkevents-win32.c b/gdk/win32/gdkevents-win32.c index f85aca6506..9857095519 100644 --- a/gdk/win32/gdkevents-win32.c +++ b/gdk/win32/gdkevents-win32.c @@ -1857,7 +1857,8 @@ gdk_event_translate (MSG *msg, 0, FALSE, &translated, - &translated); + &translated, + NULL); _gdk_win32_append_event (event); } break; @@ -1906,8 +1907,12 @@ gdk_event_translate (MSG *msg, GdkTranslatedKey translated; GdkTranslatedKey no_lock; BYTE key_state[256]; - wchar_t wbuf[100]; - int ccount = 0; + GArray *translation; + MSG msg2; + int level = 0; + int effective_group = 0; + GdkModifierType consumed = 0; + char *composed = NULL; /* Ignore key messages intended for the IME */ if (msg->wParam == VK_PROCESSKEY || in_ime_composition) @@ -1929,34 +1934,41 @@ 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 */; - } - } - } - keyval = GDK_KEY_VoidSymbol; keycode = msg->wParam; + /* 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). */ + + translation = g_array_sized_new (FALSE, FALSE, sizeof (gunichar2), 2); + 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; + + /* Append character to translation string. */ + g_array_append_val (translation, c); + + /* Remove message from queue */ + GetMessageW (&msg2, msg->hwnd, 0, 0); + } + + if (translation->len > 0) + composed = g_utf16_to_utf8 ((gunichar2*)translation->data, + translation->len, NULL, NULL, NULL); + + g_array_unref (translation); + translation = NULL; + + /* Ignore control sequences like Backspace */ + if (composed && g_unichar_iscntrl (g_utf8_get_char (composed))) + g_clear_pointer (&composed, g_free); + if (HIWORD (msg->lParam) & KF_EXTENDED) { switch (msg->wParam) @@ -1985,61 +1997,20 @@ gdk_event_translate (MSG *msg, state = build_key_event_state (key_state); group = get_active_group (); - 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; + gdk_keymap_translate_keyboard_state ((GdkKeymap*) win32_keymap, keycode, state, group, + &keyval, &effective_group, &level, &consumed); + translated.keyval = keyval; + translated.consumed = consumed; + translated.layout = effective_group; + translated.level = level; - if (msg->message == WM_KEYDOWN) - leading = impl->leading_surrogate_keydown; - else - leading = impl->leading_surrogate_keyup; - - keyval = gdk_unicode_to_keyval ((leading - 0xD800) * 0x400 + wbuf[0] - 0xDC00 + 0x10000); - } - else - { - keyval = gdk_unicode_to_keyval (wbuf[0]); - } - - translated.keyval = keyval; - translated.consumed = 0; - translated.layout = 0; - translated.level = 0; - - no_lock = translated; - } - else - { - int level = 0; - int effective_group = 0; - GdkModifierType consumed = 0; - - gdk_keymap_translate_keyboard_state ((GdkKeymap*) win32_keymap, keycode, state, group, - &keyval, &effective_group, &level, &consumed); - translated.keyval = keyval; - translated.consumed = consumed; - translated.layout = effective_group; - translated.level = level; - - gdk_keymap_translate_keyboard_state ((GdkKeymap*) win32_keymap, keycode, - state & ~GDK_LOCK_MASK, group, &keyval, - &effective_group, &level, &consumed); - no_lock.keyval = keyval; - no_lock.consumed = consumed; - no_lock.layout = effective_group; - no_lock.level = level; - } - - if (msg->message == WM_KEYDOWN) - impl->leading_surrogate_keydown = 0; - else - impl->leading_surrogate_keyup = 0; + gdk_keymap_translate_keyboard_state ((GdkKeymap*) win32_keymap, keycode, + state & ~GDK_LOCK_MASK, group, &keyval, + &effective_group, &level, &consumed); + no_lock.keyval = keyval; + no_lock.consumed = consumed; + no_lock.layout = effective_group; + no_lock.level = level; /* Only one release key event is fired when both shift keys are pressed together and then released. In order to send the missing event, press events for shift @@ -2087,10 +2058,12 @@ gdk_event_translate (MSG *msg, state, is_modifier, &translated, - &no_lock); + &no_lock, + composed); _gdk_win32_append_event (event); + g_free (composed); return_val = TRUE; } break; @@ -2169,7 +2142,8 @@ gdk_event_translate (MSG *msg, build_key_event_state (key_state), FALSE, &translated, - &translated); + &translated, + NULL); _gdk_win32_append_event (event); @@ -2182,7 +2156,8 @@ gdk_event_translate (MSG *msg, build_key_event_state (key_state), FALSE, &translated, - &translated); + &translated, + NULL); _gdk_win32_append_event (event); } diff --git a/gdk/win32/gdksurface-win32.h b/gdk/win32/gdksurface-win32.h index cff41117ef..22daa8faa7 100644 --- a/gdk/win32/gdksurface-win32.h +++ b/gdk/win32/gdksurface-win32.h @@ -235,14 +235,6 @@ struct _GdkWin32Surface /* The cursor that GDK set for this window via GdkDevice */ GdkWin32HCursor *cursor; - /* 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 */ int hint_flags; GdkGeometry hints; diff --git a/gdk/x11/gdkdevicemanager-xi2.c b/gdk/x11/gdkdevicemanager-xi2.c index 566d519809..02ceb2f110 100644 --- a/gdk/x11/gdkdevicemanager-xi2.c +++ b/gdk/x11/gdkdevicemanager-xi2.c @@ -1595,7 +1595,8 @@ gdk_x11_device_manager_xi2_translate_event (GdkEventTranslator *translator, state, gdk_x11_keymap_key_is_modifier (keymap, xev->detail), &translated, - &no_lock); + &no_lock, + NULL); if (ev->evtype == XI_KeyPress) set_user_time (event); diff --git a/gtk/gtkimcontext.c b/gtk/gtkimcontext.c index 3772d58db4..26f33b2cbe 100644 --- a/gtk/gtkimcontext.c +++ b/gtk/gtkimcontext.c @@ -607,7 +607,8 @@ gtk_im_context_filter_key (GtkIMContext *context, state, FALSE, /* FIXME */ &translated, - &no_lock); + &no_lock, + NULL); ret = GTK_IM_CONTEXT_GET_CLASS (context)->filter_keypress (context, key); diff --git a/gtk/gtkimcontextime.c b/gtk/gtkimcontextime.c index e341925733..1fba0af36f 100644 --- a/gtk/gtkimcontextime.c +++ b/gtk/gtkimcontextime.c @@ -34,6 +34,7 @@ #include "imm-extra.h" #include "gdk/gdkkeysyms.h" +#include "gdk/gdkeventsprivate.h" #include "gdk/win32/gdkwin32.h" #include "gtk/gtkimmodule.h" #include "gtk/gtkstylecontextprivate.h" @@ -61,9 +62,6 @@ typedef enum { GTK_WIN32_IME_FOCUS_BEHAVIOR_FOLLOW, } GtkWin32IMEFocusBehavior; -#define IS_DEAD_KEY(k) \ - ((k) >= GDK_KEY_dead_grave && (k) <= (GDK_KEY_dead_dasia+1)) - struct _GtkIMContextIMEPrivate { /* When pretend_empty_preedit is set to TRUE, @@ -81,7 +79,6 @@ struct _GtkIMContextIMEPrivate * https://gitlab.gnome.org/GNOME/gtk/commit/c255ba68fc2c918dd84da48a472e7973d3c00b03 */ gboolean pretend_empty_preedit; - guint32 dead_key_keyval; GtkWin32IMEFocusBehavior focus_behavior; }; @@ -277,134 +274,26 @@ gtk_im_context_ime_set_client_widget (GtkIMContext *context, context_ime->client_surface = surface; } -static gunichar -_gtk_im_context_ime_dead_key_unichar (guint keyval, - gboolean spacing) -{ - switch (keyval) - { -#define CASE(keysym, unicode, spacing_unicode) \ - case GDK_KEY_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) -{ - char 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, GdkEvent *event) { GtkIMContextIME *context_ime; - gboolean retval = FALSE; - guint32 c; - GdkModifierType state, consumed_modifiers, no_text_input_mask; - guint keyval; + char *compose_sequence = NULL; g_return_val_if_fail (GTK_IS_IM_CONTEXT_IME (context), FALSE); g_return_val_if_fail (event, FALSE); - if (gdk_event_get_event_type ((GdkEvent *) event) == GDK_KEY_RELEASE) - return FALSE; - - no_text_input_mask = GDK_ALT_MASK|GDK_CONTROL_MASK; - - state = gdk_event_get_modifier_state ((GdkEvent *) event); - consumed_modifiers = gdk_key_event_get_consumed_modifiers (event); - - if (state & no_text_input_mask & ~consumed_modifiers) - return FALSE; - context_ime = GTK_IM_CONTEXT_IME (context); - if (!context_ime->focus) - return FALSE; - - if (!GDK_IS_SURFACE (context_ime->client_surface)) - return FALSE; - - keyval = gdk_key_event_get_keyval ((GdkEvent *) event); - - if (keyval == GDK_KEY_space && - context_ime->priv->dead_key_keyval != 0) + compose_sequence = gdk_key_event_get_compose_sequence (event); + if (compose_sequence) { - 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); + g_signal_emit_by_name (context_ime, "commit", compose_sequence); return TRUE; } - c = gdk_keyval_to_unicode (keyval); - - if (c && !g_unichar_iscntrl(c)) - { - _gtk_im_context_ime_commit_unichar (context_ime, c); - retval = TRUE; - } - else if (IS_DEAD_KEY (keyval)) - { - gunichar dead_key; - - dead_key = _gtk_im_context_ime_dead_key_unichar (keyval, FALSE); - - /* Emulate double input of dead keys */ - if (dead_key && 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 = keyval; - } - - return retval; + return FALSE; } diff --git a/gtk/gtkimcontextsimple.c b/gtk/gtkimcontextsimple.c index a0682bd617..8eede2b243 100644 --- a/gtk/gtkimcontextsimple.c +++ b/gtk/gtkimcontextsimple.c @@ -663,7 +663,8 @@ no_sequence_matches (GtkIMContextSimple *context_simple, gdk_event_get_modifier_state (event), FALSE, &translated, - &translated); + &translated, + NULL); gtk_im_context_filter_keypress (context, tmp_event); gdk_event_unref (tmp_event); diff --git a/gtk/gtkmain.c b/gtk/gtkmain.c index 7ea84d4878..65a6c10e6e 100644 --- a/gtk/gtkmain.c +++ b/gtk/gtkmain.c @@ -1084,7 +1084,8 @@ rewrite_event_for_toplevel (GdkEvent *event) gdk_key_event_get_keycode (event), gdk_event_get_modifier_state (event), gdk_key_event_is_modifier (event), - key, key_no_lock); + key, key_no_lock, + gdk_key_event_get_compose_sequence (event)); } static gboolean