diff --git a/gdk/win32/gdkevents-win32.c b/gdk/win32/gdkevents-win32.c index 1fe362d08e..9f60588bc5 100644 --- a/gdk/win32/gdkevents-win32.c +++ b/gdk/win32/gdkevents-win32.c @@ -1718,6 +1718,367 @@ _gdk_win32_surface_fill_min_max_info (GdkSurface *surface, return TRUE; } +static gboolean +handle_keyboard_event (GdkDisplay *display, + GdkSurface *surface, + MSG *msg, + int *ret_valp, + gboolean *goto_done) +{ + GdkEvent *event; + GdkWin32Display *win32_display = GDK_WIN32_DISPLAY (display); + GdkWin32Keymap *win32_keymap; + GdkTranslatedKey translated; + gboolean return_val = FALSE; + + switch (msg->message) + { + case WM_INPUTLANGCHANGE: + { + HKL input_locale; + + win32_keymap = GDK_WIN32_KEYMAP (gdk_display_get_keymap (display)); + + input_locale = (HKL) msg->lParam; + gdk_win32_display_set_input_locale (win32_display, input_locale); + _gdk_win32_keymap_set_active_layout (win32_keymap, input_locale); + gdk_win32_display_increment_keymap_serial (win32_display); + GDK_NOTE (EVENTS, + g_print (" cs:%lu hkl:%p%s", + (gulong) msg->wParam, + (gpointer) msg->lParam, + gdk_win32_display_input_locale_is_ime (win32_display) ? " (IME)" : "")); + gdk_display_setting_changed (display, "gtk-im-module"); + + /* Generate a dummy key event to "nudge" IMContext */ + translated.keyval = GDK_KEY_VoidSymbol; + translated.consumed = 0; + translated.layout = 0; + translated.level = 0; + event = gdk_key_event_new (GDK_KEY_PRESS, + surface, + win32_display->device_manager->core_keyboard, + _gdk_win32_get_next_tick (msg->time), + 0, + 0, + FALSE, + &translated, + &translated, + NULL); + _gdk_win32_append_event (event); + } + break; + case WM_SYSKEYUP: + case WM_SYSKEYDOWN: + GDK_NOTE (EVENTS, + g_print (" %s ch:%.02x %s", + _gdk_win32_key_to_string (msg->lParam), + (int) msg->wParam, + decode_key_lparam (msg->lParam))); + + /* If posted without us having keyboard focus, ignore */ + if ((msg->wParam != VK_F10 && msg->wParam != VK_MENU) && + !(HIWORD (msg->lParam) & KF_ALTDOWN)) + break; + + /* Let the system handle Alt-Tab, Alt-Space and Alt-F4 unless + * the keyboard is grabbed. + */ + if (!_gdk_display_get_last_device_grab (display, win32_display->device_manager->core_keyboard) && + (msg->wParam == VK_TAB || + msg->wParam == VK_SPACE || + msg->wParam == VK_F4)) + break; + + /* Jump to code in common with WM_KEYUP and WM_KEYDOWN */ + G_GNUC_FALLTHROUGH; + + case WM_KEYUP: + case WM_KEYDOWN: + { + GdkModifierType state; + guint keyval; + guint16 keycode; + guint8 group; + gboolean is_modifier; + GdkTranslatedKey no_lock; + BYTE key_state[256]; + GArray *translation; + MSG msg2; + int level = 0; + int effective_group = 0; + GdkModifierType consumed = 0; + char *composed = NULL; + GdkWin32Surface *impl; + + if (msg->message == WM_KEYUP || msg->message == WM_KEYDOWN) + { + GDK_NOTE (EVENTS, + g_print (" %s ch:%.02x %s", + _gdk_win32_key_to_string (msg->lParam), + (int) msg->wParam, + decode_key_lparam (msg->lParam))); + } + + /* Ignore key messages intended for the IME */ + if (msg->wParam == VK_PROCESSKEY || win32_display->event_record->in_ime_composition) + break; + + /* Ignore autorepeats on modifiers */ + if (msg->message == WM_KEYDOWN && + (msg->wParam == VK_MENU || msg->wParam == VK_CONTROL || msg->wParam == VK_SHIFT) && + ((HIWORD(msg->lParam) & KF_REPEAT) >= 1)) + break; + + if (GDK_SURFACE_DESTROYED (surface)) + break; + + win32_keymap = GDK_WIN32_KEYMAP (gdk_display_get_keymap (display)); + impl = GDK_WIN32_SURFACE (surface); + + API_CALL (GetKeyboardState, (key_state)); + + 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) + { + case VK_CONTROL: + keycode = VK_RCONTROL; + break; + case VK_SHIFT: /* Actually, KF_EXTENDED is not set + * for the right shift key. + */ + keycode = VK_RSHIFT; + break; + case VK_MENU: + keycode = VK_RMENU; + break; + } + } + else if (msg->wParam == VK_SHIFT && + LOBYTE (HIWORD (msg->lParam)) == _gdk_win32_keymap_get_rshift_scancode (win32_keymap)) + keycode = VK_RSHIFT; + + is_modifier = (msg->wParam == VK_CONTROL || + msg->wParam == VK_SHIFT || + msg->wParam == VK_MENU); + + state = build_key_event_state (display, key_state); + group = get_active_group (display); + + 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; + + /* 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 + keys are recorded and sent together when the release event occurs. + Other modifiers (e.g. ctrl, alt) don't have this problem. */ + if (msg->message == WM_KEYDOWN && msg->wParam == VK_SHIFT) + { + int pressed_shift = msg->lParam & 0xffffff; /* mask shift modifier */ + + if (win32_display->event_record->both_shift_pressed[0] == 0) + win32_display->event_record->both_shift_pressed[0] = pressed_shift; + else if (win32_display->event_record->both_shift_pressed[0] != pressed_shift) + win32_display->event_record->both_shift_pressed[1] = pressed_shift; + } + + if (msg->message == WM_KEYUP && msg->wParam == VK_SHIFT) + { + if (win32_display->event_record->both_shift_pressed[0] != 0 && + win32_display->event_record->both_shift_pressed[1] != 0) + { + int tmp_retval; + MSG fake_release = *msg; + int pressed_shift = msg->lParam & 0xffffff; + + if (win32_display->event_record->both_shift_pressed[0] == pressed_shift) + fake_release.lParam = win32_display->event_record->both_shift_pressed[1]; + else + fake_release.lParam = win32_display->event_record->both_shift_pressed[0]; + + win32_display->event_record->both_shift_pressed[0] = win32_display->event_record->both_shift_pressed[1] = 0; + gdk_event_translate (&fake_release, &tmp_retval); + } + win32_display->event_record->both_shift_pressed[0] = win32_display->event_record->both_shift_pressed[1] = 0; + } + + /* Reset ALT_MASK if it is the Alt key itself */ + if (msg->wParam == VK_MENU) + state &= ~GDK_ALT_MASK; + + event = gdk_key_event_new ((msg->message == WM_KEYDOWN || msg->message == WM_SYSKEYDOWN) ? GDK_KEY_PRESS : GDK_KEY_RELEASE, + surface, + win32_display->device_manager->core_keyboard, + _gdk_win32_get_next_tick (msg->time), + keycode, + state, + is_modifier, + &translated, + &no_lock, + composed); + + _gdk_win32_append_event (event); + + g_free (composed); + return_val = TRUE; + } + break; + + case WM_SYSCHAR: + if (msg->wParam != VK_SPACE) + { + /* To prevent beeps, don't let DefWindowProcW() be called */ + return_val = TRUE; + *goto_done = TRUE; + } + break; + + case WM_IME_STARTCOMPOSITION: + win32_display->event_record->in_ime_composition = TRUE; + break; + + case WM_IME_ENDCOMPOSITION: + win32_display->event_record->in_ime_composition = FALSE; + break; + case WM_IME_COMPOSITION: + { + BYTE key_state[256]; + wchar_t wbuf[100]; + int ccount = 0; + HIMC himc; + int i; + + /* On Win2k WM_IME_CHAR doesn't work correctly for non-Unicode + * applications. Thus, handle WM_IME_COMPOSITION with + * GCS_RESULTSTR instead, fetch the Unicode chars from the IME + * with ImmGetCompositionStringW(). + * + * See for instance + * http://groups.google.com/groups?selm=natX5.57%24g77.19788%40nntp2.onemain.com + * and + * http://groups.google.com/groups?selm=u2XfrXw5BHA.1628%40tkmsftngp02 + * for comments by other people that seems to have the same + * experience. WM_IME_CHAR just gives question marks, apparently + * because of going through some conversion to the current code + * page. + * + * WM_IME_CHAR might work on NT4 or Win9x with ActiveIMM, but + * use WM_IME_COMPOSITION there, too, to simplify the code. + */ + GDK_NOTE (EVENTS, g_print (" %#lx", (long) msg->lParam)); + + if (!(msg->lParam & GCS_RESULTSTR)) + break; + + if (GDK_SURFACE_DESTROYED (surface)) + break; + + himc = ImmGetContext (msg->hwnd); + ccount = ImmGetCompositionStringW (himc, GCS_RESULTSTR, + wbuf, sizeof (wbuf)); + ImmReleaseContext (msg->hwnd, himc); + + ccount /= 2; + + API_CALL (GetKeyboardState, (key_state)); + + for (i = 0; i < ccount; i++) + { + GdkTranslatedKey translated; + + /* Build a key press event */ + translated.keyval = gdk_unicode_to_keyval (wbuf[i]); + translated.consumed = 0; + translated.layout = get_active_group (display); + translated.level = 0; + event = gdk_key_event_new (GDK_KEY_PRESS, + surface, + win32_display->device_manager->core_keyboard, + _gdk_win32_get_next_tick (msg->time), + 0, + build_key_event_state (display, key_state), + FALSE, + &translated, + &translated, + NULL); + + _gdk_win32_append_event (event); + + /* Build a key release event. */ + event = gdk_key_event_new (GDK_KEY_RELEASE, + surface, + win32_display->device_manager->core_keyboard, + _gdk_win32_get_next_tick (msg->time), + 0, + build_key_event_state (display, key_state), + FALSE, + &translated, + &translated, + NULL); + + _gdk_win32_append_event (event); + } + + return_val = TRUE; + } + break; + default: + g_warning ("Maybe this was reached because this is not a keyboard-related event"); + g_assert_not_reached (); + } + + return return_val; +} + + #define GDK_ANY_BUTTON_MASK (GDK_BUTTON1_MASK | \ GDK_BUTTON2_MASK | \ GDK_BUTTON3_MASK | \ @@ -1732,7 +2093,6 @@ gdk_event_translate (MSG *msg, POINT point; MINMAXINFO *mmi; HWND hwnd; - HIMC himc; WINDOWPOS *hwndpos; gboolean ignore_leave; @@ -1754,8 +2114,7 @@ gdk_event_translate (MSG *msg, int button; gboolean return_val = FALSE; - - int i; + gboolean goto_done = FALSE; display = gdk_display_get_default (); win32_display = GDK_WIN32_DISPLAY (display); @@ -1811,346 +2170,19 @@ gdk_event_translate (MSG *msg, switch (msg->message) { + /* keyboard inpout related events (including IME events) */ case WM_INPUTLANGCHANGE: - { - GdkWin32Keymap *win32_keymap; - GdkTranslatedKey translated; - HKL input_locale; - - win32_keymap = GDK_WIN32_KEYMAP (gdk_display_get_keymap (display)); - - input_locale = (HKL) msg->lParam; - gdk_win32_display_set_input_locale (win32_display, input_locale); - _gdk_win32_keymap_set_active_layout (win32_keymap, input_locale); - gdk_win32_display_increment_keymap_serial (win32_display); - GDK_NOTE (EVENTS, - g_print (" cs:%lu hkl:%p%s", - (gulong) msg->wParam, - (gpointer) msg->lParam, - gdk_win32_display_input_locale_is_ime (win32_display) ? " (IME)" : "")); - gdk_display_setting_changed (display, "gtk-im-module"); - - /* Generate a dummy key event to "nudge" IMContext */ - translated.keyval = GDK_KEY_VoidSymbol; - translated.consumed = 0; - translated.layout = 0; - translated.level = 0; - event = gdk_key_event_new (GDK_KEY_PRESS, - surface, - win32_display->device_manager->core_keyboard, - _gdk_win32_get_next_tick (msg->time), - 0, - 0, - FALSE, - &translated, - &translated, - NULL); - _gdk_win32_append_event (event); - } - break; - case WM_SYSKEYUP: case WM_SYSKEYDOWN: - GDK_NOTE (EVENTS, - g_print (" %s ch:%.02x %s", - _gdk_win32_key_to_string (msg->lParam), - (int) msg->wParam, - decode_key_lparam (msg->lParam))); - - /* If posted without us having keyboard focus, ignore */ - if ((msg->wParam != VK_F10 && msg->wParam != VK_MENU) && - !(HIWORD (msg->lParam) & KF_ALTDOWN)) - break; - - /* Let the system handle Alt-Tab, Alt-Space and Alt-F4 unless - * the keyboard is grabbed. - */ - if (!keyboard_grab && - (msg->wParam == VK_TAB || - msg->wParam == VK_SPACE || - msg->wParam == VK_F4)) - break; - - /* Jump to code in common with WM_KEYUP and WM_KEYDOWN */ - goto keyup_or_down; - case WM_KEYUP: case WM_KEYDOWN: - GDK_NOTE (EVENTS, - g_print (" %s ch:%.02x %s", - _gdk_win32_key_to_string (msg->lParam), - (int) msg->wParam, - decode_key_lparam (msg->lParam))); - - keyup_or_down: - { - GdkWin32Keymap *win32_keymap; - GdkModifierType state; - guint keyval; - guint16 keycode; - guint8 group; - gboolean is_modifier; - GdkTranslatedKey translated; - GdkTranslatedKey no_lock; - BYTE key_state[256]; - 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 || win32_display->event_record->in_ime_composition) - break; - - /* Ignore autorepeats on modifiers */ - if (msg->message == WM_KEYDOWN && - (msg->wParam == VK_MENU || - msg->wParam == VK_CONTROL || - msg->wParam == VK_SHIFT) && - ((HIWORD(msg->lParam) & KF_REPEAT) >= 1)) - break; - - if (GDK_SURFACE_DESTROYED (surface)) - break; - - win32_keymap = GDK_WIN32_KEYMAP (gdk_display_get_keymap (display)); - impl = GDK_WIN32_SURFACE (surface); - - API_CALL (GetKeyboardState, (key_state)); - - 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) - { - case VK_CONTROL: - keycode = VK_RCONTROL; - break; - case VK_SHIFT: /* Actually, KF_EXTENDED is not set - * for the right shift key. - */ - keycode = VK_RSHIFT; - break; - case VK_MENU: - keycode = VK_RMENU; - break; - } - } - else if (msg->wParam == VK_SHIFT && - LOBYTE (HIWORD (msg->lParam)) == _gdk_win32_keymap_get_rshift_scancode (win32_keymap)) - keycode = VK_RSHIFT; - - is_modifier = (msg->wParam == VK_CONTROL || - msg->wParam == VK_SHIFT || - msg->wParam == VK_MENU); - - state = build_key_event_state (display, key_state); - group = get_active_group (display); - - 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; - - /* 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 - keys are recorded and sent together when the release event occurs. - Other modifiers (e.g. ctrl, alt) don't have this problem. */ - if (msg->message == WM_KEYDOWN && msg->wParam == VK_SHIFT) - { - int pressed_shift = msg->lParam & 0xffffff; /* mask shift modifier */ - if (win32_display->event_record->both_shift_pressed[0] == 0) - win32_display->event_record->both_shift_pressed[0] = pressed_shift; - else if (win32_display->event_record->both_shift_pressed[0] != pressed_shift) - win32_display->event_record->both_shift_pressed[1] = pressed_shift; - } - - if (msg->message == WM_KEYUP && msg->wParam == VK_SHIFT) - { - if (win32_display->event_record->both_shift_pressed[0] != 0 && - win32_display->event_record->both_shift_pressed[1] != 0) - { - int tmp_retval; - MSG fake_release = *msg; - int pressed_shift = msg->lParam & 0xffffff; - - if (win32_display->event_record->both_shift_pressed[0] == pressed_shift) - fake_release.lParam = win32_display->event_record->both_shift_pressed[1]; - else - fake_release.lParam = win32_display->event_record->both_shift_pressed[0]; - - win32_display->event_record->both_shift_pressed[0] = win32_display->event_record->both_shift_pressed[1] = 0; - gdk_event_translate (&fake_release, &tmp_retval); - } - win32_display->event_record->both_shift_pressed[0] = win32_display->event_record->both_shift_pressed[1] = 0; - } - - /* Reset ALT_MASK if it is the Alt key itself */ - if (msg->wParam == VK_MENU) - state &= ~GDK_ALT_MASK; - - event = gdk_key_event_new ((msg->message == WM_KEYDOWN || msg->message == WM_SYSKEYDOWN) - ? GDK_KEY_PRESS - : GDK_KEY_RELEASE, - surface, - win32_display->device_manager->core_keyboard, - _gdk_win32_get_next_tick (msg->time), - keycode, - state, - is_modifier, - &translated, - &no_lock, - composed); - - _gdk_win32_append_event (event); - - g_free (composed); - return_val = TRUE; - } - break; - case WM_SYSCHAR: - if (msg->wParam != VK_SPACE) - { - /* To prevent beeps, don't let DefWindowProcW() be called */ - return_val = TRUE; - goto done; - } - break; - case WM_IME_STARTCOMPOSITION: - win32_display->event_record->in_ime_composition = TRUE; - break; - case WM_IME_ENDCOMPOSITION: - win32_display->event_record->in_ime_composition = FALSE; - break; - case WM_IME_COMPOSITION: - { - BYTE key_state[256]; - wchar_t wbuf[100]; - int ccount = 0; - - /* On Win2k WM_IME_CHAR doesn't work correctly for non-Unicode - * applications. Thus, handle WM_IME_COMPOSITION with - * GCS_RESULTSTR instead, fetch the Unicode chars from the IME - * with ImmGetCompositionStringW(). - * - * See for instance - * http://groups.google.com/groups?selm=natX5.57%24g77.19788%40nntp2.onemain.com - * and - * http://groups.google.com/groups?selm=u2XfrXw5BHA.1628%40tkmsftngp02 - * for comments by other people that seems to have the same - * experience. WM_IME_CHAR just gives question marks, apparently - * because of going through some conversion to the current code - * page. - * - * WM_IME_CHAR might work on NT4 or Win9x with ActiveIMM, but - * use WM_IME_COMPOSITION there, too, to simplify the code. - */ - GDK_NOTE (EVENTS, g_print (" %#lx", (long) msg->lParam)); - - if (!(msg->lParam & GCS_RESULTSTR)) - break; - - if (GDK_SURFACE_DESTROYED (surface)) - break; - - himc = ImmGetContext (msg->hwnd); - ccount = ImmGetCompositionStringW (himc, GCS_RESULTSTR, - wbuf, sizeof (wbuf)); - ImmReleaseContext (msg->hwnd, himc); - - ccount /= 2; - - API_CALL (GetKeyboardState, (key_state)); - - for (i = 0; i < ccount; i++) - { - GdkTranslatedKey translated; - - /* Build a key press event */ - translated.keyval = gdk_unicode_to_keyval (wbuf[i]); - translated.consumed = 0; - translated.layout = get_active_group (display); - translated.level = 0; - event = gdk_key_event_new (GDK_KEY_PRESS, - surface, - win32_display->device_manager->core_keyboard, - _gdk_win32_get_next_tick (msg->time), - 0, - build_key_event_state (display, key_state), - FALSE, - &translated, - &translated, - NULL); - - _gdk_win32_append_event (event); - - /* Build a key release event. */ - event = gdk_key_event_new (GDK_KEY_RELEASE, - surface, - win32_display->device_manager->core_keyboard, - _gdk_win32_get_next_tick (msg->time), - 0, - build_key_event_state (display, key_state), - FALSE, - &translated, - &translated, - NULL); - - _gdk_win32_append_event (event); - } - - return_val = TRUE; - } + return_val = handle_keyboard_event (display, surface, msg, ret_valp, &goto_done); + if (goto_done) + goto done; break; case WM_LBUTTONDOWN: