Skip to content

Commit ac360bf

Browse files
committed
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)
1 parent 6159424 commit ac360bf

4 files changed

Lines changed: 115 additions & 92 deletions

File tree

runtime/doc/options.txt

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7396,14 +7396,12 @@ A jump table for the options with a short description can be found at |Q_op|.
73967396
the GUI it only applies to the keyboard ( 'encoding' is used for the
73977397
display). Except for the Mac when 'macatsui' is off, then
73987398
'termencoding' should be "macroman".
7399-
In the Win32 console version the default value is the console codepage
7400-
when it differs from the ANSI codepage.
74017399
*E617*
74027400
Note: This does not apply to the GTK+ 2 GUI. After the GUI has been
74037401
successfully initialized, 'termencoding' is forcibly set to "utf-8".
74047402
Any attempts to set a different value will be rejected, and an error
74057403
message is shown.
7406-
For the Win32 GUI 'termencoding' is not used for typed characters,
7404+
For the Win32 GUI and console versions 'termencoding' is not used,
74077405
because the Win32 system always passes Unicode characters.
74087406
When empty, the same encoding is used as for the 'encoding' option.
74097407
This is the normal value.

src/os_win32.c

Lines changed: 110 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -213,8 +213,8 @@ static void standout(void);
213213
static void standend(void);
214214
static void visual_bell(void);
215215
static void cursor_visible(BOOL fVisible);
216-
static BOOL write_chars(LPCSTR pchBuf, DWORD cchToWrite);
217-
static char_u tgetch(int *pmodifiers, char_u *pch2);
216+
static DWORD write_chars(char_u *pchBuf, DWORD cbToWrite);
217+
static WCHAR tgetch(int *pmodifiers, WCHAR *pch2);
218218
static void create_conin(void);
219219
static int s_cursor_visible = TRUE;
220220
static int did_create_conin = FALSE;
@@ -265,15 +265,15 @@ read_console_input(
265265
if (!win8_or_later)
266266
{
267267
if (nLength == -1)
268-
return PeekConsoleInput(hInput, lpBuffer, 1, lpEvents);
269-
return ReadConsoleInput(hInput, lpBuffer, 1, &dwEvents);
268+
return PeekConsoleInputW(hInput, lpBuffer, 1, lpEvents);
269+
return ReadConsoleInputW(hInput, lpBuffer, 1, &dwEvents);
270270
}
271271

272272
if (s_dwMax == 0)
273273
{
274274
if (nLength == -1)
275-
return PeekConsoleInput(hInput, lpBuffer, 1, lpEvents);
276-
if (!ReadConsoleInput(hInput, s_irCache, IRSIZE, &dwEvents))
275+
return PeekConsoleInputW(hInput, lpBuffer, 1, lpEvents);
276+
if (!ReadConsoleInputW(hInput, s_irCache, IRSIZE, &dwEvents))
277277
return FALSE;
278278
s_dwIndex = 0;
279279
s_dwMax = dwEvents;
@@ -868,9 +868,9 @@ static const struct
868868
#endif
869869

870870
#if defined(__GNUC__) && !defined(__MINGW32__) && !defined(__CYGWIN__)
871-
# define AChar AsciiChar
871+
# define UChar UnicodeChar
872872
#else
873-
# define AChar uChar.AsciiChar
873+
# define UChar uChar.UnicodeChar
874874
#endif
875875

876876
/* The return code indicates key code size. */
@@ -889,12 +889,12 @@ win32_kbd_patch_key(
889889

890890
if (s_iIsDead == 2)
891891
{
892-
pker->AChar = (CHAR) awAnsiCode[1];
892+
pker->UChar = (WCHAR) awAnsiCode[1];
893893
s_iIsDead = 0;
894894
return 1;
895895
}
896896

897-
if (pker->AChar != 0)
897+
if (pker->UChar != 0)
898898
return 1;
899899

900900
vim_memset(abKeystate, 0, sizeof (abKeystate));
@@ -909,7 +909,7 @@ win32_kbd_patch_key(
909909
}
910910

911911
/* 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);
913913

914914
if (uMods & SHIFT_PRESSED)
915915
abKeystate[VK_SHIFT] = 0x80;
@@ -922,11 +922,11 @@ win32_kbd_patch_key(
922922
abKeystate[VK_MENU] = abKeystate[VK_RMENU] = 0x80;
923923
}
924924

925-
s_iIsDead = ToAscii(pker->wVirtualKeyCode, pker->wVirtualScanCode,
926-
abKeystate, awAnsiCode, 0);
925+
s_iIsDead = ToUnicode(pker->wVirtualKeyCode, pker->wVirtualScanCode,
926+
abKeystate, awAnsiCode, 2, 0);
927927

928928
if (s_iIsDead > 0)
929-
pker->AChar = (CHAR) awAnsiCode[0];
929+
pker->UChar = (WCHAR) awAnsiCode[0];
930930

931931
return s_iIsDead;
932932
}
@@ -953,8 +953,8 @@ static BOOL g_fJustGotFocus = FALSE;
953953
static BOOL
954954
decode_key_event(
955955
KEY_EVENT_RECORD *pker,
956-
char_u *pch,
957-
char_u *pch2,
956+
WCHAR *pch,
957+
WCHAR *pch2,
958958
int *pmodifiers,
959959
BOOL fDoPost)
960960
{
@@ -982,7 +982,7 @@ decode_key_event(
982982
}
983983

984984
/* special cases */
985-
if ((nModifs & CTRL) != 0 && (nModifs & ~CTRL) == 0 && pker->AChar == NUL)
985+
if ((nModifs & CTRL) != 0 && (nModifs & ~CTRL) == 0 && pker->UChar == NUL)
986986
{
987987
/* Ctrl-6 is Ctrl-^ */
988988
if (pker->wVirtualKeyCode == '6')
@@ -1044,7 +1044,7 @@ decode_key_event(
10441044
*pch = NUL;
10451045
else
10461046
{
1047-
*pch = (i > 0) ? pker->AChar : NUL;
1047+
*pch = (i > 0) ? pker->UChar : NUL;
10481048

10491049
if (pmodifiers != NULL)
10501050
{
@@ -1436,7 +1436,7 @@ WaitForChar(long msec)
14361436
DWORD dwNow = 0, dwEndTime = 0;
14371437
INPUT_RECORD ir;
14381438
DWORD cRecords;
1439-
char_u ch, ch2;
1439+
WCHAR ch, ch2;
14401440

14411441
if (msec > 0)
14421442
/* Wait until the specified time has elapsed. */
@@ -1523,7 +1523,7 @@ WaitForChar(long msec)
15231523
#ifdef FEAT_MBYTE_IME
15241524
/* Windows IME sends two '\n's with only one 'ENTER'. First:
15251525
* wVirtualKeyCode == 13. second: wVirtualKeyCode == 0 */
1526-
if (ir.Event.KeyEvent.uChar.UnicodeChar == 0
1526+
if (ir.Event.KeyEvent.UChar == 0
15271527
&& ir.Event.KeyEvent.wVirtualKeyCode == 13)
15281528
{
15291529
read_console_input(g_hConIn, &ir, 1, &cRecords);
@@ -1586,10 +1586,10 @@ create_conin(void)
15861586
/*
15871587
* Get a keystroke or a mouse event
15881588
*/
1589-
static char_u
1590-
tgetch(int *pmodifiers, char_u *pch2)
1589+
static WCHAR
1590+
tgetch(int *pmodifiers, WCHAR *pch2)
15911591
{
1592-
char_u ch;
1592+
WCHAR ch;
15931593

15941594
for (;;)
15951595
{
@@ -1658,11 +1658,6 @@ mch_inchar(
16581658
#define TYPEAHEADLEN 20
16591659
static char_u typeahead[TYPEAHEADLEN]; /* previously typed bytes. */
16601660
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
16661661

16671662
/* First use any typeahead that was kept because "buf" was too small. */
16681663
if (typeaheadlen > 0)
@@ -1761,38 +1756,11 @@ mch_inchar(
17611756
else
17621757
#endif
17631758
{
1764-
char_u ch2 = NUL;
1759+
WCHAR ch2 = NUL;
17651760
int modifiers = 0;
17661761

17671762
c = tgetch(&modifiers, &ch2);
17681763

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-
17961764
if (typebuf_changed(tb_change_cnt))
17971765
{
17981766
/* "buf" may be invalid now if a client put something in the
@@ -1816,27 +1784,36 @@ mch_inchar(
18161784
int n = 1;
18171785
int conv = FALSE;
18181786

1819-
typeahead[typeaheadlen] = c;
1820-
if (ch2 != NUL)
1821-
{
1822-
typeahead[typeaheadlen + 1] = 3;
1823-
typeahead[typeaheadlen + 2] = ch2;
1824-
n += 2;
1825-
}
18261787
#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))
1788+
if (ch2 == NUL)
18321789
{
1833-
conv = TRUE;
1834-
typeaheadlen -= unconverted;
1835-
n = convert_input_safe(typeahead + typeaheadlen,
1836-
n + unconverted, TYPEAHEADLEN - typeaheadlen,
1837-
rest == NULL ? &rest : NULL, &restlen);
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+
}
18381807
}
1808+
else
18391809
#endif
1810+
typeahead[typeaheadlen] = c;
1811+
if (ch2 != NUL)
1812+
{
1813+
typeahead[typeaheadlen + n] = 3;
1814+
typeahead[typeaheadlen + n + 1] = (char_u)ch2;
1815+
n += 2;
1816+
}
18401817

18411818
if (conv)
18421819
{
@@ -5366,27 +5343,73 @@ cursor_visible(BOOL fVisible)
53665343

53675344

53685345
/*
5369-
* write `cchToWrite' characters in `pchBuf' to the screen
5370-
* Returns the number of characters actually written (at least one).
5346+
* write `cbToWrite' bytes in `pchBuf' to the screen
5347+
* Returns the number of bytes actually written (at least one).
53715348
*/
5372-
static BOOL
5349+
static DWORD
53735350
write_chars(
5374-
LPCSTR pchBuf,
5375-
DWORD cchToWrite)
5351+
char_u *pchBuf,
5352+
DWORD cbToWrite)
53765353
{
53775354
COORD coord = g_coord;
53785355
DWORD written;
53795356

5380-
FillConsoleOutputAttribute(g_hConOut, g_attrCurrent, cchToWrite,
5381-
coord, &written);
5382-
/* When writing fails or didn't write a single character, pretend one
5383-
* character was written, otherwise we get stuck. */
5384-
if (WriteConsoleOutputCharacter(g_hConOut, pchBuf, cchToWrite,
5385-
coord, &written) == 0
5386-
|| written == 0)
5387-
written = 1;
5357+
#ifdef FEAT_MBYTE
5358+
if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
5359+
{
5360+
static WCHAR *unicodebuf = NULL;
5361+
static int unibuflen = 0;
5362+
int length;
5363+
DWORD n, cchwritten, cells;
53885364

5389-
g_coord.X += (SHORT) written;
5365+
length = MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)pchBuf, cbToWrite, 0, 0);
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+
}
53905413

53915414
while (g_coord.X > g_srScrollRegion.Right)
53925415
{

src/ui.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ ui_write(s, len)
4242
/* Don't output anything in silent mode ("ex -s") unless 'verbose' set */
4343
if (!(silent_mode && p_verbose == 0))
4444
{
45-
#ifdef FEAT_MBYTE
45+
#if defined(FEAT_MBYTE) && !defined(WIN3264)
4646
char_u *tofree = NULL;
4747

4848
if (output_conv.vc_type != CONV_NONE)
@@ -56,7 +56,7 @@ ui_write(s, len)
5656

5757
mch_write(s, len);
5858

59-
#ifdef FEAT_MBYTE
59+
#if defined(FEAT_MBYTE) && !defined(WIN3264)
6060
if (output_conv.vc_type != CONV_NONE)
6161
vim_free(tofree);
6262
#endif

src/version.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -741,6 +741,8 @@ static char *(features[]) =
741741

742742
static int included_patches[] =
743743
{ /* Add new patch number below this line */
744+
/**/
745+
852,
744746
/**/
745747
851,
746748
/**/

0 commit comments

Comments
 (0)