1 /*
2  * PROJECT:     ReactOS api tests
3  * LICENSE:     GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4  * PURPOSE:     Tests for i18n console.
5  * COPYRIGHT:   Copyright 2017-2020 Katayama Hirofumi MZ
6  *              Copyright 2020-2022 Hermès Bélusca-Maïto
7  */
8 
9 #include "precomp.h"
10 
11 #define okCURSOR(hCon, c) \
12 do { \
13   CONSOLE_SCREEN_BUFFER_INFO __sbi; \
14   BOOL expect = GetConsoleScreenBufferInfo((hCon), &__sbi) && \
15                 __sbi.dwCursorPosition.X == (c).X && __sbi.dwCursorPosition.Y == (c).Y; \
16   ok(expect, "Expected cursor at (%d,%d), got (%d,%d)\n", \
17      (c).X, (c).Y, __sbi.dwCursorPosition.X, __sbi.dwCursorPosition.Y); \
18 } while (0)
19 
20 #define ATTR    (FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED)
21 
22 static const WCHAR u0414[] = {0x0414, 0}; /* Д */
23 static const WCHAR u9580[] = {0x9580, 0}; /* 門 */
24 static const WCHAR space[] = {L' ', 0};
25 static const WCHAR ideograph_space = (WCHAR)0x3000; /* fullwidth space */
26 static const WCHAR s_str[] = {L'A', 0x9580, 'B', 0};
27 static const LCID lcidJapanese = MAKELCID(MAKELANGID(LANG_JAPANESE, SUBLANG_DEFAULT), SORT_DEFAULT);
28 static const LCID lcidRussian  = MAKELCID(MAKELANGID(LANG_RUSSIAN , SUBLANG_DEFAULT), SORT_DEFAULT);
29 
30 static UINT s_uOEMCP;
31 static BOOL s_bIs8Plus;
32 
IsCJKCodePage(_In_ UINT CodePage)33 static BOOL IsCJKCodePage(_In_ UINT CodePage)
34 {
35     switch (CodePage)
36     {
37     case 932:   // Japanese
38     case 949:   // Korean
39     case 1361:  // Korean (Johab)
40     case 936:   // Chinese PRC
41     case 950:   // Taiwan
42         return TRUE;
43     }
44     return FALSE;
45 }
46 
47 static __inline
MapCJKCPToLangId(_In_ UINT CodePage)48 LANGID MapCJKCPToLangId(_In_ UINT CodePage)
49 {
50     switch (CodePage)
51     {
52     case 932:   // Japanese (Shift-JIS)
53         return MAKELANGID(LANG_JAPANESE, SUBLANG_DEFAULT);
54     case 949:   // Korean (Hangul/Wansung)
55     // case 1361:  // Korean (Johab)
56         return MAKELANGID(LANG_KOREAN, SUBLANG_KOREAN);
57     case 936:   // Chinese PRC (Chinese Simplified)
58         return MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED);
59     case 950:   // Taiwan (Chinese Traditional)
60         return MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL);
61     default:
62         return MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US);
63     }
64 }
65 
ChangeOutputCP_(_In_ const char * file,_In_ int line,_In_ UINT CodePage)66 static BOOL ChangeOutputCP_(
67     _In_ const char* file,
68     _In_ int line,
69     _In_ UINT CodePage)
70 {
71     BOOL bSuccess;
72 
73     /* Validate the code page */
74     bSuccess = IsValidCodePage(CodePage);
75     if (!bSuccess)
76     {
77         skip_(file, line)("Code page %d not available\n", CodePage);
78         return FALSE;
79     }
80 
81     /* Set the new code page */
82     SetLastError(0xdeadbeef);
83     bSuccess = SetConsoleOutputCP(CodePage);
84     if (!bSuccess)
85         skip_(file, line)("SetConsoleOutputCP(%d) failed with last error %lu\n", CodePage, GetLastError());
86     return bSuccess;
87 }
88 
89 #define ChangeOutputCP(CodePage) \
90     ChangeOutputCP_(__FILE__, __LINE__, CodePage)
91 
92 
93 #define cmpThreadLangId(file, line, ExpectedLangId) \
94 do { \
95     LANGID ThreadLangId; \
96     ThreadLangId = LANGIDFROMLCID(NtCurrentTeb()->CurrentLocale); \
97     trace_((file), (line))("Thread LangId %d, expecting %d...\n", \
98                            ThreadLangId, (ExpectedLangId)); \
99     ok_((file), (line))(ThreadLangId == (ExpectedLangId),   \
100                         "Thread LangId %d, expected %d\n",  \
101                         ThreadLangId, (ExpectedLangId));    \
102 } while (0)
103 
104 static BOOL
doTest_CP_ThreadLang_(_In_ const char * file,_In_ int line,_In_ UINT CodePage,_In_ LANGID ExpectedLangId)105 doTest_CP_ThreadLang_(
106     _In_ const char* file,
107     _In_ int line,
108     _In_ UINT CodePage,
109     _In_ LANGID ExpectedLangId)
110 {
111     UINT newcp;
112 
113     /* Verify and set the new code page */
114     if (!ChangeOutputCP_(file, line, CodePage))
115     {
116         skip_(file, line)("Code page %d expected to be valid!\n", CodePage);
117         return FALSE;
118     }
119 
120     newcp = GetConsoleOutputCP();
121     ok_(file, line)(newcp == CodePage, "Console output CP is %d, expected %d\n", newcp, CodePage);
122 
123     /* Verify that the thread lang ID is the expected one */
124     cmpThreadLangId(file, line, ExpectedLangId);
125     return TRUE;
126 }
127 
128 #define doTest_CP_ThreadLang(...) \
129     doTest_CP_ThreadLang_(__FILE__, __LINE__, ##__VA_ARGS__)
130 
test_CP_ThreadLang(VOID)131 static VOID test_CP_ThreadLang(VOID)
132 {
133     /* Save the initial current thread locale. It is (re)initialized after
134      * attaching to a console. Don't use GetThreadLocale() as the latter
135      * can return a replacement value in case CurrentLocale is 0. */
136     LANGID ThreadLangId = LANGIDFROMLCID(NtCurrentTeb()->CurrentLocale);
137     UINT oldcp = GetConsoleOutputCP();
138 
139     if (IsCJKCodePage(s_uOEMCP))
140     {
141         /* We are on a CJK system */
142 
143         /* Is the console in CJK? If so, current thread should be in CJK language */
144         if (!IsCJKCodePage(oldcp))
145         {
146             skip("CJK system but console CP not in CJK\n");
147         }
148         else
149         {
150             /* Check that the thread lang ID matches what the console set */
151             LANGID LangId = MapCJKCPToLangId(oldcp);
152             cmpThreadLangId(__FILE__, __LINE__, LangId);
153         }
154 
155         /* Set the code page to OEM USA (non-CJK codepage that is supported).
156          * Verify that the thread lang ID has changed to non-CJK language. */
157         doTest_CP_ThreadLang(437, MapCJKCPToLangId(437));
158 
159         /* Set the code page to the default system CJK codepage.
160          * Check that the thread lang ID matches what the console set. */
161         doTest_CP_ThreadLang(s_uOEMCP, MapCJKCPToLangId(s_uOEMCP));
162     }
163     else
164     {
165         /* We are on a non-CJK system */
166 
167         /* Code pages: Japanese, Korean, Chinese Simplified/Traditional */
168         UINT CJKCodePages[] = {932, 949, 936, 950};
169         UINT newcp;
170         USHORT i;
171 
172         /* Switch to a different code page (OEM USA) than the current one.
173          * In such setup, the current thread lang ID should not change. */
174         newcp = (s_uOEMCP == 437 ? 850 : 437);
175         doTest_CP_ThreadLang(newcp, ThreadLangId);
176 
177         /* Try switching to a CJK codepage, if possible, but
178          * the thread lang ID should not change either... */
179 
180         /* Retry as long as no valid CJK codepage has been found */
181         for (i = 0; i < ARRAYSIZE(CJKCodePages); ++i)
182         {
183             newcp = CJKCodePages[i];
184             if (IsValidCodePage(newcp))
185                 break; // Found a valid one.
186         }
187         if (i >= ARRAYSIZE(CJKCodePages))
188         {
189             /* No valid CJK code pages on the system */
190             skip("CJK system but console CP not in CJK\n");
191         }
192         else
193         {
194             /* Verify that the thread lang ID remains the same */
195             doTest_CP_ThreadLang(newcp, ThreadLangId);
196         }
197     }
198 
199     /* Restore code page */
200     SetConsoleOutputCP(oldcp);
201 }
202 
203 
204 /* Russian Code Page 855 */
205 // NOTE that CP 866 can also be used
test_cp855(HANDLE hConOut)206 static void test_cp855(HANDLE hConOut)
207 {
208     BOOL ret;
209     UINT oldcp;
210     int n;
211     DWORD len;
212     COORD c;
213     CONSOLE_SCREEN_BUFFER_INFO csbi;
214     int count;
215     WCHAR str[32];
216     WORD attrs[16];
217 
218     /* Set code page */
219     oldcp = GetConsoleOutputCP();
220     if (!ChangeOutputCP(855))
221     {
222         skip("Codepage 855 not available\n");
223         return;
224     }
225 
226     /* Get info */
227     ret = GetConsoleScreenBufferInfo(hConOut, &csbi);
228     ok(ret, "GetConsoleScreenBufferInfo failed\n");
229     trace("csbi.dwSize.X:%d, csbi.dwSize.Y:%d\n", csbi.dwSize.X, csbi.dwSize.Y);
230     count = 200;
231 
232     /* "\u0414" */
233     {
234         /* Output u0414 "count" times at (0,0) */
235         c.X = c.Y = 0;
236         SetConsoleCursorPosition(hConOut, c);
237         okCURSOR(hConOut, c);
238         for (n = 0; n < count; ++n)
239         {
240             ret = WriteConsoleW(hConOut, u0414, lstrlenW(u0414), &len, NULL);
241             ok(ret && len == lstrlenW(u0414), "WriteConsoleW failed\n");
242         }
243 
244         /* Check cursor */
245         len = count;        /* u0414 is normal width in Russian */
246         c.X = (SHORT)(len % csbi.dwSize.X);
247         c.Y = (SHORT)(len / csbi.dwSize.X);
248         okCURSOR(hConOut, c);
249 
250         /* Read characters at (0,0) */
251         c.X = c.Y = 0;
252         ret = ReadConsoleOutputCharacterW(hConOut, str, 3 * sizeof(WCHAR), c, &len);
253         ok(ret, "ReadConsoleOutputCharacterW failed\n");
254         ok_long(len, 6);
255         ok_int(str[0], 0x414);
256         ok_int(str[1], 0x414);
257         ok_int(str[2], 0x414);
258 
259         /* Read attributes at (0,0) */
260         c.X = c.Y = 0;
261         ret = ReadConsoleOutputAttribute(hConOut, attrs, 6, c, &len);
262         ok(ret, "ReadConsoleOutputAttribute failed\n");
263         ok_long(len, 6);
264         ok_int(attrs[0], ATTR);
265 
266         /* Check cursor */
267         c.X = 1;
268         c.Y = 0;
269         ret = SetConsoleCursorPosition(hConOut, c);
270         ok(ret, "SetConsoleCursorPosition failed\n");
271         okCURSOR(hConOut, c);
272 
273         /* Fill by space */
274         c.X = c.Y = 0;
275         FillConsoleOutputCharacterW(hConOut, L' ', csbi.dwSize.X * csbi.dwSize.Y, c, &len);
276 
277         /* Output u0414 "count" times at (1,0) */
278         c.X = 1;
279         c.Y = 0;
280         SetConsoleCursorPosition(hConOut, c);
281         okCURSOR(hConOut, c);
282         for (n = 0; n < count; ++n)
283         {
284             ret = WriteConsoleW(hConOut, u0414, lstrlenW(u0414), &len, NULL);
285             ok(ret && len == lstrlenW(u0414), "WriteConsoleW failed\n");
286         }
287 
288         /* Check cursor */
289         len = 1 + count;
290         c.X = (SHORT)(len % csbi.dwSize.X);
291         c.Y = (SHORT)(len / csbi.dwSize.X);
292         okCURSOR(hConOut, c);
293 
294         /* Read characters at (0,0) */
295         c.X = c.Y = 0;
296         ret = ReadConsoleOutputCharacterW(hConOut, str, 3 * sizeof(WCHAR), c, &len);
297         ok(ret, "ReadConsoleOutputCharacterW failed\n");
298         ok_long(len, 6);
299         ok_int(str[0], L' ');
300         ok_int(str[1], 0x414);
301         ok_int(str[2], 0x414);
302     }
303 
304     /* "\u9580" */
305     {
306         /* Output u9580 "count" times at (0,0) */
307         c.X = c.Y = 0;
308         SetConsoleCursorPosition(hConOut, c);
309         okCURSOR(hConOut, c);
310         for (n = 0; n < count; ++n)
311         {
312             ret = WriteConsoleW(hConOut, u9580, lstrlenW(u9580), &len, NULL);
313             ok(ret && len == lstrlenW(u9580), "WriteConsoleW failed\n");
314         }
315 
316         /* Check cursor */
317         len = count;        /* u9580 is normal width in Russian */
318         c.X = (SHORT)(len % csbi.dwSize.X);
319         c.Y = (SHORT)(len / csbi.dwSize.X);
320         okCURSOR(hConOut, c);
321 
322         /* Check cursor */
323         c.X = 1;
324         c.Y = 0;
325         ret = SetConsoleCursorPosition(hConOut, c);
326         ok(ret, "SetConsoleCursorPosition failed\n");
327         okCURSOR(hConOut, c);
328 
329         /* Fill by space */
330         c.X = c.Y = 0;
331         ret = FillConsoleOutputCharacterW(hConOut, L' ', csbi.dwSize.X * csbi.dwSize.Y, c, &len);
332         ok(ret, "FillConsoleOutputCharacterW failed\n");
333         ok_long(len, csbi.dwSize.X * csbi.dwSize.Y);
334 
335         /* Output u9580 "count" times at (1,0) */
336         c.X = 1;
337         c.Y = 0;
338         SetConsoleCursorPosition(hConOut, c);
339         okCURSOR(hConOut, c);
340         for (n = 0; n < count; ++n)
341         {
342             ret = WriteConsoleW(hConOut, u9580, lstrlenW(u9580), &len, NULL);
343             ok(ret && len == lstrlenW(u9580), "WriteConsoleW failed\n");
344         }
345 
346         /* Check cursor */
347         len = 1 + count;
348         c.X = (SHORT)(len % csbi.dwSize.X);
349         c.Y = (SHORT)(len / csbi.dwSize.X);
350         okCURSOR(hConOut, c);
351 
352         /* Fill by ideograph space */
353         c.X = c.Y = 0;
354         ret = FillConsoleOutputCharacterW(hConOut, ideograph_space, csbi.dwSize.X * csbi.dwSize.Y, c, &len);
355         ok(ret, "FillConsoleOutputCharacterW failed\n");
356         ok_long(len, csbi.dwSize.X * csbi.dwSize.Y);
357 
358         /* Read characters at (0,0) */
359         c.X = c.Y = 0;
360         ret = ReadConsoleOutputCharacterW(hConOut, str, 3 * sizeof(WCHAR), c, &len);
361         ok(ret, "ReadConsoleOutputCharacterW failed\n");
362         ok_long(len, 6);
363         ok(str[0] == ideograph_space || str[0] == L'?', "str[0] was: 0x%04X\n", str[0]);
364         ok(str[1] == ideograph_space || str[1] == L'?', "str[1] was: 0x%04X\n", str[1]);
365         ok(str[2] == ideograph_space || str[2] == L'?', "str[2] was: 0x%04X\n", str[2]);
366 
367         /* Read attributes at (0,0) */
368         c.X = c.Y = 0;
369         ret = ReadConsoleOutputAttribute(hConOut, attrs, 6, c, &len);
370         ok(ret, "ReadConsoleOutputAttribute failed\n");
371         ok_long(len, 6);
372         ok_int(attrs[0], ATTR);
373 
374         /* Read characters at (1,0) */
375         c.X = 1;
376         c.Y = 0;
377         ret = ReadConsoleOutputCharacterW(hConOut, str, 3 * sizeof(WCHAR), c, &len);
378         ok(ret, "ReadConsoleOutputCharacterW failed\n");
379         ok_long(len, 6);
380         ok(str[0] == ideograph_space || str[0] == L'?', "str[0] was: 0x%04X\n", str[0]);
381         ok(str[1] == ideograph_space || str[1] == L'?', "str[1] was: 0x%04X\n", str[1]);
382         ok(str[2] == ideograph_space || str[2] == L'?', "str[2] was: 0x%04X\n", str[2]);
383 
384         /* Output u9580 "count" once at (1,0) */
385         c.X = 1;
386         c.Y = 0;
387         SetConsoleCursorPosition(hConOut, c);
388         okCURSOR(hConOut, c);
389         ret = WriteConsoleW(hConOut, u9580, lstrlenW(u9580), &len, NULL);
390         ok(ret && len == lstrlenW(u9580), "WriteConsoleW failed\n");
391 
392         /* Read attributes at (1,0) */
393         c.X = 1;
394         c.Y = 0;
395         ret = ReadConsoleOutputAttribute(hConOut, attrs, 1, c, &len);
396         ok(ret, "ReadConsoleOutputAttribute failed\n");
397         ok_long(len, 1);
398         ok_int(attrs[0], ATTR);
399 
400         /* Check cursor */
401         c.X = 2;
402         c.Y = 0;
403         okCURSOR(hConOut, c);
404 
405         /* Read characters at (0,0) */
406         c.X = c.Y = 0;
407         ret = ReadConsoleOutputCharacterW(hConOut, str, 3 * sizeof(WCHAR), c, &len);
408         ok(ret, "ReadConsoleOutputCharacterW failed\n");
409         ok_long(len, 6);
410         ok(str[0] == ideograph_space || str[0] == L'?', "str[0] was: 0x%04X\n", str[0]);
411         ok(str[1] == u9580[0] || str[1] == L'?', "str[1] was: 0x%04X\n", str[1]);
412         ok(str[2] == ideograph_space || str[2] == L'?', "str[2] was: 0x%04X\n", str[2]);
413     }
414 
415     /* Restore code page */
416     SetConsoleOutputCP(oldcp);
417 }
418 
419 /* Japanese Code Page 932 */
test_cp932(HANDLE hConOut)420 static void test_cp932(HANDLE hConOut)
421 {
422     BOOL ret;
423     UINT oldcp;
424     int n;
425     DWORD len;
426     COORD c, buffSize;
427     CONSOLE_SCREEN_BUFFER_INFO csbi;
428     int count;
429     WCHAR str[32];
430     WORD attrs[16];
431     CHAR_INFO buff[16];
432     SMALL_RECT sr;
433 
434     /* Set code page */
435     oldcp = GetConsoleOutputCP();
436     if (!ChangeOutputCP(932))
437     {
438         skip("Codepage 932 not available\n");
439         return;
440     }
441 
442     /* Get info */
443     ret = GetConsoleScreenBufferInfo(hConOut, &csbi);
444     ok(ret, "GetConsoleScreenBufferInfo failed\n");
445     trace("csbi.dwSize.X:%d, csbi.dwSize.Y:%d\n", csbi.dwSize.X, csbi.dwSize.Y);
446     count = 200;
447 
448     /* "\u0414" */
449     {
450         /* Output u0414 "count" times at (0,0) */
451         c.X = c.Y = 0;
452         SetConsoleCursorPosition(hConOut, c);
453         okCURSOR(hConOut, c);
454         for (n = 0; n < count; ++n)
455         {
456             ret = WriteConsoleW(hConOut, u0414, lstrlenW(u0414), &len, NULL);
457             ok(ret && len == lstrlenW(u0414), "WriteConsoleW failed\n");
458         }
459 
460         /* Check cursor */
461         GetConsoleScreenBufferInfo(hConOut, &csbi);
462         len = count * 2;     /* u0414 is fullwidth in Japanese */
463         c.X = (SHORT)(len % csbi.dwSize.X);
464         c.Y = (SHORT)(len / csbi.dwSize.X);
465         okCURSOR(hConOut, c);
466 
467         /* Read characters at (0,0) */
468         c.X = c.Y = 0;
469         ret = ReadConsoleOutputCharacterW(hConOut, str, 3 * sizeof(WCHAR), c, &len);
470         ok(ret, "ReadConsoleOutputCharacterW failed\n");
471         ok_long(len, 3);
472         ok_int(str[0], 0x414);
473         ok_int(str[1], 0x414);
474         ok_int(str[2], 0x414);
475 
476         /* Read attributes at (0,0) */
477         c.X = c.Y = 0;
478         ret = ReadConsoleOutputAttribute(hConOut, attrs, 6, c, &len);
479         ok(ret, "ReadConsoleOutputAttribute failed\n");
480         ok_long(len, 6);
481         ok_int(attrs[0], ATTR | COMMON_LVB_LEADING_BYTE);
482         ok_int(attrs[1], ATTR | COMMON_LVB_TRAILING_BYTE);
483 
484         /* Check cursor */
485         c.X = 1;
486         c.Y = 0;
487         ret = SetConsoleCursorPosition(hConOut, c);
488         ok(ret, "SetConsoleCursorPosition failed\n");
489         okCURSOR(hConOut, c);
490 
491         /* Fill by space */
492         c.X = c.Y = 0;
493         FillConsoleOutputCharacterW(hConOut, L' ', csbi.dwSize.X * csbi.dwSize.Y, c, &len);
494 
495         /* Output u0414 "count" times at (1,0) */
496         c.X = 1;
497         c.Y = 0;
498         SetConsoleCursorPosition(hConOut, c);
499         okCURSOR(hConOut, c);
500         for (n = 0; n < count; ++n)
501         {
502             ret = WriteConsoleW(hConOut, u0414, lstrlenW(u0414), &len, NULL);
503             ok(ret && len == lstrlenW(u0414), "WriteConsoleW failed\n");
504         }
505 
506         /* Check cursor */
507         len = csbi.dwSize.X + (count - (csbi.dwSize.X - 1) / 2) * 2;
508         c.X = (SHORT)(len % csbi.dwSize.X);
509         c.Y = (SHORT)(len / csbi.dwSize.X);
510         okCURSOR(hConOut, c);
511 
512         /* Read characters at (0,0) */
513         c.X = 0;
514         c.Y = 0;
515         ret = ReadConsoleOutputCharacterW(hConOut, str, 3 * sizeof(WCHAR), c, &len);
516         ok(ret, "ReadConsoleOutputCharacterW failed\n");
517         ok_long(len, 4);
518         ok_int(str[0], L' ');
519         ok_int(str[1], 0x414);
520         ok_int(str[2], 0x414);
521     }
522 
523     /* "\u9580" */
524     {
525         /* Output u9580 "count" times at (0,0) */
526         c.X = c.Y = 0;
527         SetConsoleCursorPosition(hConOut, c);
528         okCURSOR(hConOut, c);
529         for (n = 0; n < count; ++n)
530         {
531             ret = WriteConsoleW(hConOut, u9580, lstrlenW(u9580), &len, NULL);
532             ok(ret && len == lstrlenW(u9580), "WriteConsoleW failed\n");
533         }
534 
535         /* Check cursor */
536         len = count * 2;     /* u9580 is fullwidth in Japanese */
537         c.X = (SHORT)(len % csbi.dwSize.X);
538         c.Y = (SHORT)(len / csbi.dwSize.X);
539         okCURSOR(hConOut, c);
540 
541         /* Check cursor */
542         c.X = 1;
543         c.Y = 0;
544         ret = SetConsoleCursorPosition(hConOut, c);
545         ok(ret, "SetConsoleCursorPosition failed\n");
546         okCURSOR(hConOut, c);
547 
548         /* Fill by space */
549         c.X = c.Y = 0;
550         ret = FillConsoleOutputCharacterW(hConOut, L' ', csbi.dwSize.X * csbi.dwSize.Y, c, &len);
551         ok(ret, "FillConsoleOutputCharacterW failed\n");
552         ok_long(len, csbi.dwSize.X * csbi.dwSize.Y);
553 
554         /* Output u9580 "count" times at (1,0) */
555         c.X = 1;
556         c.Y = 0;
557         SetConsoleCursorPosition(hConOut, c);
558         okCURSOR(hConOut, c);
559         for (n = 0; n < count; ++n)
560         {
561             ret = WriteConsoleW(hConOut, u9580, lstrlenW(u9580), &len, NULL);
562             ok(ret && len == lstrlenW(u9580), "WriteConsoleW failed\n");
563         }
564 
565         /* Check cursor */
566         len = csbi.dwSize.X + (count - (csbi.dwSize.X - 1) / 2) * 2;
567         c.X = (SHORT)(len % csbi.dwSize.X);
568         c.Y = (SHORT)(len / csbi.dwSize.X);
569         okCURSOR(hConOut, c);
570 
571         /* Fill by ideograph space */
572         c.X = c.Y = 0;
573         ret = FillConsoleOutputCharacterW(hConOut, ideograph_space, csbi.dwSize.X * csbi.dwSize.Y, c, &len);
574         ok(ret, "FillConsoleOutputCharacterW failed\n");
575         if (s_bIs8Plus)
576             ok_long(len, csbi.dwSize.X * csbi.dwSize.Y / 2);
577         else
578             ok_long(len, csbi.dwSize.X * csbi.dwSize.Y);
579 
580         /* Read characters at (0,0) */
581         c.X = c.Y = 0;
582         ret = ReadConsoleOutputCharacterW(hConOut, str, 3 * sizeof(WCHAR), c, &len);
583         ok(ret, "ReadConsoleOutputCharacterW failed\n");
584         ok_long(len, 3);
585         ok_int(str[0], ideograph_space);
586         ok_int(str[1], ideograph_space);
587         ok_int(str[2], ideograph_space);
588 
589         /* Read attributes at (0,0) */
590         c.X = c.Y = 0;
591         ret = ReadConsoleOutputAttribute(hConOut, attrs, 6, c, &len);
592         ok(ret, "ReadConsoleOutputAttribute failed\n");
593         ok_long(len, 6);
594         ok_int(attrs[0], ATTR | COMMON_LVB_LEADING_BYTE);
595         ok_int(attrs[1], ATTR | COMMON_LVB_TRAILING_BYTE);
596         ok_int(attrs[2], ATTR | COMMON_LVB_LEADING_BYTE);
597         ok_int(attrs[3], ATTR | COMMON_LVB_TRAILING_BYTE);
598         ok_int(attrs[4], ATTR | COMMON_LVB_LEADING_BYTE);
599         ok_int(attrs[5], ATTR | COMMON_LVB_TRAILING_BYTE);
600 
601         /* Output u9580 "count" once at (1,0) */
602         c.X = 1;
603         c.Y = 0;
604         SetConsoleCursorPosition(hConOut, c);
605         okCURSOR(hConOut, c);
606         ret = WriteConsoleW(hConOut, u9580, lstrlenW(u9580), &len, NULL);
607         ok(ret && len == lstrlenW(u9580), "WriteConsoleW failed\n");
608 
609         /*
610          * Read attributes at (1,0) -
611          * Note that if only one attribute of a fullwidth character
612          * is retrieved, no leading/trailing byte flag is set!
613          */
614         c.X = 1;
615         c.Y = 0;
616         ret = ReadConsoleOutputAttribute(hConOut, attrs, 1, c, &len);
617         ok(ret, "ReadConsoleOutputAttribute failed\n");
618         ok_long(len, 1);
619         ok_int(attrs[0], ATTR);
620 
621         /* Check that the same problem happens for the trailing byte */
622         c.X = 2;
623         c.Y = 0;
624         ret = ReadConsoleOutputAttribute(hConOut, attrs, 1, c, &len);
625         ok(ret, "ReadConsoleOutputAttribute failed\n");
626         ok_long(len, 1);
627         ok_int(attrs[0], ATTR);
628 
629         /* Read attributes at (1,0) */
630         c.X = 1;
631         c.Y = 0;
632         ret = ReadConsoleOutputAttribute(hConOut, attrs, 2, c, &len);
633         ok(ret, "ReadConsoleOutputAttribute failed\n");
634         ok_long(len, 2);
635         ok_int(attrs[0], ATTR | COMMON_LVB_LEADING_BYTE);
636         ok_int(attrs[1], ATTR | COMMON_LVB_TRAILING_BYTE);
637 
638         /* Read attributes at (1,0) */
639         ret = ReadConsoleOutputAttribute(hConOut, attrs, 3, c, &len);
640         ok(ret, "ReadConsoleOutputAttribute failed\n");
641         ok_long(len, 3);
642         ok_int(attrs[0], ATTR | COMMON_LVB_LEADING_BYTE);
643         ok_int(attrs[1], ATTR | COMMON_LVB_TRAILING_BYTE);
644         if (s_bIs8Plus)
645             ok_int(attrs[2], ATTR | COMMON_LVB_TRAILING_BYTE);
646         else
647             ok_int(attrs[2], ATTR);
648 
649         /* Read attributes at (0,0) */
650         c.X = c.Y = 0;
651         ret = ReadConsoleOutputAttribute(hConOut, attrs, 4, c, &len);
652         ok(ret, "ReadConsoleOutputAttribute failed\n");
653         ok_long(len, 4);
654         if (s_bIs8Plus)
655             ok_int(attrs[0], ATTR | COMMON_LVB_LEADING_BYTE);
656         else
657             ok_int(attrs[0], ATTR);
658         ok_int(attrs[1], ATTR | COMMON_LVB_LEADING_BYTE);
659         ok_int(attrs[2], ATTR | COMMON_LVB_TRAILING_BYTE);
660         if (s_bIs8Plus)
661             ok_int(attrs[3], ATTR | COMMON_LVB_TRAILING_BYTE);
662         else
663             ok_int(attrs[3], ATTR);
664 
665         /* Check cursor */
666         c.X = 3;
667         c.Y = 0;
668         okCURSOR(hConOut, c);
669 
670         /* Read characters */
671         c.X = c.Y = 0;
672         ret = ReadConsoleOutputCharacterW(hConOut, str, 3 * sizeof(WCHAR), c, &len);
673         ok(ret, "ReadConsoleOutputCharacterW failed\n");
674         if (s_bIs8Plus)
675         {
676             ok_long(len, 3);
677             ok_int(str[0], ideograph_space);
678             ok_int(str[1], u9580[0]);
679             ok_int(str[2], ideograph_space);
680         }
681         else
682         {
683             ok_long(len, 4);
684             ok_int(str[0], L' ');
685             ok_int(str[1], u9580[0]);
686             ok_int(str[2], L' ');
687         }
688     }
689 
690     /* COMMON_LVB_LEADING_BYTE and COMMON_LVB_TRAILING_BYTE for u0414 */
691     {
692         /* set cursor */
693         c.X = c.Y = 0;
694         SetConsoleCursorPosition(hConOut, c);
695         okCURSOR(hConOut, c);
696 
697         /* fill by 'A' */
698         ret = FillConsoleOutputCharacterW(hConOut, L'A', csbi.dwSize.X * 2, c, &len);
699         ok_int(ret, 1);
700         ok_long(len, csbi.dwSize.X * 2);
701 
702         /* reset buff */
703         buffSize.X = ARRAYSIZE(buff);
704         buffSize.Y = 1;
705         memset(buff, 0x7F, sizeof(buff));
706 
707         /* read output */
708         c.X = c.Y = 0;
709         ZeroMemory(&sr, sizeof(sr));
710         sr.Right = buffSize.X - 1;
711         ret = ReadConsoleOutputW(hConOut, buff, buffSize, c, &sr);
712         ok_int(ret, 1);
713         ok_int(sr.Left, 0);
714         ok_int(sr.Top, 0);
715         ok_int(sr.Right, buffSize.X - 1);
716         ok_int(sr.Bottom, 0);
717 
718         /* check buff */
719         ok_int(buff[0].Char.UnicodeChar, L'A');
720         ok_int(buff[0].Attributes, ATTR);
721 
722         /* read attr */
723         ret = ReadConsoleOutputAttribute(hConOut, attrs, 1, c, &len);
724         ok_int(ret, 1);
725         ok_long(len, 1);
726         ok_int(attrs[0], ATTR);
727 
728         /* read char */
729         c.X = c.Y = 0;
730         memset(str, 0x7F, sizeof(str));
731         ret = ReadConsoleOutputCharacterW(hConOut, str, 4, c, &len);
732         ok_int(ret, 1);
733         ok_long(len, 4);
734         ok_int(str[0], L'A');
735         ok_int(str[1], L'A');
736         ok_int(str[2], L'A');
737         ok_int(str[3], L'A');
738 
739         /* set cursor */
740         c.X = c.Y = 0;
741         SetConsoleCursorPosition(hConOut, c);
742         okCURSOR(hConOut, c);
743 
744         /* write u0414 */
745         ret = WriteConsoleW(hConOut, u0414, 1, &len, NULL);
746         ok_int(ret, 1);
747         ok_long(len, 1);
748 
749         /* reset buff */
750         buffSize.X = ARRAYSIZE(buff);
751         buffSize.Y = 1;
752         memset(buff, 0x7F, sizeof(buff));
753 
754         /* read output */
755         c.X = c.Y = 0;
756         ZeroMemory(&sr, sizeof(sr));
757         sr.Right = buffSize.X - 1;
758         ret = ReadConsoleOutputW(hConOut, buff, buffSize, c, &sr);
759         ok_int(ret, 1);
760         ok_int(sr.Left, 0);
761         ok_int(sr.Top, 0);
762         ok_int(sr.Right, buffSize.X - 1);
763         ok_int(sr.Bottom, 0);
764 
765         /* check buff */
766         if (s_bIs8Plus)
767         {
768             ok_int(buff[0].Char.UnicodeChar, 0x0414);
769             ok_int(buff[0].Attributes, ATTR | COMMON_LVB_LEADING_BYTE);
770             ok_int(buff[1].Char.UnicodeChar, 0x0414);
771             ok_int(buff[1].Attributes, ATTR | COMMON_LVB_TRAILING_BYTE);
772         }
773         else
774         {
775             ok_int(buff[0].Char.UnicodeChar, 0x0414);
776             ok_int(buff[0].Attributes, ATTR);
777             ok_int(buff[1].Char.UnicodeChar, L'A');
778             ok_int(buff[1].Attributes, ATTR);
779         }
780         ok_int(buff[2].Char.UnicodeChar, L'A');
781         ok_int(buff[2].Attributes, ATTR);
782         ok_int(buff[3].Char.UnicodeChar, L'A');
783         ok_int(buff[3].Attributes, ATTR);
784 
785         /* read attr */
786         ret = ReadConsoleOutputAttribute(hConOut, attrs, ARRAYSIZE(attrs), c, &len);
787         ok_int(ret, 1);
788         ok_long(len, ARRAYSIZE(attrs));
789         ok_int(attrs[0], ATTR | COMMON_LVB_LEADING_BYTE);
790         ok_int(attrs[1], ATTR | COMMON_LVB_TRAILING_BYTE);
791         ok_int(attrs[2], ATTR);
792         ok_int(attrs[3], ATTR);
793 
794         /* read char */
795         c.X = c.Y = 0;
796         memset(str, 0x7F, sizeof(str));
797         ret = ReadConsoleOutputCharacterW(hConOut, str, 4, c, &len);
798         ok_int(ret, 1);
799         ok_long(len, 3);
800         ok_int(str[0], 0x0414);
801         ok_int(str[1], L'A');
802         ok_int(str[2], L'A');
803         if (s_bIs8Plus)
804             ok_int(str[3], 0);
805         else
806             ok_int(str[3], 0x7F7F);
807 
808         /* set cursor */
809         c.X = 1;
810         c.Y = 0;
811         SetConsoleCursorPosition(hConOut, c);
812         okCURSOR(hConOut, c);
813 
814         /* write u0414 */
815         ret = WriteConsoleW(hConOut, u0414, 1, &len, NULL);
816         ok_int(ret, 1);
817         ok_long(len, 1);
818 
819         /* read output */
820         c.X = c.Y = 0;
821         ZeroMemory(&sr, sizeof(sr));
822         sr.Right = buffSize.X - 1;
823         ret = ReadConsoleOutputW(hConOut, buff, buffSize, c, &sr);
824         ok_int(ret, 1);
825         ok_int(sr.Left, 0);
826         ok_int(sr.Top, 0);
827         ok_int(sr.Right, buffSize.X - 1);
828         ok_int(sr.Bottom, 0);
829 
830         /* check buff */
831         if (s_bIs8Plus)
832         {
833             ok_int(buff[0].Char.UnicodeChar, 0x0414);
834             ok_int(buff[0].Attributes, ATTR | COMMON_LVB_LEADING_BYTE);
835             ok_int(buff[1].Char.UnicodeChar, 0x0414);
836             ok_int(buff[1].Attributes, ATTR | COMMON_LVB_LEADING_BYTE);
837             ok_int(buff[2].Char.UnicodeChar, 0x0414);
838             ok_int(buff[2].Attributes, ATTR | COMMON_LVB_TRAILING_BYTE);
839         }
840         else
841         {
842             ok_int(buff[0].Char.UnicodeChar, L' ');
843             ok_int(buff[0].Attributes, ATTR);
844             ok_int(buff[1].Char.UnicodeChar, 0x0414);
845             ok_int(buff[1].Attributes, ATTR);
846             ok_int(buff[2].Char.UnicodeChar, L'A');
847             ok_int(buff[2].Attributes, ATTR);
848         }
849         ok_int(buff[3].Char.UnicodeChar, L'A');
850         ok_int(buff[3].Attributes, ATTR);
851 
852         /* read attr */
853         c.X = c.Y = 0;
854         ret = ReadConsoleOutputAttribute(hConOut, attrs, ARRAYSIZE(attrs), c, &len);
855         ok_int(ret, 1);
856         ok_long(len, ARRAYSIZE(attrs));
857         if (s_bIs8Plus)
858             ok_int(attrs[0], ATTR | COMMON_LVB_LEADING_BYTE);
859         else
860             ok_int(attrs[0], ATTR);
861         ok_int(attrs[1], ATTR | COMMON_LVB_LEADING_BYTE);
862         ok_int(attrs[2], ATTR | COMMON_LVB_TRAILING_BYTE);
863         ok_int(attrs[3], ATTR);
864 
865         /* read char */
866         c.X = c.Y = 0;
867         memset(str, 0x7F, sizeof(str));
868         ret = ReadConsoleOutputCharacterW(hConOut, str, 4, c, &len);
869         ok_int(ret, 1);
870         ok_long(len, 3);
871         if (s_bIs8Plus)
872         {
873             ok_int(str[0], 0x0414);
874             ok_int(str[1], 0x0414);
875             ok_int(str[2], L'A');
876             ok_int(str[3], 0);
877         }
878         else
879         {
880             ok_int(str[0], L' ');
881             ok_int(str[1], 0x0414);
882             ok_int(str[2], L'A');
883             ok_int(str[3], 0x7F7F);
884         }
885 
886         /* set cursor */
887         c.X = csbi.dwSize.X - 1;
888         c.Y = 0;
889         SetConsoleCursorPosition(hConOut, c);
890         okCURSOR(hConOut, c);
891 
892         /* write u0414 */
893         WriteConsoleW(hConOut, u0414, 1, &len, NULL);
894         ok_int(ret, 1);
895         ok_long(len, 1);
896 
897         /* reset buff */
898         buffSize.X = ARRAYSIZE(buff);
899         buffSize.Y = 1;
900         memset(buff, 0x7F, sizeof(buff));
901 
902         /* read output */
903         c.X = c.Y = 0;
904         sr.Left = csbi.dwSize.X - 2;
905         sr.Top = 0;
906         sr.Right = csbi.dwSize.X - 1;
907         sr.Bottom = 0;
908         ret = ReadConsoleOutputW(hConOut, buff, buffSize, c, &sr);
909         ok_int(ret, 1);
910         ok_int(sr.Left, csbi.dwSize.X - 2);
911         ok_int(sr.Top, 0);
912         ok_int(sr.Right, csbi.dwSize.X - 1);
913         ok_int(sr.Bottom, 0);
914 
915         /* check buff */
916         ok_int(buff[0].Char.UnicodeChar, L'A');
917         ok_int(buff[0].Attributes, ATTR);
918         ok_int(buff[1].Char.UnicodeChar, L'A');
919         ok_int(buff[1].Attributes, ATTR);
920 
921         /* read attrs */
922         c.X = csbi.dwSize.X - 2;
923         c.Y = 0;
924         ret = ReadConsoleOutputAttribute(hConOut, attrs, ARRAYSIZE(attrs), c, &len);
925         ok_int(ret, 1);
926         ok_long(len, ARRAYSIZE(attrs));
927         ok_int(attrs[0], ATTR);
928         ok_int(attrs[1], ATTR);
929 
930         /* read char */
931         ret = ReadConsoleOutputCharacterW(hConOut, str, 2, c, &len);
932         ok_int(ret, 1);
933         ok_long(len, 2);
934         ok_int(str[0], L'A');
935         ok_int(str[1], L'A');
936 
937         /* fill by 'A' */
938         c.X = c.Y = 0;
939         ret = FillConsoleOutputCharacterW(hConOut, L'A', 10, c, &len);
940         ok_int(ret, 1);
941         ok_long(len, 10);
942 
943         /* fill by u0414 */
944         c.X = 1;
945         c.Y = 0;
946         ret = FillConsoleOutputCharacterW(hConOut, 0x0414, 1, c, &len);
947         c.X = c.Y = 0;
948         ok_int(ret, 1);
949         ok_long(len, 1);
950 
951         /* reset buff */
952         buffSize.X = ARRAYSIZE(buff);
953         buffSize.Y = 1;
954         memset(buff, 0x7F, sizeof(buff));
955 
956         /* read output */
957         c.X = c.Y = 0;
958         sr.Left = 0;
959         sr.Top = 0;
960         sr.Right = 4;
961         sr.Bottom = 0;
962         ret = ReadConsoleOutputW(hConOut, buff, buffSize, c, &sr);
963         ok_int(ret, 1);
964         ok_int(sr.Left, 0);
965         ok_int(sr.Top, 0);
966         ok_int(sr.Right, 4);
967         ok_int(sr.Bottom, 0);
968 
969         /* check buff */
970         ok_int(buff[0].Char.UnicodeChar, L'A');
971         ok_int(buff[0].Attributes, ATTR);
972         if (s_bIs8Plus)
973         {
974             ok_int(buff[1].Char.UnicodeChar, 0x0414);
975             ok_int(buff[1].Attributes, ATTR | COMMON_LVB_LEADING_BYTE);
976             ok_int(buff[2].Char.UnicodeChar, 0x0414);
977             ok_int(buff[2].Attributes, ATTR | COMMON_LVB_TRAILING_BYTE);
978         }
979         else
980         {
981             ok_int(buff[1].Char.UnicodeChar, L' ');
982             ok_int(buff[1].Attributes, ATTR);
983             ok_int(buff[2].Char.UnicodeChar, L'A');
984             ok_int(buff[2].Attributes, ATTR);
985         }
986         ok_int(buff[3].Char.UnicodeChar, L'A');
987         ok_int(buff[3].Attributes, ATTR);
988         ok_int(buff[4].Char.UnicodeChar, L'A');
989         ok_int(buff[4].Attributes, ATTR);
990 
991         /* read attrs */
992         c.X = 0;
993         c.Y = 0;
994         ret = ReadConsoleOutputAttribute(hConOut, attrs, 4, c, &len);
995         ok_int(ret, 1);
996         ok_long(len, 4);
997         ok_int(attrs[0], ATTR);
998         if (s_bIs8Plus)
999         {
1000             ok_int(attrs[1], ATTR | COMMON_LVB_LEADING_BYTE);
1001             ok_int(attrs[2], ATTR | COMMON_LVB_TRAILING_BYTE);
1002         }
1003         else
1004         {
1005             ok_int(attrs[1], ATTR);
1006             ok_int(attrs[2], ATTR);
1007         }
1008         ok_int(attrs[3], ATTR);
1009     }
1010 
1011     /* COMMON_LVB_LEADING_BYTE and COMMON_LVB_TRAILING_BYTE for u9580 */
1012     {
1013         /* set cursor */
1014         c.X = c.Y = 0;
1015         SetConsoleCursorPosition(hConOut, c);
1016         okCURSOR(hConOut, c);
1017 
1018         /* fill by 'A' */
1019         ret = FillConsoleOutputCharacterW(hConOut, L'A', csbi.dwSize.X * 2, c, &len);
1020         ok_int(ret, 1);
1021         ok_long(len, csbi.dwSize.X * 2);
1022 
1023         /* reset buff */
1024         buffSize.X = ARRAYSIZE(buff);
1025         buffSize.Y = 1;
1026         memset(buff, 0x7F, sizeof(buff));
1027 
1028         /* read output */
1029         c.X = c.Y = 0;
1030         ZeroMemory(&sr, sizeof(sr));
1031         sr.Right = buffSize.X - 1;
1032         ret = ReadConsoleOutputW(hConOut, buff, buffSize, c, &sr);
1033         ok_int(ret, 1);
1034         ok_int(sr.Left, 0);
1035         ok_int(sr.Top, 0);
1036         ok_int(sr.Right, buffSize.X - 1);
1037         ok_int(sr.Bottom, 0);
1038 
1039         /* check buff */
1040         ok_int(buff[0].Char.UnicodeChar, L'A');
1041         ok_int(buff[0].Attributes, ATTR);
1042 
1043         /* read attr */
1044         ret = ReadConsoleOutputAttribute(hConOut, attrs, 1, c, &len);
1045         ok_int(ret, 1);
1046         ok_long(len, 1);
1047         ok_int(attrs[0], ATTR);
1048 
1049         /* read char */
1050         memset(str, 0x7F, sizeof(str));
1051         ret = ReadConsoleOutputCharacterW(hConOut, str, 2, c, &len);
1052         ok_int(ret, 1);
1053         ok_long(len, 2);
1054         ok_int(str[0], L'A');
1055         ok_int(str[1], L'A');
1056 
1057         /* set cursor */
1058         c.X = 0;
1059         c.Y = 0;
1060         SetConsoleCursorPosition(hConOut, c);
1061         okCURSOR(hConOut, c);
1062 
1063         /* write u9580 */
1064         ret = WriteConsoleW(hConOut, u9580, 1, &len, NULL);
1065         ok_int(ret, 1);
1066         ok_long(len, 1);
1067 
1068         /* reset buff */
1069         buffSize.X = ARRAYSIZE(buff);
1070         buffSize.Y = 1;
1071         memset(buff, 0x7F, sizeof(buff));
1072 
1073         /* read output */
1074         c.X = c.Y = 0;
1075         ZeroMemory(&sr, sizeof(sr));
1076         sr.Right = buffSize.X - 1;
1077         ret = ReadConsoleOutputW(hConOut, buff, buffSize, c, &sr);
1078         ok_int(ret, 1);
1079         ok_int(sr.Left, 0);
1080         ok_int(sr.Top, 0);
1081         ok_int(sr.Right, buffSize.X - 1);
1082         ok_int(sr.Bottom, 0);
1083 
1084         /* check buff */
1085         if (s_bIs8Plus)
1086         {
1087             ok_int(buff[0].Char.UnicodeChar, u9580[0]);
1088             ok_int(buff[0].Attributes, ATTR | COMMON_LVB_LEADING_BYTE);
1089             ok_int(buff[1].Char.UnicodeChar, u9580[0]);
1090             ok_int(buff[1].Attributes, ATTR | COMMON_LVB_TRAILING_BYTE);
1091         }
1092         else
1093         {
1094             ok_int(buff[0].Char.UnicodeChar, u9580[0]);
1095             ok_int(buff[0].Attributes, ATTR);
1096             ok_int(buff[1].Char.UnicodeChar, L'A');
1097             ok_int(buff[1].Attributes, ATTR);
1098         }
1099         ok_int(buff[2].Char.UnicodeChar, L'A');
1100         ok_int(buff[2].Attributes, ATTR);
1101         ok_int(buff[3].Char.UnicodeChar, L'A');
1102         ok_int(buff[3].Attributes, ATTR);
1103 
1104         /* read attr */
1105         ret = ReadConsoleOutputAttribute(hConOut, attrs, ARRAYSIZE(attrs), c, &len);
1106         ok_int(ret, 1);
1107         ok_long(len, ARRAYSIZE(attrs));
1108 
1109         ok_int(attrs[0], ATTR | COMMON_LVB_LEADING_BYTE);
1110         ok_int(attrs[1], ATTR | COMMON_LVB_TRAILING_BYTE);
1111         ok_int(attrs[2], ATTR);
1112         ok_int(attrs[3], ATTR);
1113 
1114         /* read char */
1115         c.X = c.Y = 0;
1116         memset(str, 0x7F, sizeof(str));
1117         ret = ReadConsoleOutputCharacterW(hConOut, str, 4, c, &len);
1118         ok_int(ret, 1);
1119         ok_long(len, 3);
1120         ok_int(str[0], u9580[0]);
1121         ok_int(str[1], L'A');
1122         ok_int(str[2], L'A');
1123         if (s_bIs8Plus)
1124             ok_int(str[3], 0);
1125         else
1126             ok_int(str[3], 0x7F7F);
1127 
1128         /* set cursor */
1129         c.X = 1;
1130         c.Y = 0;
1131         SetConsoleCursorPosition(hConOut, c);
1132         okCURSOR(hConOut, c);
1133 
1134         /* write u9580 */
1135         ret = WriteConsoleW(hConOut, u9580, 1, &len, NULL);
1136         ok_int(ret, 1);
1137         ok_long(len, 1);
1138 
1139         /* read output */
1140         c.X = c.Y = 0;
1141         ZeroMemory(&sr, sizeof(sr));
1142         sr.Right = buffSize.X - 1;
1143         ret = ReadConsoleOutputW(hConOut, buff, buffSize, c, &sr);
1144         ok_int(ret, 1);
1145         ok_int(sr.Left, 0);
1146         ok_int(sr.Top, 0);
1147         ok_int(sr.Right, buffSize.X - 1);
1148         ok_int(sr.Bottom, 0);
1149 
1150         /* check buff */
1151         if (s_bIs8Plus)
1152         {
1153             ok_int(buff[0].Char.UnicodeChar, u9580[0]);
1154             ok_int(buff[0].Attributes, ATTR | COMMON_LVB_LEADING_BYTE);
1155             ok_int(buff[1].Char.UnicodeChar, u9580[0]);
1156             ok_int(buff[1].Attributes, ATTR | COMMON_LVB_LEADING_BYTE);
1157             ok_int(buff[2].Char.UnicodeChar, u9580[0]);
1158             ok_int(buff[2].Attributes, ATTR | COMMON_LVB_TRAILING_BYTE);
1159         }
1160         else
1161         {
1162             ok_int(buff[0].Char.UnicodeChar, L' ');
1163             ok_int(buff[0].Attributes, ATTR);
1164             ok_int(buff[1].Char.UnicodeChar, u9580[0]);
1165             ok_int(buff[1].Attributes, ATTR);
1166             ok_int(buff[2].Char.UnicodeChar, L'A');
1167             ok_int(buff[2].Attributes, ATTR);
1168         }
1169         ok_int(buff[3].Char.UnicodeChar, L'A');
1170         ok_int(buff[3].Attributes, ATTR);
1171 
1172         /* read attr */
1173         c.X = c.Y = 0;
1174         ret = ReadConsoleOutputAttribute(hConOut, attrs, ARRAYSIZE(attrs), c, &len);
1175         ok_int(ret, 1);
1176         ok_long(len, ARRAYSIZE(attrs));
1177 
1178         if (s_bIs8Plus)
1179         {
1180             ok_int(attrs[0], ATTR | COMMON_LVB_LEADING_BYTE);
1181         }
1182         else
1183         {
1184             ok_int(attrs[0], ATTR);
1185         }
1186         ok_int(attrs[1], ATTR | COMMON_LVB_LEADING_BYTE);
1187         ok_int(attrs[2], ATTR | COMMON_LVB_TRAILING_BYTE);
1188         ok_int(attrs[3], ATTR);
1189 
1190         /* set cursor */
1191         c.X = csbi.dwSize.X - 1;
1192         c.Y = 0;
1193         SetConsoleCursorPosition(hConOut, c);
1194         okCURSOR(hConOut, c);
1195 
1196         /* write u9580 */
1197         WriteConsoleW(hConOut, u9580, 1, &len, NULL);
1198         ok_int(ret, 1);
1199         ok_long(len, 1);
1200 
1201         /* reset buff */
1202         buffSize.X = ARRAYSIZE(buff);
1203         buffSize.Y = 1;
1204         memset(buff, 0x7F, sizeof(buff));
1205 
1206         /* read output */
1207         c.X = c.Y = 0;
1208         sr.Left = csbi.dwSize.X - 2;
1209         sr.Top = 0;
1210         sr.Right = csbi.dwSize.X - 1;
1211         sr.Bottom = 0;
1212         ret = ReadConsoleOutputW(hConOut, buff, buffSize, c, &sr);
1213         ok_int(ret, 1);
1214         ok_int(sr.Left, csbi.dwSize.X - 2);
1215         ok_int(sr.Top, 0);
1216         ok_int(sr.Right, csbi.dwSize.X - 1);
1217         ok_int(sr.Bottom, 0);
1218 
1219         /* check buff */
1220         ok_int(buff[0].Char.UnicodeChar, L'A');
1221         ok_int(buff[0].Attributes, ATTR);
1222         ok_int(buff[1].Char.UnicodeChar, L'A');
1223         ok_int(buff[1].Attributes, ATTR);
1224 
1225         /* read attr */
1226         c.X = csbi.dwSize.X - 2;
1227         c.Y = 0;
1228         ret = ReadConsoleOutputAttribute(hConOut, attrs, ARRAYSIZE(attrs), c, &len);
1229         ok_int(ret, 1);
1230         ok_long(len, ARRAYSIZE(attrs));
1231         ok_int(attrs[0], ATTR);
1232         ok_int(attrs[1], ATTR);
1233 
1234         /* read char */
1235         memset(str, 0x7F, sizeof(str));
1236         ret = ReadConsoleOutputCharacterW(hConOut, str, 2, c, &len);
1237         ok_int(ret, 1);
1238         ok_long(len, 2);
1239         ok_int(str[0], L'A');
1240         ok_int(str[1], L'A');
1241 
1242         /* fill by 'A' */
1243         c.X = c.Y = 0;
1244         ret = FillConsoleOutputCharacterW(hConOut, L'A', 10, c, &len);
1245         ok_int(ret, 1);
1246         ok_long(len, 10);
1247 
1248         /* fill by u9580 */
1249         c.X = 1;
1250         c.Y = 0;
1251         ret = FillConsoleOutputCharacterW(hConOut, u9580[0], 1, c, &len);
1252         c.X = c.Y = 0;
1253         ok_int(ret, 1);
1254         ok_long(len, 1);
1255 
1256         /* reset buff */
1257         buffSize.X = ARRAYSIZE(buff);
1258         buffSize.Y = 1;
1259         memset(buff, 0x7F, sizeof(buff));
1260 
1261         /* read output */
1262         c.X = c.Y = 0;
1263         sr.Left = 0;
1264         sr.Top = 0;
1265         sr.Right = 4;
1266         sr.Bottom = 0;
1267         ret = ReadConsoleOutputW(hConOut, buff, buffSize, c, &sr);
1268         ok_int(ret, 1);
1269         ok_int(sr.Left, 0);
1270         ok_int(sr.Top, 0);
1271         ok_int(sr.Right, 4);
1272         ok_int(sr.Bottom, 0);
1273 
1274         /* check buff */
1275         ok_int(buff[0].Char.UnicodeChar, L'A');
1276         ok_int(buff[0].Attributes, ATTR);
1277         if (s_bIs8Plus)
1278         {
1279             ok_int(buff[1].Char.UnicodeChar, u9580[0]);
1280             ok_int(buff[1].Attributes, ATTR | COMMON_LVB_LEADING_BYTE);
1281             ok_int(buff[2].Char.UnicodeChar, u9580[0]);
1282             ok_int(buff[2].Attributes, ATTR | COMMON_LVB_TRAILING_BYTE);
1283         }
1284         else
1285         {
1286             ok_int(buff[1].Char.UnicodeChar, L' ');
1287             ok_int(buff[1].Attributes, ATTR);
1288             ok_int(buff[2].Char.UnicodeChar, L'A');
1289             ok_int(buff[2].Attributes, ATTR);
1290         }
1291         ok_int(buff[3].Char.UnicodeChar, L'A');
1292         ok_int(buff[3].Attributes, ATTR);
1293         ok_int(buff[4].Char.UnicodeChar, L'A');
1294         ok_int(buff[4].Attributes, ATTR);
1295 
1296         /* read attrs */
1297         c.X = 0;
1298         c.Y = 0;
1299         ret = ReadConsoleOutputAttribute(hConOut, attrs, 4, c, &len);
1300         ok_int(ret, 1);
1301         ok_long(len, 4);
1302         ok_int(attrs[0], ATTR);
1303         if (s_bIs8Plus)
1304         {
1305             ok_int(attrs[1], ATTR | COMMON_LVB_LEADING_BYTE);
1306             ok_int(attrs[2], ATTR | COMMON_LVB_TRAILING_BYTE);
1307         }
1308         else
1309         {
1310             ok_int(attrs[1], ATTR);
1311             ok_int(attrs[2], ATTR);
1312         }
1313         ok_int(attrs[3], ATTR);
1314     }
1315 
1316     /* FillConsoleOutputAttribute and WriteConsoleOutput */
1317     {
1318         c.X = c.Y = 0;
1319         SetConsoleCursorPosition(hConOut, c);
1320         okCURSOR(hConOut, c);
1321         for (n = 0; n < count; ++n)
1322         {
1323             ret = WriteConsoleW(hConOut, space, lstrlenW(space), &len, NULL);
1324             ok_int(ret, 1);
1325             ok_long(len, 1);
1326         }
1327 
1328         /* fill attrs */
1329         c.X = c.Y = 0;
1330         SetConsoleCursorPosition(hConOut, c);
1331         okCURSOR(hConOut, c);
1332         ret = FillConsoleOutputAttribute(hConOut, 0xFFFF, 2, c, &len);
1333         ok_int(ret, 1);
1334         ok_long(len, 2);
1335 
1336         /* read attrs */
1337         memset(attrs, 0x7F, sizeof(attrs));
1338         ret = ReadConsoleOutputAttribute(hConOut, attrs, 3, c, &len);
1339         ok_int(ret, 1);
1340         ok_long(len, 3);
1341         if (s_bIs8Plus)
1342         {
1343             ok_int(attrs[0], 0xDCFF);
1344             ok_int(attrs[1], 0xDCFF);
1345         }
1346         else
1347         {
1348             ok_int(attrs[0], 0xFCFF);
1349             ok_int(attrs[1], 0xFCFF);
1350         }
1351         ok_int(attrs[2], ATTR);
1352 
1353         /* fill attrs */
1354         c.X = c.Y = 0;
1355         SetConsoleCursorPosition(hConOut, c);
1356         okCURSOR(hConOut, c);
1357         ret = FillConsoleOutputAttribute(hConOut, ATTR, 4, c, &len);
1358         ok_int(ret, 1);
1359         ok_long(len, 4);
1360 
1361         /* write */
1362         c.X = c.Y = 0;
1363         sr.Left = 0;
1364         sr.Top = 0;
1365         sr.Right = 4;
1366         sr.Bottom = 0;
1367         // Check how Read/WriteConsoleOutput() handle inconsistent DBCS flags.
1368         buff[0].Char.UnicodeChar = u9580[0];
1369         buff[0].Attributes = ATTR | COMMON_LVB_LEADING_BYTE;
1370         buff[1].Char.UnicodeChar = u9580[0];
1371         buff[1].Attributes = ATTR | COMMON_LVB_LEADING_BYTE;
1372         buff[2].Char.UnicodeChar = u9580[0];
1373         buff[2].Attributes = ATTR | COMMON_LVB_TRAILING_BYTE;
1374         buff[3].Char.UnicodeChar = L'A';
1375         buff[3].Attributes = ATTR;
1376         buff[4].Char.UnicodeChar = L' ';
1377         buff[4].Attributes = 0xFFFF;
1378         buffSize.X = 4;
1379         buffSize.Y = 1;
1380         ret = WriteConsoleOutputW(hConOut, buff, buffSize, c, &sr);
1381         ok_int(ret, 1);
1382         ok_int(sr.Left, 0);
1383         ok_int(sr.Top, 0);
1384         ok_int(sr.Right, 3);
1385         ok_int(sr.Bottom, 0);
1386 
1387         /* read output */
1388         sr.Left = 0;
1389         sr.Top = 0;
1390         sr.Right = 4;
1391         sr.Bottom = 0;
1392         memset(buff, 0x7F, sizeof(buff));
1393         ret = ReadConsoleOutputW(hConOut, buff, buffSize, c, &sr);
1394         ok_int(ret, 1);
1395         ok_int(sr.Left, 0);
1396         ok_int(sr.Top, 0);
1397         ok_int(sr.Right, 3);
1398         ok_int(sr.Bottom, 0);
1399 
1400         /* check buff */
1401         if (s_bIs8Plus)
1402         {
1403             ok_int(buff[0].Char.UnicodeChar, u9580[0]);
1404             ok_int(buff[0].Attributes, ATTR | COMMON_LVB_LEADING_BYTE);
1405             ok_int(buff[1].Char.UnicodeChar, u9580[0]);
1406             ok_int(buff[1].Attributes, ATTR | COMMON_LVB_LEADING_BYTE);
1407             ok_int(buff[2].Char.UnicodeChar, u9580[0]);
1408             ok_int(buff[2].Attributes, ATTR | COMMON_LVB_TRAILING_BYTE);
1409             ok_int(buff[3].Char.UnicodeChar, L'A');
1410             ok_int(buff[3].Attributes, ATTR);
1411             ok_int(buff[4].Char.UnicodeChar, 0x7F7F);
1412             ok_int(buff[4].Attributes, 0x7F7F);
1413         }
1414         else
1415         {
1416             ok_int(buff[0].Char.UnicodeChar, u9580[0]);
1417             ok_int(buff[0].Attributes, ATTR);
1418             ok_int(buff[1].Char.UnicodeChar, u9580[0]);
1419             ok_int(buff[1].Attributes, ATTR);
1420             ok_int(buff[2].Char.UnicodeChar, 0);
1421             ok_int(buff[2].Attributes, 0);
1422             ok_int(buff[3].Char.UnicodeChar, 0);
1423             ok_int(buff[3].Attributes, 0);
1424             ok_int(buff[4].Char.UnicodeChar, 0x7F7F);
1425             ok_int(buff[4].Attributes, 0x7F7F);
1426         }
1427 
1428         /* read attrs */
1429         c.X = c.Y = 0;
1430         memset(attrs, 0x7F, sizeof(attrs));
1431         ret = ReadConsoleOutputAttribute(hConOut, attrs, 6, c, &len);
1432         ok_int(ret, 1);
1433         ok_long(len, 6);
1434         ok_int(attrs[0], ATTR | COMMON_LVB_LEADING_BYTE);
1435         if (s_bIs8Plus)
1436         {
1437             ok_int(attrs[1], ATTR | COMMON_LVB_LEADING_BYTE);
1438             ok_int(attrs[2], ATTR | COMMON_LVB_TRAILING_BYTE);
1439             ok_int(attrs[3], ATTR);
1440         }
1441         else
1442         {
1443             ok_int(attrs[1], ATTR | COMMON_LVB_TRAILING_BYTE);
1444             ok_int(attrs[2], ATTR | COMMON_LVB_LEADING_BYTE);
1445             ok_int(attrs[3], ATTR | COMMON_LVB_TRAILING_BYTE);
1446         }
1447         ok_int(attrs[4], ATTR);
1448         ok_int(attrs[5], ATTR);
1449     }
1450 
1451     /* WriteConsoleOutputCharacterW and WriteConsoleOutputAttribute */
1452     {
1453         c.X = c.Y = 0;
1454         SetConsoleCursorPosition(hConOut, c);
1455         okCURSOR(hConOut, c);
1456         for (n = 0; n < count; ++n)
1457         {
1458             ret = WriteConsoleW(hConOut, space, lstrlenW(space), &len, NULL);
1459             ok_int(ret, 1);
1460             ok_long(len, 1);
1461         }
1462 
1463         /* write attrs */
1464         attrs[0] = ATTR;
1465         attrs[1] = 0xFFFF;
1466         attrs[2] = ATTR;
1467         attrs[3] = 0;
1468         ret = WriteConsoleOutputAttribute(hConOut, attrs, 4, c, &len);
1469         ok_int(ret, 1);
1470         ok_long(len, 4);
1471 
1472         /* read attrs */
1473         memset(attrs, 0x7F, sizeof(attrs));
1474         ret = ReadConsoleOutputAttribute(hConOut, attrs, 4, c, &len);
1475         ok_int(ret, 1);
1476         ok_long(len, 4);
1477         ok_int(attrs[0], ATTR);
1478         if (s_bIs8Plus)
1479             ok_int(attrs[1], 0xDCFF);
1480         else
1481             ok_int(attrs[1], 0xFCFF);
1482         ok_int(attrs[2], ATTR);
1483         ok_int(attrs[3], 0);
1484 
1485         /* fill attr */
1486         ret = FillConsoleOutputAttribute(hConOut, ATTR, 4, c, &len);
1487         ok_int(ret, 1);
1488         ok_long(len, 4);
1489 
1490         /* write char */
1491         ret = WriteConsoleOutputCharacterW(hConOut, s_str, 4, c, &len);
1492         ok_int(ret, 1);
1493         ok_long(len, 4);
1494 
1495         /* read attrs */
1496         memset(attrs, 0x7F, sizeof(attrs));
1497         ret = ReadConsoleOutputAttribute(hConOut, attrs, 4, c, &len);
1498         ok_int(ret, 1);
1499         ok_long(len, 4);
1500         ok_int(attrs[0], ATTR);
1501         ok_int(attrs[1], ATTR | COMMON_LVB_LEADING_BYTE);
1502         ok_int(attrs[2], ATTR | COMMON_LVB_TRAILING_BYTE);
1503         ok_int(attrs[3], ATTR);
1504     }
1505 
1506     /* Restore code page */
1507     SetConsoleOutputCP(oldcp);
1508 }
1509 
1510 
START_TEST(ConsoleCP)1511 START_TEST(ConsoleCP)
1512 {
1513     HANDLE hConIn, hConOut;
1514     OSVERSIONINFOA osver = { sizeof(osver) };
1515 
1516     // https://github.com/reactos/reactos/pull/2131#issuecomment-563189380
1517     GetVersionExA(&osver);
1518     s_bIs8Plus = (osver.dwMajorVersion > 6) ||
1519                  (osver.dwMajorVersion == 6 && osver.dwMinorVersion >= 2);
1520 
1521     FreeConsole();
1522     ok(AllocConsole(), "Couldn't alloc console\n");
1523 
1524     hConIn = CreateFileA("CONIN$", GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0);
1525     hConOut = CreateFileA("CONOUT$", GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0);
1526     ok(hConIn != INVALID_HANDLE_VALUE, "Opening ConIn\n");
1527     ok(hConOut != INVALID_HANDLE_VALUE, "Opening ConOut\n");
1528 
1529     /* Retrieve the system OEM code page */
1530     s_uOEMCP = GetOEMCP();
1531     trace("Running on %s system (codepage %d)\n",
1532           IsCJKCodePage(s_uOEMCP) ? "CJK" : "Non-CJK",
1533           s_uOEMCP);
1534 
1535     /* Test thread lang ID syncing with console code page */
1536     test_CP_ThreadLang();
1537 
1538     if (IsValidLocale(lcidRussian, LCID_INSTALLED))
1539     {
1540         if (!IsCJKCodePage(s_uOEMCP))
1541             test_cp855(hConOut);
1542         else
1543             skip("Russian testcase is skipped because of CJK\n");
1544     }
1545     else
1546     {
1547         skip("Russian locale is not installed\n");
1548     }
1549 
1550     if (IsValidLocale(lcidJapanese, LCID_INSTALLED))
1551     {
1552         if (IsCJKCodePage(s_uOEMCP))
1553             test_cp932(hConOut);
1554         else
1555             skip("Japanese testcase is skipped because of not CJK\n");
1556     }
1557     else
1558     {
1559         skip("Japanese locale is not installed\n");
1560     }
1561 
1562     CloseHandle(hConIn);
1563     CloseHandle(hConOut);
1564     FreeConsole();
1565     ok(AllocConsole(), "Couldn't alloc console\n");
1566 }
1567