installed pty
[VSoRC/.git] / node_modules / node-pty / deps / winpty / misc / winbug-15048.cc
1 /*
2
3 Test program demonstrating a problem in Windows 15048's ReadConsoleOutput API.
4
5 To compile:
6
7     cl /nologo /EHsc winbug-15048.cc shell32.lib
8
9 Example of regressed input:
10
11 Case 1:
12
13     > chcp 932
14     > winbug-15048 -face-gothic 3044
15
16     Correct output:
17
18         1**34      (nb: U+3044 replaced with '**' to avoid MSVC encoding warning)
19         5678
20
21         ReadConsoleOutputW (both rows, 3 cols)
22         row 0: U+0031(0007) U+3044(0107) U+3044(0207) U+0033(0007)
23         row 1: U+0035(0007) U+0036(0007) U+0037(0007) U+0038(0007)
24
25         ReadConsoleOutputW (both rows, 4 cols)
26         row 0: U+0031(0007) U+3044(0107) U+3044(0207) U+0033(0007) U+0034(0007)
27         row 1: U+0035(0007) U+0036(0007) U+0037(0007) U+0038(0007) U+0020(0007)
28
29         ReadConsoleOutputW (second row)
30         row 1: U+0035(0007) U+0036(0007) U+0037(0007) U+0038(0007) U+0020(0007)
31
32         ...
33
34     Win10 15048 bad output:
35
36         1**34
37         5678
38
39         ReadConsoleOutputW (both rows, 3 cols)
40         row 0: U+0031(0007) U+3044(0007) U+0033(0007) U+0035(0007)
41         row 1: U+0036(0007) U+0037(0007) U+0038(0007) U+0000(0000)
42
43         ReadConsoleOutputW (both rows, 4 cols)
44         row 0: U+0031(0007) U+3044(0007) U+0033(0007) U+0034(0007) U+0035(0007)
45         row 1: U+0036(0007) U+0037(0007) U+0038(0007) U+0020(0007) U+0000(0000)
46
47         ReadConsoleOutputW (second row)
48         row 1: U+0035(0007) U+0036(0007) U+0037(0007) U+0038(0007) U+0020(0007)
49
50         ...
51
52     The U+3044 character (HIRAGANA LETTER I) occupies two columns, but it only
53     fills one record in the ReadConsoleOutput output buffer, which has the
54     effect of shifting the first cell of the second row into the last cell of
55     the first row.  Ordinarily, the first and second cells would also have the
56     COMMON_LVB_LEADING_BYTE and COMMON_LVB_TRAILING_BYTE attributes set, which
57     allows winpty to detect the double-column character.
58
59 Case 2:
60
61     > chcp 437
62     > winbug-15048 -face "Lucida Console" -h 4 221A
63
64     The same issue happens with U+221A (SQUARE ROOT), but only in certain
65     fonts.  The console seems to think this character occupies two columns
66     if the font is sufficiently small.  The Windows console properties dialog
67     doesn't allow fonts below 5 pt, but winpty tries to use 2pt and 4pt Lucida
68     Console to allow very large console windows.
69
70 Case 3:
71
72     > chcp 437
73     > winbug-15048 -face "Lucida Console" -h 12 FF12
74
75     The console selection system thinks U+FF12 (FULLWIDTH DIGIT TWO) occupies
76     two columns, which happens to be correct, but it's displayed as a single
77     column unrecognized character.  It otherwise behaves the same as the other
78     cases.
79
80 */
81
82 #include <windows.h>
83 #include <assert.h>
84 #include <string.h>
85 #include <stdlib.h>
86 #include <stdio.h>
87 #include <wchar.h>
88
89 #include <string>
90
91 #define COUNT_OF(array) (sizeof(array) / sizeof((array)[0]))
92
93 // See https://en.wikipedia.org/wiki/List_of_CJK_fonts
94 const wchar_t kMSGothic[] = { 0xff2d, 0xff33, 0x0020, 0x30b4, 0x30b7, 0x30c3, 0x30af, 0 }; // Japanese
95 const wchar_t kNSimSun[] = { 0x65b0, 0x5b8b, 0x4f53, 0 }; // Simplified Chinese
96 const wchar_t kMingLight[] = { 0x7d30, 0x660e, 0x9ad4, 0 }; // Traditional Chinese
97 const wchar_t kGulimChe[] = { 0xad74, 0xb9bc, 0xccb4, 0 }; // Korean
98
99 static void set_font(const wchar_t *name, int size) {
100     const HANDLE conout = GetStdHandle(STD_OUTPUT_HANDLE);
101     CONSOLE_FONT_INFOEX fontex {};
102     fontex.cbSize = sizeof(fontex);
103     fontex.dwFontSize.Y = size;
104     fontex.FontWeight = 400;
105     fontex.FontFamily = 0x36;
106     wcsncpy(fontex.FaceName, name, COUNT_OF(fontex.FaceName));
107     assert(SetCurrentConsoleFontEx(conout, FALSE, &fontex));
108 }
109
110 static void usage(const wchar_t *prog) {
111     printf("Usage: %ls [options]\n", prog);
112     printf("  -h HEIGHT\n");
113     printf("  -face FACENAME\n");
114     printf("  -face-{gothic|simsun|minglight|gulimche) [JP,CN-sim,CN-tra,KR]\n");
115     printf("  hhhh -- print U+hhhh\n");
116     exit(1);
117 }
118
119 static void dump_region(SMALL_RECT region, const char *name) {
120     const HANDLE conout = GetStdHandle(STD_OUTPUT_HANDLE);
121
122     CHAR_INFO buf[1000];
123     memset(buf, 0xcc, sizeof(buf));
124
125     const int w = region.Right - region.Left + 1;
126     const int h = region.Bottom - region.Top + 1;
127
128     assert(ReadConsoleOutputW(
129         conout, buf, { (short)w, (short)h }, { 0, 0 },
130         &region));
131
132     printf("\n");
133     printf("ReadConsoleOutputW (%s)\n", name);
134     for (int y = 0; y < h; ++y) {
135         printf("row %d: ", region.Top + y);
136         for (int i = 0; i < region.Left * 13; ++i) {
137             printf(" ");
138         }
139         for (int x = 0; x < w; ++x) {
140             const int i = y * w + x;
141             printf("U+%04x(%04x) ", buf[i].Char.UnicodeChar, buf[i].Attributes);
142         }
143         printf("\n");
144     }
145 }
146
147 int main() {
148     wchar_t *cmdline = GetCommandLineW();
149     int argc = 0;
150     wchar_t **argv = CommandLineToArgvW(cmdline, &argc);
151     const wchar_t *font_name = L"Lucida Console";
152     int font_height = 8;
153     int test_ch = 0xff12; // U+FF12 FULLWIDTH DIGIT TWO
154
155     for (int i = 1; i < argc; ++i) {
156         const std::wstring arg = argv[i];
157         const std::wstring next = i + 1 < argc ? argv[i + 1] : L"";
158         if (arg == L"-face" && i + 1 < argc) {
159             font_name = argv[i + 1];
160             i++;
161         } else if (arg == L"-face-gothic") {
162             font_name = kMSGothic;
163         } else if (arg == L"-face-simsun") {
164             font_name = kNSimSun;
165         } else if (arg == L"-face-minglight") {
166             font_name = kMingLight;
167         } else if (arg == L"-face-gulimche") {
168             font_name = kGulimChe;
169         } else if (arg == L"-h" && i + 1 < argc) {
170             font_height = _wtoi(next.c_str());
171             i++;
172         } else if (arg.c_str()[0] != '-') {
173             test_ch = wcstol(arg.c_str(), NULL, 16);
174         } else {
175             printf("Unrecognized argument: %ls\n", arg.c_str());
176             usage(argv[0]);
177         }
178     }
179
180     const HANDLE conout = GetStdHandle(STD_OUTPUT_HANDLE);
181
182     set_font(font_name, font_height);
183
184     system("cls");
185     DWORD actual = 0;
186     wchar_t output[] = L"1234\n5678\n";
187     output[1] = test_ch;
188     WriteConsoleW(conout, output, 10, &actual, nullptr);
189
190     dump_region({ 0, 0, 3, 1 }, "both rows, 3 cols");
191     dump_region({ 0, 0, 4, 1 }, "both rows, 4 cols");
192     dump_region({ 0, 1, 4, 1 }, "second row");
193     dump_region({ 0, 0, 4, 0 }, "first row");
194     dump_region({ 1, 0, 4, 0 }, "first row, skip 1");
195     dump_region({ 2, 0, 4, 0 }, "first row, skip 2");
196     dump_region({ 3, 0, 4, 0 }, "first row, skip 3");
197
198     set_font(font_name, 14);
199
200     return 0;
201 }