1 | #include <windows.h>
|
2 |
|
3 | #include <assert.h>
|
4 | #include <vector>
|
5 |
|
6 | #include "TestUtil.cc"
|
7 |
|
8 | #define COUNT_OF(x) (sizeof(x) / sizeof((x)[0]))
|
9 |
|
10 |
|
11 | CHAR_INFO ci(wchar_t ch, WORD attributes) {
|
12 | CHAR_INFO ret;
|
13 | ret.Char.UnicodeChar = ch;
|
14 | ret.Attributes = attributes;
|
15 | return ret;
|
16 | }
|
17 |
|
18 | CHAR_INFO ci(wchar_t ch) {
|
19 | return ci(ch, 7);
|
20 | }
|
21 |
|
22 | CHAR_INFO ci() {
|
23 | return ci(L' ');
|
24 | }
|
25 |
|
26 | bool operator==(SMALL_RECT x, SMALL_RECT y) {
|
27 | return !memcmp(&x, &y, sizeof(x));
|
28 | }
|
29 |
|
30 | SMALL_RECT sr(COORD pt, COORD size) {
|
31 | return {
|
32 | pt.X, pt.Y,
|
33 | static_cast<SHORT>(pt.X + size.X - 1),
|
34 | static_cast<SHORT>(pt.Y + size.Y - 1)
|
35 | };
|
36 | }
|
37 |
|
38 | static void set(
|
39 | const COORD pt,
|
40 | const COORD size,
|
41 | const std::vector<CHAR_INFO> &data) {
|
42 | assert(data.size() == size.X * size.Y);
|
43 | SMALL_RECT writeRegion = sr(pt, size);
|
44 | BOOL ret = WriteConsoleOutputW(
|
45 | GetStdHandle(STD_OUTPUT_HANDLE),
|
46 | data.data(), size, {0, 0}, &writeRegion);
|
47 | assert(ret && writeRegion == sr(pt, size));
|
48 | }
|
49 |
|
50 | static void set(
|
51 | const COORD pt,
|
52 | const std::vector<CHAR_INFO> &data) {
|
53 | set(pt, {static_cast<SHORT>(data.size()), 1}, data);
|
54 | }
|
55 |
|
56 | static void writeAttrsAt(
|
57 | const COORD pt,
|
58 | const std::vector<WORD> &data) {
|
59 | DWORD actual = 0;
|
60 | BOOL ret = WriteConsoleOutputAttribute(
|
61 | GetStdHandle(STD_OUTPUT_HANDLE),
|
62 | data.data(), data.size(), pt, &actual);
|
63 | assert(ret && actual == data.size());
|
64 | }
|
65 |
|
66 | static void writeCharsAt(
|
67 | const COORD pt,
|
68 | const std::vector<wchar_t> &data) {
|
69 | DWORD actual = 0;
|
70 | BOOL ret = WriteConsoleOutputCharacterW(
|
71 | GetStdHandle(STD_OUTPUT_HANDLE),
|
72 | data.data(), data.size(), pt, &actual);
|
73 | assert(ret && actual == data.size());
|
74 | }
|
75 |
|
76 | static void writeChars(
|
77 | const std::vector<wchar_t> &data) {
|
78 | DWORD actual = 0;
|
79 | BOOL ret = WriteConsoleW(
|
80 | GetStdHandle(STD_OUTPUT_HANDLE),
|
81 | data.data(), data.size(), &actual, NULL);
|
82 | assert(ret && actual == data.size());
|
83 | }
|
84 |
|
85 | std::vector<CHAR_INFO> get(
|
86 | const COORD pt,
|
87 | const COORD size) {
|
88 | std::vector<CHAR_INFO> data(size.X * size.Y);
|
89 | SMALL_RECT readRegion = sr(pt, size);
|
90 | BOOL ret = ReadConsoleOutputW(
|
91 | GetStdHandle(STD_OUTPUT_HANDLE),
|
92 | data.data(), size, {0, 0}, &readRegion);
|
93 | assert(ret && readRegion == sr(pt, size));
|
94 | return data;
|
95 | }
|
96 |
|
97 | std::vector<wchar_t> readCharsAt(
|
98 | const COORD pt,
|
99 | int size) {
|
100 | std::vector<wchar_t> data(size);
|
101 | DWORD actual = 0;
|
102 | BOOL ret = ReadConsoleOutputCharacterW(
|
103 | GetStdHandle(STD_OUTPUT_HANDLE),
|
104 | data.data(), data.size(), pt, &actual);
|
105 | assert(ret);
|
106 | data.resize(actual);
|
107 | return data;
|
108 | }
|
109 |
|
110 | static void dump(const COORD pt, const COORD size) {
|
111 | for (CHAR_INFO ci : get(pt, size)) {
|
112 | printf("%04X %04X\n", ci.Char.UnicodeChar, ci.Attributes);
|
113 | }
|
114 | }
|
115 |
|
116 | static void dumpCharsAt(const COORD pt, int size) {
|
117 | for (wchar_t ch : readCharsAt(pt, size)) {
|
118 | printf("%04X\n", ch);
|
119 | }
|
120 | }
|
121 |
|
122 | static COORD getCursorPos() {
|
123 | CONSOLE_SCREEN_BUFFER_INFO info = { sizeof(info) };
|
124 | assert(GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &info));
|
125 | return info.dwCursorPosition;
|
126 | }
|
127 |
|
128 | static void test1() {
|
129 |
|
130 |
|
131 |
|
132 | printf("test1 - overlap full-width char with full-width char\n");
|
133 | writeCharsAt({1,0}, {0x4000, 0x4000});
|
134 | dump({0,0}, {6,1});
|
135 | printf("\n");
|
136 | writeCharsAt({2,0}, {0x4001});
|
137 | dump({0,0}, {6,1});
|
138 | printf("\n");
|
139 | }
|
140 |
|
141 | static void test2() {
|
142 |
|
143 |
|
144 | printf("test2 - overlap full-width char with full-width char (lowlevel)\n");
|
145 | writeCharsAt({1,0}, {0x4000, 0x4000});
|
146 | dump({0,0}, {6,1});
|
147 | printf("\n");
|
148 | set({2,0}, {ci(0x4001,0x107), ci(0x4001,0x207)});
|
149 | dump({0,0}, {6,1});
|
150 | printf("\n");
|
151 | }
|
152 |
|
153 | static void test3() {
|
154 |
|
155 |
|
156 | printf("test3 - explicitly violate LEADING/TRAILING using lowlevel API\n");
|
157 | set({1,0}, {
|
158 | ci(0x4000, 0x207),
|
159 | ci(0x4001, 0x107),
|
160 | ci(0x3044, 7),
|
161 | ci(L'X', 0x107),
|
162 | ci(L'X', 0x207),
|
163 | });
|
164 | dump({0,0}, {7,1});
|
165 | }
|
166 |
|
167 | static void test4() {
|
168 |
|
169 |
|
170 | printf("test4 - use lowlevel to assign two colors to one full-width char\n");
|
171 | set({0,0}, {
|
172 | ci(0x4000, 0x142),
|
173 | ci(0x4000, 0x224),
|
174 | });
|
175 | dump({0,0}, {2,1});
|
176 | }
|
177 |
|
178 | static void test5() {
|
179 |
|
180 |
|
181 | printf("test5 - WriteConsoleOutputAttribute cannot affect LEADING/TRAILING\n");
|
182 |
|
183 |
|
184 | writeCharsAt({0,0}, {0x4000});
|
185 | dump({0,0}, {2,1});
|
186 | writeAttrsAt({0,0}, {0x42, 0x24});
|
187 | printf("\n");
|
188 | dump({0,0}, {2,1});
|
189 |
|
190 |
|
191 | writeCharsAt({0,1}, {'A', ' '});
|
192 | writeAttrsAt({0,1}, {0x107, 0x207});
|
193 | printf("\n");
|
194 | dump({0,1}, {2,1});
|
195 | }
|
196 |
|
197 | static void test6() {
|
198 |
|
199 |
|
200 |
|
201 | printf("test6 - cursor can be either left or right cell of full-width char\n");
|
202 |
|
203 | writeCharsAt({2,1}, {0x4000});
|
204 |
|
205 | setCursorPos(2, 1);
|
206 | auto pos1 = getCursorPos();
|
207 | Sleep(1000);
|
208 |
|
209 | setCursorPos(3, 1);
|
210 | auto pos2 = getCursorPos();
|
211 | Sleep(1000);
|
212 |
|
213 | setCursorPos(0, 15);
|
214 | printf("%d,%d\n", pos1.X, pos1.Y);
|
215 | printf("%d,%d\n", pos2.X, pos2.Y);
|
216 | }
|
217 |
|
218 | static void runTest(void (&test)()) {
|
219 | system("cls");
|
220 | setCursorPos(0, 14);
|
221 | test();
|
222 | system("pause");
|
223 | }
|
224 |
|
225 | int main(int argc, char *argv[]) {
|
226 | if (argc == 1) {
|
227 | startChildProcess(L"CHILD");
|
228 | return 0;
|
229 | }
|
230 |
|
231 | setWindowPos(0, 0, 1, 1);
|
232 | setBufferSize(80, 40);
|
233 | setWindowPos(0, 0, 80, 40);
|
234 |
|
235 | auto cp = GetConsoleOutputCP();
|
236 | assert(cp == 932 || cp == 936 || cp == 949 || cp == 950);
|
237 |
|
238 | runTest(test1);
|
239 | runTest(test2);
|
240 | runTest(test3);
|
241 | runTest(test4);
|
242 | runTest(test5);
|
243 | runTest(test6);
|
244 |
|
245 | return 0;
|
246 | }
|