Mercurial > vim
comparison src/gui_w32.c @ 28566:893251eed714 v8.2.4807
patch 8.2.4807: processing key eveints in Win32 GUI is not ideal
Commit: https://github.com/vim/vim/commit/77fc0b02e53bdc49213900e0055cb6d678ce180d
Author: LemonBoy <thatlemon@gmail.com>
Date: Fri Apr 22 22:45:52 2022 +0100
patch 8.2.4807: processing key eveints in Win32 GUI is not ideal
Problem: Processing key eveints in Win32 GUI is not ideal.
Solution: Improve processing of key events. (closes https://github.com/vim/vim/issues/10155)
author | Bram Moolenaar <Bram@vim.org> |
---|---|
date | Sat, 23 Apr 2022 00:00:04 +0200 |
parents | 16bd027b039e |
children | 7c96d7924ea5 |
comparison
equal
deleted
inserted
replaced
28565:7d76c24fbb4d | 28566:893251eed714 |
---|---|
822 * Key hit, add it to the input buffer. | 822 * Key hit, add it to the input buffer. |
823 */ | 823 */ |
824 static void | 824 static void |
825 _OnChar( | 825 _OnChar( |
826 HWND hwnd UNUSED, | 826 HWND hwnd UNUSED, |
827 UINT ch, | 827 UINT cch, |
828 int cRepeat UNUSED) | 828 int cRepeat UNUSED) |
829 { | 829 { |
830 char_u string[40]; | 830 char_u string[40]; |
831 int len = 0; | 831 int len = 0; |
832 int modifiers = 0; | |
833 int ch = cch; // special keys are negative | |
832 | 834 |
833 dead_key = 0; | 835 dead_key = 0; |
836 | |
837 if (GetKeyState(VK_SHIFT) & 0x8000) | |
838 modifiers |= MOD_MASK_SHIFT; | |
839 if (GetKeyState(VK_CONTROL) & 0x8000) | |
840 modifiers |= MOD_MASK_CTRL; | |
841 | |
842 ch = simplify_key(ch, &modifiers); | |
843 // remove the SHIFT modifier for keys where it's already included, e.g., | |
844 // '(' and '*' | |
845 modifiers = may_remove_shift_modifier(modifiers, ch); | |
846 | |
847 // Unify modifiers somewhat. No longer use ALT to set the 8th bit. | |
848 ch = extract_modifiers(ch, &modifiers, FALSE, NULL); | |
849 if (ch == CSI) | |
850 ch = K_CSI; | |
851 | |
852 if (modifiers) | |
853 { | |
854 string[0] = CSI; | |
855 string[1] = KS_MODIFIER; | |
856 string[2] = modifiers; | |
857 add_to_input_buf(string, 3); | |
858 } | |
834 | 859 |
835 len = char_to_string(ch, string, 40, FALSE); | 860 len = char_to_string(ch, string, 40, FALSE); |
836 if (len == 1 && string[0] == Ctrl_C && ctrl_c_interrupts) | 861 if (len == 1 && string[0] == Ctrl_C && ctrl_c_interrupts) |
837 { | 862 { |
838 trash_input_buf(); | 863 trash_input_buf(); |
855 int len; | 880 int len; |
856 int modifiers; | 881 int modifiers; |
857 int ch = cch; // special keys are negative | 882 int ch = cch; // special keys are negative |
858 | 883 |
859 dead_key = 0; | 884 dead_key = 0; |
860 | |
861 // TRACE("OnSysChar(%d, %c)\n", ch, ch); | |
862 | 885 |
863 // OK, we have a character key (given by ch) which was entered with the | 886 // OK, we have a character key (given by ch) which was entered with the |
864 // ALT key pressed. Eg, if the user presses Alt-A, then ch == 'A'. Note | 887 // ALT key pressed. Eg, if the user presses Alt-A, then ch == 'A'. Note |
865 // that the system distinguishes Alt-a and Alt-A (Alt-Shift-a unless | 888 // that the system distinguishes Alt-a and Alt-A (Alt-Shift-a unless |
866 // CAPSLOCK is pressed) at this point. | 889 // CAPSLOCK is pressed) at this point. |
1814 // re-generate the current character free of the dead char influence | 1837 // re-generate the current character free of the dead char influence |
1815 PostMessage(originalMsg.hwnd, originalMsg.message, originalMsg.wParam, | 1838 PostMessage(originalMsg.hwnd, originalMsg.message, originalMsg.wParam, |
1816 originalMsg.lParam); | 1839 originalMsg.lParam); |
1817 } | 1840 } |
1818 | 1841 |
1819 | |
1820 /* | 1842 /* |
1821 * Process a single Windows message. | 1843 * Process a single Windows message. |
1822 * If one is not available we hang until one is. | 1844 * If one is not available we hang until one is. |
1823 */ | 1845 */ |
1824 static void | 1846 static void |
1831 int modifiers = 0; | 1853 int modifiers = 0; |
1832 int key; | 1854 int key; |
1833 #ifdef FEAT_MENU | 1855 #ifdef FEAT_MENU |
1834 static char_u k10[] = {K_SPECIAL, 'k', ';', 0}; | 1856 static char_u k10[] = {K_SPECIAL, 'k', ';', 0}; |
1835 #endif | 1857 #endif |
1858 BYTE keyboard_state[256]; | |
1836 | 1859 |
1837 GetMessageW(&msg, NULL, 0, 0); | 1860 GetMessageW(&msg, NULL, 0, 0); |
1838 | 1861 |
1839 #ifdef FEAT_OLE | 1862 #ifdef FEAT_OLE |
1840 // Look after OLE Automation commands | 1863 // Look after OLE Automation commands |
1892 /* | 1915 /* |
1893 * If a dead key was pressed and the user presses VK_SPACE, | 1916 * If a dead key was pressed and the user presses VK_SPACE, |
1894 * VK_BACK, or VK_ESCAPE it means that he actually wants to deal | 1917 * VK_BACK, or VK_ESCAPE it means that he actually wants to deal |
1895 * with the dead char now, so do nothing special and let Windows | 1918 * with the dead char now, so do nothing special and let Windows |
1896 * handle it. | 1919 * handle it. |
1897 * | |
1898 * Note that VK_SPACE combines with the dead_key's character and | |
1899 * only one WM_CHAR will be generated by TranslateMessage(), in | |
1900 * the two other cases two WM_CHAR will be generated: the dead | |
1901 * char and VK_BACK or VK_ESCAPE. That is most likely what the | |
1902 * user expects. | |
1903 */ | 1920 */ |
1904 if ((vk == VK_SPACE || vk == VK_BACK || vk == VK_ESCAPE)) | 1921 if ((vk == VK_SPACE || vk == VK_BACK || vk == VK_ESCAPE)) |
1905 { | 1922 { |
1906 dead_key = 0; | 1923 dead_key = 0; |
1907 TranslateMessage(&msg); | |
1908 return; | |
1909 } | 1924 } |
1910 // In modes where we are not typing, dead keys should behave | 1925 // In modes where we are not typing, dead keys should behave |
1911 // normally | 1926 // normally |
1912 else if (!(get_real_state() & (INSERT | CMDLINE | SELECTMODE))) | 1927 else if (!(get_real_state() & (INSERT | CMDLINE | SELECTMODE))) |
1913 { | 1928 { |
1922 trash_input_buf(); | 1937 trash_input_buf(); |
1923 got_int = TRUE; | 1938 got_int = TRUE; |
1924 ctrl_break_was_pressed = TRUE; | 1939 ctrl_break_was_pressed = TRUE; |
1925 string[0] = Ctrl_C; | 1940 string[0] = Ctrl_C; |
1926 add_to_input_buf(string, 1); | 1941 add_to_input_buf(string, 1); |
1942 } | |
1943 | |
1944 // This is an IME event or a synthetic keystroke, let Windows handle it. | |
1945 if (vk == VK_PROCESSKEY || vk == VK_PACKET) | |
1946 { | |
1947 TranslateMessage(&msg); | |
1948 return; | |
1927 } | 1949 } |
1928 | 1950 |
1929 for (i = 0; special_keys[i].key_sym != 0; i++) | 1951 for (i = 0; special_keys[i].key_sym != 0; i++) |
1930 { | 1952 { |
1931 // ignore VK_SPACE when ALT key pressed: system menu | 1953 // ignore VK_SPACE when ALT key pressed: system menu |
2003 add_to_input_buf(string, len); | 2025 add_to_input_buf(string, len); |
2004 } | 2026 } |
2005 break; | 2027 break; |
2006 } | 2028 } |
2007 } | 2029 } |
2030 | |
2031 // Not a special key. | |
2008 if (special_keys[i].key_sym == 0) | 2032 if (special_keys[i].key_sym == 0) |
2009 { | 2033 { |
2010 // Some keys need C-S- where they should only need C-. | 2034 WCHAR ch[8]; |
2011 // Ignore 0xff, Windows XP sends it when NUMLOCK has changed since | 2035 int len; |
2012 // system startup (Helmut Stiegler, 2003 Oct 3). | 2036 int i; |
2013 if (vk != 0xff | 2037 UINT scan_code; |
2014 && (GetKeyState(VK_CONTROL) & 0x8000) | 2038 |
2015 && !(GetKeyState(VK_SHIFT) & 0x8000) | 2039 if (GetKeyState(VK_SHIFT) & 0x8000) |
2016 && !(GetKeyState(VK_MENU) & 0x8000)) | 2040 modifiers |= MOD_MASK_SHIFT; |
2041 if (GetKeyState(VK_CONTROL) & 0x8000) | |
2042 modifiers |= MOD_MASK_CTRL; | |
2043 if (GetKeyState(VK_LMENU) & 0x8000) | |
2044 modifiers |= MOD_MASK_ALT; | |
2045 | |
2046 // Construct the state table with only a few modifiers, we don't | |
2047 // really care about the presence of Ctrl/Alt as those modifiers are | |
2048 // handled by Vim separately. | |
2049 memset(keyboard_state, 0, 256); | |
2050 if (GetKeyState(VK_SHIFT) & 0x8000) | |
2051 keyboard_state[VK_SHIFT] = 0x80; | |
2052 if (GetKeyState(VK_RMENU) & 0x8000) | |
2017 { | 2053 { |
2018 // CTRL-6 is '^'; Japanese keyboard maps '^' to vk == 0xDE | 2054 keyboard_state[VK_MENU] = 0x80; |
2019 if (vk == '6' || MapVirtualKey(vk, 2) == (UINT)'^') | 2055 keyboard_state[VK_CONTROL] = 0x80; |
2020 { | 2056 } |
2021 string[0] = Ctrl_HAT; | 2057 |
2022 add_to_input_buf(string, 1); | 2058 // Translate the virtual key according to the current keyboard |
2023 } | 2059 // layout. |
2024 // vk == 0xBD AZERTY for CTRL-'-', but CTRL-[ for * QWERTY! | 2060 scan_code = MapVirtualKey(vk, MAPVK_VK_TO_VSC); |
2025 else if (vk == 0xBD) // QWERTY for CTRL-'-' | 2061 // Convert the scan-code into a sequence of zero or more unicode |
2026 { | 2062 // codepoints. |
2027 string[0] = Ctrl__; | 2063 // If this is a dead key ToUnicode returns a negative value. |
2028 add_to_input_buf(string, 1); | 2064 len = ToUnicode(vk, scan_code, keyboard_state, ch, ARRAY_LENGTH(ch), |
2029 } | 2065 0); |
2030 // CTRL-2 is '@'; Japanese keyboard maps '@' to vk == 0xC0 | 2066 dead_key = len < 0; |
2031 else if (vk == '2' || MapVirtualKey(vk, 2) == (UINT)'@') | 2067 |
2032 { | 2068 if (len <= 0) |
2033 string[0] = Ctrl_AT; | 2069 return; |
2034 add_to_input_buf(string, 1); | 2070 |
2035 } | 2071 // Post the message as TranslateMessage would do. |
2036 else | 2072 if (msg.message == WM_KEYDOWN) |
2037 TranslateMessage(&msg); | 2073 { |
2074 for (i = 0; i < len; i++) | |
2075 PostMessageW(msg.hwnd, WM_CHAR, ch[i], msg.lParam); | |
2038 } | 2076 } |
2039 else | 2077 else |
2040 TranslateMessage(&msg); | 2078 { |
2079 for (i = 0; i < len; i++) | |
2080 PostMessageW(msg.hwnd, WM_SYSCHAR, ch[i], msg.lParam); | |
2081 } | |
2041 } | 2082 } |
2042 } | 2083 } |
2043 #ifdef FEAT_MBYTE_IME | 2084 #ifdef FEAT_MBYTE_IME |
2044 else if (msg.message == WM_IME_NOTIFY) | 2085 else if (msg.message == WM_IME_NOTIFY) |
2045 _OnImeNotify(msg.hwnd, (DWORD)msg.wParam, (DWORD)msg.lParam); | 2086 _OnImeNotify(msg.hwnd, (DWORD)msg.wParam, (DWORD)msg.lParam); |
3725 UINT cFiles = DragQueryFile(hDrop, DRAGQVAL, NULL, 0); | 3766 UINT cFiles = DragQueryFile(hDrop, DRAGQVAL, NULL, 0); |
3726 UINT i; | 3767 UINT i; |
3727 char_u **fnames; | 3768 char_u **fnames; |
3728 POINT pt; | 3769 POINT pt; |
3729 int_u modifiers = 0; | 3770 int_u modifiers = 0; |
3730 | |
3731 // TRACE("_OnDropFiles: %d files dropped\n", cFiles); | |
3732 | 3771 |
3733 // Obtain dropped position | 3772 // Obtain dropped position |
3734 DragQueryPoint(hDrop, &pt); | 3773 DragQueryPoint(hDrop, &pt); |
3735 MapWindowPoints(s_hwnd, s_textArea, &pt, 1); | 3774 MapWindowPoints(s_hwnd, s_textArea, &pt, 1); |
3736 | 3775 |
4578 HWND hwnd, | 4617 HWND hwnd, |
4579 UINT uMsg, | 4618 UINT uMsg, |
4580 WPARAM wParam, | 4619 WPARAM wParam, |
4581 LPARAM lParam) | 4620 LPARAM lParam) |
4582 { | 4621 { |
4583 /* | 4622 // TRACE("WndProc: hwnd = %08x, msg = %x, wParam = %x, lParam = %x\n", |
4584 TRACE("WndProc: hwnd = %08x, msg = %x, wParam = %x, lParam = %x\n", | 4623 // hwnd, uMsg, wParam, lParam); |
4585 hwnd, uMsg, wParam, lParam); | |
4586 */ | |
4587 | 4624 |
4588 HandleMouseHide(uMsg, lParam); | 4625 HandleMouseHide(uMsg, lParam); |
4589 | 4626 |
4590 s_uMsg = uMsg; | 4627 s_uMsg = uMsg; |
4591 s_wParam = wParam; | 4628 s_wParam = wParam; |
4632 { | 4669 { |
4633 _OnEndSession(); | 4670 _OnEndSession(); |
4634 return 0L; | 4671 return 0L; |
4635 } | 4672 } |
4636 break; | 4673 break; |
4637 | |
4638 case WM_KEYUP: | |
4639 // handle CTRL-/ | |
4640 if ((GetKeyState(VK_CONTROL) & 0x8000) != 0 && wParam == 0xBF) | |
4641 { | |
4642 char_u string[4]; | |
4643 | |
4644 string[0] = CSI; | |
4645 string[1] = KS_MODIFIER; | |
4646 string[2] = MOD_MASK_CTRL; | |
4647 string[3] = 0x2F; | |
4648 add_to_input_buf(string, 4); | |
4649 } | |
4650 return 0L; | |
4651 | 4674 |
4652 case WM_CHAR: | 4675 case WM_CHAR: |
4653 // Don't use HANDLE_MSG() for WM_CHAR, it truncates wParam to a single | 4676 // Don't use HANDLE_MSG() for WM_CHAR, it truncates wParam to a single |
4654 // byte while we want the UTF-16 character value. | 4677 // byte while we want the UTF-16 character value. |
4655 _OnChar(hwnd, (UINT)wParam, (int)(short)LOWORD(lParam)); | 4678 _OnChar(hwnd, (UINT)wParam, (int)(short)LOWORD(lParam)); |