Mercurial > vim
comparison src/os_win32.c @ 7080:1a34f5272977 v7.4.852
commit https://github.com/vim/vim/commit/ac360bf2ca293735fc7c6654dc2b3066f4c62488
Author: Bram Moolenaar <Bram@vim.org>
Date: Tue Sep 1 20:31:20 2015 +0200
patch 7.4.852
Problem: On MS-Windows console Vim uses ANSI APIs for keyboard input and
console output, it cannot input/output Unicode characters.
Solution: Use Unicode APIs for console I/O. (Ken Takata, Yasuhiro Matsumoto)
author | Christian Brabandt <cb@256bit.org> |
---|---|
date | Tue, 01 Sep 2015 20:45:04 +0200 |
parents | 383d6f39669b |
children | aaf96b1aa605 |
comparison
equal
deleted
inserted
replaced
7079:1d7d1607be8a | 7080:1a34f5272977 |
---|---|
211 static void textbackground(WORD wAttr); | 211 static void textbackground(WORD wAttr); |
212 static void standout(void); | 212 static void standout(void); |
213 static void standend(void); | 213 static void standend(void); |
214 static void visual_bell(void); | 214 static void visual_bell(void); |
215 static void cursor_visible(BOOL fVisible); | 215 static void cursor_visible(BOOL fVisible); |
216 static BOOL write_chars(LPCSTR pchBuf, DWORD cchToWrite); | 216 static DWORD write_chars(char_u *pchBuf, DWORD cbToWrite); |
217 static char_u tgetch(int *pmodifiers, char_u *pch2); | 217 static WCHAR tgetch(int *pmodifiers, WCHAR *pch2); |
218 static void create_conin(void); | 218 static void create_conin(void); |
219 static int s_cursor_visible = TRUE; | 219 static int s_cursor_visible = TRUE; |
220 static int did_create_conin = FALSE; | 220 static int did_create_conin = FALSE; |
221 #else | 221 #else |
222 static int s_dont_use_vimrun = TRUE; | 222 static int s_dont_use_vimrun = TRUE; |
263 return (s_dwMax > 0) ? TRUE : FALSE; | 263 return (s_dwMax > 0) ? TRUE : FALSE; |
264 | 264 |
265 if (!win8_or_later) | 265 if (!win8_or_later) |
266 { | 266 { |
267 if (nLength == -1) | 267 if (nLength == -1) |
268 return PeekConsoleInput(hInput, lpBuffer, 1, lpEvents); | 268 return PeekConsoleInputW(hInput, lpBuffer, 1, lpEvents); |
269 return ReadConsoleInput(hInput, lpBuffer, 1, &dwEvents); | 269 return ReadConsoleInputW(hInput, lpBuffer, 1, &dwEvents); |
270 } | 270 } |
271 | 271 |
272 if (s_dwMax == 0) | 272 if (s_dwMax == 0) |
273 { | 273 { |
274 if (nLength == -1) | 274 if (nLength == -1) |
275 return PeekConsoleInput(hInput, lpBuffer, 1, lpEvents); | 275 return PeekConsoleInputW(hInput, lpBuffer, 1, lpEvents); |
276 if (!ReadConsoleInput(hInput, s_irCache, IRSIZE, &dwEvents)) | 276 if (!ReadConsoleInputW(hInput, s_irCache, IRSIZE, &dwEvents)) |
277 return FALSE; | 277 return FALSE; |
278 s_dwIndex = 0; | 278 s_dwIndex = 0; |
279 s_dwMax = dwEvents; | 279 s_dwMax = dwEvents; |
280 if (dwEvents == 0) | 280 if (dwEvents == 0) |
281 { | 281 { |
866 # pragma warning(disable: 4748) | 866 # pragma warning(disable: 4748) |
867 # pragma optimize("", off) | 867 # pragma optimize("", off) |
868 #endif | 868 #endif |
869 | 869 |
870 #if defined(__GNUC__) && !defined(__MINGW32__) && !defined(__CYGWIN__) | 870 #if defined(__GNUC__) && !defined(__MINGW32__) && !defined(__CYGWIN__) |
871 # define AChar AsciiChar | 871 # define UChar UnicodeChar |
872 #else | 872 #else |
873 # define AChar uChar.AsciiChar | 873 # define UChar uChar.UnicodeChar |
874 #endif | 874 #endif |
875 | 875 |
876 /* The return code indicates key code size. */ | 876 /* The return code indicates key code size. */ |
877 static int | 877 static int |
878 #ifdef __BORLANDC__ | 878 #ifdef __BORLANDC__ |
887 static BYTE abKeystate[256]; | 887 static BYTE abKeystate[256]; |
888 | 888 |
889 | 889 |
890 if (s_iIsDead == 2) | 890 if (s_iIsDead == 2) |
891 { | 891 { |
892 pker->AChar = (CHAR) awAnsiCode[1]; | 892 pker->UChar = (WCHAR) awAnsiCode[1]; |
893 s_iIsDead = 0; | 893 s_iIsDead = 0; |
894 return 1; | 894 return 1; |
895 } | 895 } |
896 | 896 |
897 if (pker->AChar != 0) | 897 if (pker->UChar != 0) |
898 return 1; | 898 return 1; |
899 | 899 |
900 vim_memset(abKeystate, 0, sizeof (abKeystate)); | 900 vim_memset(abKeystate, 0, sizeof (abKeystate)); |
901 | 901 |
902 // Should only be non-NULL on NT 4.0 | 902 // Should only be non-NULL on NT 4.0 |
907 if ((*s_pfnGetConsoleKeyboardLayoutName)(szKLID)) | 907 if ((*s_pfnGetConsoleKeyboardLayoutName)(szKLID)) |
908 (void)LoadKeyboardLayout(szKLID, KLF_ACTIVATE); | 908 (void)LoadKeyboardLayout(szKLID, KLF_ACTIVATE); |
909 } | 909 } |
910 | 910 |
911 /* Clear any pending dead keys */ | 911 /* Clear any pending dead keys */ |
912 ToAscii(VK_SPACE, MapVirtualKey(VK_SPACE, 0), abKeystate, awAnsiCode, 0); | 912 ToUnicode(VK_SPACE, MapVirtualKey(VK_SPACE, 0), abKeystate, awAnsiCode, 2, 0); |
913 | 913 |
914 if (uMods & SHIFT_PRESSED) | 914 if (uMods & SHIFT_PRESSED) |
915 abKeystate[VK_SHIFT] = 0x80; | 915 abKeystate[VK_SHIFT] = 0x80; |
916 if (uMods & CAPSLOCK_ON) | 916 if (uMods & CAPSLOCK_ON) |
917 abKeystate[VK_CAPITAL] = 1; | 917 abKeystate[VK_CAPITAL] = 1; |
920 { | 920 { |
921 abKeystate[VK_CONTROL] = abKeystate[VK_LCONTROL] = | 921 abKeystate[VK_CONTROL] = abKeystate[VK_LCONTROL] = |
922 abKeystate[VK_MENU] = abKeystate[VK_RMENU] = 0x80; | 922 abKeystate[VK_MENU] = abKeystate[VK_RMENU] = 0x80; |
923 } | 923 } |
924 | 924 |
925 s_iIsDead = ToAscii(pker->wVirtualKeyCode, pker->wVirtualScanCode, | 925 s_iIsDead = ToUnicode(pker->wVirtualKeyCode, pker->wVirtualScanCode, |
926 abKeystate, awAnsiCode, 0); | 926 abKeystate, awAnsiCode, 2, 0); |
927 | 927 |
928 if (s_iIsDead > 0) | 928 if (s_iIsDead > 0) |
929 pker->AChar = (CHAR) awAnsiCode[0]; | 929 pker->UChar = (WCHAR) awAnsiCode[0]; |
930 | 930 |
931 return s_iIsDead; | 931 return s_iIsDead; |
932 } | 932 } |
933 | 933 |
934 #ifdef _MSC_VER | 934 #ifdef _MSC_VER |
951 * Decode a KEY_EVENT into one or two keystrokes | 951 * Decode a KEY_EVENT into one or two keystrokes |
952 */ | 952 */ |
953 static BOOL | 953 static BOOL |
954 decode_key_event( | 954 decode_key_event( |
955 KEY_EVENT_RECORD *pker, | 955 KEY_EVENT_RECORD *pker, |
956 char_u *pch, | 956 WCHAR *pch, |
957 char_u *pch2, | 957 WCHAR *pch2, |
958 int *pmodifiers, | 958 int *pmodifiers, |
959 BOOL fDoPost) | 959 BOOL fDoPost) |
960 { | 960 { |
961 int i; | 961 int i; |
962 const int nModifs = pker->dwControlKeyState & (SHIFT | ALT | CTRL); | 962 const int nModifs = pker->dwControlKeyState & (SHIFT | ALT | CTRL); |
980 default: | 980 default: |
981 break; | 981 break; |
982 } | 982 } |
983 | 983 |
984 /* special cases */ | 984 /* special cases */ |
985 if ((nModifs & CTRL) != 0 && (nModifs & ~CTRL) == 0 && pker->AChar == NUL) | 985 if ((nModifs & CTRL) != 0 && (nModifs & ~CTRL) == 0 && pker->UChar == NUL) |
986 { | 986 { |
987 /* Ctrl-6 is Ctrl-^ */ | 987 /* Ctrl-6 is Ctrl-^ */ |
988 if (pker->wVirtualKeyCode == '6') | 988 if (pker->wVirtualKeyCode == '6') |
989 { | 989 { |
990 *pch = Ctrl_HAT; | 990 *pch = Ctrl_HAT; |
1042 | 1042 |
1043 if (i < 0) | 1043 if (i < 0) |
1044 *pch = NUL; | 1044 *pch = NUL; |
1045 else | 1045 else |
1046 { | 1046 { |
1047 *pch = (i > 0) ? pker->AChar : NUL; | 1047 *pch = (i > 0) ? pker->UChar : NUL; |
1048 | 1048 |
1049 if (pmodifiers != NULL) | 1049 if (pmodifiers != NULL) |
1050 { | 1050 { |
1051 /* Pass on the ALT key as a modifier, but only when not combined | 1051 /* Pass on the ALT key as a modifier, but only when not combined |
1052 * with CTRL (which is ALTGR). */ | 1052 * with CTRL (which is ALTGR). */ |
1434 WaitForChar(long msec) | 1434 WaitForChar(long msec) |
1435 { | 1435 { |
1436 DWORD dwNow = 0, dwEndTime = 0; | 1436 DWORD dwNow = 0, dwEndTime = 0; |
1437 INPUT_RECORD ir; | 1437 INPUT_RECORD ir; |
1438 DWORD cRecords; | 1438 DWORD cRecords; |
1439 char_u ch, ch2; | 1439 WCHAR ch, ch2; |
1440 | 1440 |
1441 if (msec > 0) | 1441 if (msec > 0) |
1442 /* Wait until the specified time has elapsed. */ | 1442 /* Wait until the specified time has elapsed. */ |
1443 dwEndTime = GetTickCount() + msec; | 1443 dwEndTime = GetTickCount() + msec; |
1444 else if (msec < 0) | 1444 else if (msec < 0) |
1521 if (ir.EventType == KEY_EVENT && ir.Event.KeyEvent.bKeyDown) | 1521 if (ir.EventType == KEY_EVENT && ir.Event.KeyEvent.bKeyDown) |
1522 { | 1522 { |
1523 #ifdef FEAT_MBYTE_IME | 1523 #ifdef FEAT_MBYTE_IME |
1524 /* Windows IME sends two '\n's with only one 'ENTER'. First: | 1524 /* Windows IME sends two '\n's with only one 'ENTER'. First: |
1525 * wVirtualKeyCode == 13. second: wVirtualKeyCode == 0 */ | 1525 * wVirtualKeyCode == 13. second: wVirtualKeyCode == 0 */ |
1526 if (ir.Event.KeyEvent.uChar.UnicodeChar == 0 | 1526 if (ir.Event.KeyEvent.UChar == 0 |
1527 && ir.Event.KeyEvent.wVirtualKeyCode == 13) | 1527 && ir.Event.KeyEvent.wVirtualKeyCode == 13) |
1528 { | 1528 { |
1529 read_console_input(g_hConIn, &ir, 1, &cRecords); | 1529 read_console_input(g_hConIn, &ir, 1, &cRecords); |
1530 continue; | 1530 continue; |
1531 } | 1531 } |
1584 } | 1584 } |
1585 | 1585 |
1586 /* | 1586 /* |
1587 * Get a keystroke or a mouse event | 1587 * Get a keystroke or a mouse event |
1588 */ | 1588 */ |
1589 static char_u | 1589 static WCHAR |
1590 tgetch(int *pmodifiers, char_u *pch2) | 1590 tgetch(int *pmodifiers, WCHAR *pch2) |
1591 { | 1591 { |
1592 char_u ch; | 1592 WCHAR ch; |
1593 | 1593 |
1594 for (;;) | 1594 for (;;) |
1595 { | 1595 { |
1596 INPUT_RECORD ir; | 1596 INPUT_RECORD ir; |
1597 DWORD cRecords = 0; | 1597 DWORD cRecords = 0; |
1656 int len; | 1656 int len; |
1657 int c; | 1657 int c; |
1658 #define TYPEAHEADLEN 20 | 1658 #define TYPEAHEADLEN 20 |
1659 static char_u typeahead[TYPEAHEADLEN]; /* previously typed bytes. */ | 1659 static char_u typeahead[TYPEAHEADLEN]; /* previously typed bytes. */ |
1660 static int typeaheadlen = 0; | 1660 static int typeaheadlen = 0; |
1661 #ifdef FEAT_MBYTE | |
1662 static char_u *rest = NULL; /* unconverted rest of previous read */ | |
1663 static int restlen = 0; | |
1664 int unconverted; | |
1665 #endif | |
1666 | 1661 |
1667 /* First use any typeahead that was kept because "buf" was too small. */ | 1662 /* First use any typeahead that was kept because "buf" was too small. */ |
1668 if (typeaheadlen > 0) | 1663 if (typeaheadlen > 0) |
1669 goto theend; | 1664 goto theend; |
1670 | 1665 |
1759 g_nMouseClick = -1; | 1754 g_nMouseClick = -1; |
1760 } | 1755 } |
1761 else | 1756 else |
1762 #endif | 1757 #endif |
1763 { | 1758 { |
1764 char_u ch2 = NUL; | 1759 WCHAR ch2 = NUL; |
1765 int modifiers = 0; | 1760 int modifiers = 0; |
1766 | 1761 |
1767 c = tgetch(&modifiers, &ch2); | 1762 c = tgetch(&modifiers, &ch2); |
1768 | |
1769 #ifdef FEAT_MBYTE | |
1770 /* stolen from fill_input_buf() in ui.c */ | |
1771 if (rest != NULL) | |
1772 { | |
1773 /* Use remainder of previous call, starts with an invalid | |
1774 * character that may become valid when reading more. */ | |
1775 if (restlen > TYPEAHEADLEN - typeaheadlen) | |
1776 unconverted = TYPEAHEADLEN - typeaheadlen; | |
1777 else | |
1778 unconverted = restlen; | |
1779 mch_memmove(typeahead + typeaheadlen, rest, unconverted); | |
1780 if (unconverted == restlen) | |
1781 { | |
1782 vim_free(rest); | |
1783 rest = NULL; | |
1784 } | |
1785 else | |
1786 { | |
1787 restlen -= unconverted; | |
1788 mch_memmove(rest, rest + unconverted, restlen); | |
1789 } | |
1790 typeaheadlen += unconverted; | |
1791 } | |
1792 else | |
1793 unconverted = 0; | |
1794 #endif | |
1795 | 1763 |
1796 if (typebuf_changed(tb_change_cnt)) | 1764 if (typebuf_changed(tb_change_cnt)) |
1797 { | 1765 { |
1798 /* "buf" may be invalid now if a client put something in the | 1766 /* "buf" may be invalid now if a client put something in the |
1799 * typeahead buffer and "buf" is in the typeahead buffer. */ | 1767 * typeahead buffer and "buf" is in the typeahead buffer. */ |
1814 #endif | 1782 #endif |
1815 { | 1783 { |
1816 int n = 1; | 1784 int n = 1; |
1817 int conv = FALSE; | 1785 int conv = FALSE; |
1818 | 1786 |
1819 typeahead[typeaheadlen] = c; | 1787 #ifdef FEAT_MBYTE |
1788 if (ch2 == NUL) | |
1789 { | |
1790 int i; | |
1791 char_u *p; | |
1792 WCHAR ch[2]; | |
1793 | |
1794 ch[0] = c; | |
1795 if (c >= 0xD800 && c <= 0xDBFF) /* High surrogate */ | |
1796 { | |
1797 ch[1] = tgetch(&modifiers, &ch2); | |
1798 n++; | |
1799 } | |
1800 p = utf16_to_enc(ch, &n); | |
1801 if (p != NULL) | |
1802 { | |
1803 for (i = 0; i < n; i++) | |
1804 typeahead[typeaheadlen + i] = p[i]; | |
1805 vim_free(p); | |
1806 } | |
1807 } | |
1808 else | |
1809 #endif | |
1810 typeahead[typeaheadlen] = c; | |
1820 if (ch2 != NUL) | 1811 if (ch2 != NUL) |
1821 { | 1812 { |
1822 typeahead[typeaheadlen + 1] = 3; | 1813 typeahead[typeaheadlen + n] = 3; |
1823 typeahead[typeaheadlen + 2] = ch2; | 1814 typeahead[typeaheadlen + n + 1] = (char_u)ch2; |
1824 n += 2; | 1815 n += 2; |
1825 } | 1816 } |
1826 #ifdef FEAT_MBYTE | |
1827 /* Only convert normal characters, not special keys. Need to | |
1828 * convert before applying ALT, otherwise mapping <M-x> breaks | |
1829 * when 'tenc' is set. */ | |
1830 if (input_conv.vc_type != CONV_NONE | |
1831 && (ch2 == NUL || c != K_NUL)) | |
1832 { | |
1833 conv = TRUE; | |
1834 typeaheadlen -= unconverted; | |
1835 n = convert_input_safe(typeahead + typeaheadlen, | |
1836 n + unconverted, TYPEAHEADLEN - typeaheadlen, | |
1837 rest == NULL ? &rest : NULL, &restlen); | |
1838 } | |
1839 #endif | |
1840 | 1817 |
1841 if (conv) | 1818 if (conv) |
1842 { | 1819 { |
1843 char_u *p = typeahead + typeaheadlen; | 1820 char_u *p = typeahead + typeaheadlen; |
1844 | 1821 |
5364 #endif | 5341 #endif |
5365 } | 5342 } |
5366 | 5343 |
5367 | 5344 |
5368 /* | 5345 /* |
5369 * write `cchToWrite' characters in `pchBuf' to the screen | 5346 * write `cbToWrite' bytes in `pchBuf' to the screen |
5370 * Returns the number of characters actually written (at least one). | 5347 * Returns the number of bytes actually written (at least one). |
5371 */ | 5348 */ |
5372 static BOOL | 5349 static DWORD |
5373 write_chars( | 5350 write_chars( |
5374 LPCSTR pchBuf, | 5351 char_u *pchBuf, |
5375 DWORD cchToWrite) | 5352 DWORD cbToWrite) |
5376 { | 5353 { |
5377 COORD coord = g_coord; | 5354 COORD coord = g_coord; |
5378 DWORD written; | 5355 DWORD written; |
5379 | 5356 |
5380 FillConsoleOutputAttribute(g_hConOut, g_attrCurrent, cchToWrite, | 5357 #ifdef FEAT_MBYTE |
5381 coord, &written); | 5358 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage) |
5382 /* When writing fails or didn't write a single character, pretend one | 5359 { |
5383 * character was written, otherwise we get stuck. */ | 5360 static WCHAR *unicodebuf = NULL; |
5384 if (WriteConsoleOutputCharacter(g_hConOut, pchBuf, cchToWrite, | 5361 static int unibuflen = 0; |
5385 coord, &written) == 0 | 5362 int length; |
5386 || written == 0) | 5363 DWORD n, cchwritten, cells; |
5387 written = 1; | 5364 |
5388 | 5365 length = MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)pchBuf, cbToWrite, 0, 0); |
5389 g_coord.X += (SHORT) written; | 5366 if (unicodebuf == NULL || length > unibuflen) |
5367 { | |
5368 vim_free(unicodebuf); | |
5369 unicodebuf = (WCHAR *)lalloc(length * sizeof(WCHAR), FALSE); | |
5370 unibuflen = length; | |
5371 } | |
5372 MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)pchBuf, cbToWrite, | |
5373 unicodebuf, unibuflen); | |
5374 | |
5375 cells = mb_string2cells(pchBuf, cbToWrite); | |
5376 FillConsoleOutputAttribute(g_hConOut, g_attrCurrent, cells, | |
5377 coord, &written); | |
5378 /* When writing fails or didn't write a single character, pretend one | |
5379 * character was written, otherwise we get stuck. */ | |
5380 if (WriteConsoleOutputCharacterW(g_hConOut, unicodebuf, length, | |
5381 coord, &cchwritten) == 0 | |
5382 || cchwritten == 0) | |
5383 cchwritten = 1; | |
5384 | |
5385 if (cchwritten == length) | |
5386 { | |
5387 written = cbToWrite; | |
5388 g_coord.X += (SHORT)cells; | |
5389 } | |
5390 else | |
5391 { | |
5392 char_u *p = pchBuf; | |
5393 for (n = 0; n < cchwritten; n++) | |
5394 mb_cptr_adv(p); | |
5395 written = p - pchBuf; | |
5396 g_coord.X += (SHORT)mb_string2cells(pchBuf, written); | |
5397 } | |
5398 } | |
5399 else | |
5400 #endif | |
5401 { | |
5402 FillConsoleOutputAttribute(g_hConOut, g_attrCurrent, cbToWrite, | |
5403 coord, &written); | |
5404 /* When writing fails or didn't write a single character, pretend one | |
5405 * character was written, otherwise we get stuck. */ | |
5406 if (WriteConsoleOutputCharacter(g_hConOut, (LPCSTR)pchBuf, cbToWrite, | |
5407 coord, &written) == 0 | |
5408 || written == 0) | |
5409 written = 1; | |
5410 | |
5411 g_coord.X += (SHORT) written; | |
5412 } | |
5390 | 5413 |
5391 while (g_coord.X > g_srScrollRegion.Right) | 5414 while (g_coord.X > g_srScrollRegion.Right) |
5392 { | 5415 { |
5393 g_coord.X -= (SHORT) Columns; | 5416 g_coord.X -= (SHORT) Columns; |
5394 if (g_coord.Y < g_srScrollRegion.Bottom) | 5417 if (g_coord.Y < g_srScrollRegion.Bottom) |