UNPKG

6.56 kBtext/x-cView Raw
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
11CHAR_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
18CHAR_INFO ci(wchar_t ch) {
19 return ci(ch, 7);
20}
21
22CHAR_INFO ci() {
23 return ci(L' ');
24}
25
26bool operator==(SMALL_RECT x, SMALL_RECT y) {
27 return !memcmp(&x, &y, sizeof(x));
28}
29
30SMALL_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
38static 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
50static 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
56static 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
66static 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
76static 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
85std::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
97std::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); // With double-width chars, we can read fewer than `size`.
107 return data;
108}
109
110static 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
116static void dumpCharsAt(const COORD pt, int size) {
117 for (wchar_t ch : readCharsAt(pt, size)) {
118 printf("%04X\n", ch);
119 }
120}
121
122static COORD getCursorPos() {
123 CONSOLE_SCREEN_BUFFER_INFO info = { sizeof(info) };
124 assert(GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &info));
125 return info.dwCursorPosition;
126}
127
128static void test1() {
129 // We write "䀀䀀", then write "䀁" in the middle of the two. The second
130 // write turns the first and last cells into spaces. The LEADING/TRAILING
131 // flags retain consistency.
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
141static void test2() {
142 // Like `test1`, but use a lower-level API to do the write. Consistency is
143 // preserved here too -- the first and last cells are replaced with spaces.
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
153static void test3() {
154 // However, the lower-level API can break the LEADING/TRAILING invariant
155 // explicitly:
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
167static void test4() {
168 // It is possible for the two cells of a double-width character to have two
169 // colors.
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
178static void test5() {
179 // WriteConsoleOutputAttribute doesn't seem to affect the LEADING/TRAILING
180 // flags.
181 printf("test5 - WriteConsoleOutputAttribute cannot affect LEADING/TRAILING\n");
182
183 // Trying to clear the flags doesn't work...
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 // ... and trying to add them also doesn't work.
191 writeCharsAt({0,1}, {'A', ' '});
192 writeAttrsAt({0,1}, {0x107, 0x207});
193 printf("\n");
194 dump({0,1}, {2,1});
195}
196
197static void test6() {
198 // The cursor position may be on either cell of a double-width character.
199 // Visually, the cursor appears under both cells, regardless of which
200 // specific one has the cursor.
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
218static void runTest(void (&test)()) {
219 system("cls");
220 setCursorPos(0, 14);
221 test();
222 system("pause");
223}
224
225int 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}