atl: Add support for the registrar parameter of AtlModuleUpdateRegistryFromResourceD.
[wine] / dlls / kernel32 / tests / console.c
1 /*
2  * Unit tests for console API
3  *
4  * Copyright (c) 2003,2004 Eric Pouech
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include "wine/test.h"
22 #include <windows.h>
23 #include <stdio.h>
24
25 /* DEFAULT_ATTRIB is used for all initial filling of the console.
26  * all modifications are made with TEST_ATTRIB so that we could check
27  * what has to be modified or not
28  */
29 #define TEST_ATTRIB    (BACKGROUND_BLUE | FOREGROUND_GREEN)
30 #define DEFAULT_ATTRIB (FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED)
31 /* when filling the screen with non-blank chars, this macro defines
32  * what character should be at position 'c'
33  */
34 #define CONTENT(c)    ('A' + (((c).Y * 17 + (c).X) % 23))
35
36 #define okCURSOR(hCon, c) do { \
37   CONSOLE_SCREEN_BUFFER_INFO __sbi; \
38   BOOL expect = GetConsoleScreenBufferInfo((hCon), &__sbi) && \
39                 __sbi.dwCursorPosition.X == (c).X && __sbi.dwCursorPosition.Y == (c).Y; \
40   ok(expect, "Expected cursor at (%d,%d), got (%d,%d)\n", \
41      (c).X, (c).Y, __sbi.dwCursorPosition.X, __sbi.dwCursorPosition.Y); \
42 } while (0)
43
44 #define okCHAR(hCon, c, ch, attr) do { \
45   char __ch; WORD __attr; DWORD __len; BOOL expect; \
46   expect = ReadConsoleOutputCharacter((hCon), &__ch, 1, (c), &__len) == 1 && __len == 1 && __ch == (ch); \
47   ok(expect, "At (%d,%d): expecting char '%c'/%02x got '%c'/%02x\n", (c).X, (c).Y, (ch), (ch), __ch, __ch); \
48   expect = ReadConsoleOutputAttribute((hCon), &__attr, 1, (c), &__len) == 1 && __len == 1 && __attr == (attr); \
49   ok(expect, "At (%d,%d): expecting attr %04x got %04x\n", (c).X, (c).Y, (attr), __attr); \
50 } while (0)
51
52 /* FIXME: this could be optimized on a speed point of view */
53 static void resetContent(HANDLE hCon, COORD sbSize, BOOL content)
54 {
55     COORD       c;
56     WORD        attr = DEFAULT_ATTRIB;
57     char        ch;
58     DWORD       len;
59
60     for (c.X = 0; c.X < sbSize.X; c.X++)
61     {
62         for (c.Y = 0; c.Y < sbSize.Y; c.Y++)
63         {
64             ch = (content) ? CONTENT(c) : ' ';
65             WriteConsoleOutputAttribute(hCon, &attr, 1, c, &len);
66             WriteConsoleOutputCharacterA(hCon, &ch, 1, c, &len);
67         }
68     }
69 }
70
71 static void testCursor(HANDLE hCon, COORD sbSize)
72 {
73     COORD               c;
74
75     c.X = c.Y = 0;
76     ok(SetConsoleCursorPosition(0, c) == 0, "No handle\n");
77     ok(GetLastError() == ERROR_INVALID_HANDLE, "GetLastError: expecting %u got %u\n",
78        ERROR_INVALID_HANDLE, GetLastError());
79
80     c.X = c.Y = 0;
81     ok(SetConsoleCursorPosition(hCon, c) != 0, "Cursor in upper-left\n");
82     okCURSOR(hCon, c);
83
84     c.X = sbSize.X - 1;
85     c.Y = sbSize.Y - 1;
86     ok(SetConsoleCursorPosition(hCon, c) != 0, "Cursor in lower-right\n");
87     okCURSOR(hCon, c);
88
89     c.X = sbSize.X;
90     c.Y = sbSize.Y - 1;
91     ok(SetConsoleCursorPosition(hCon, c) == 0, "Cursor is outside\n");
92     ok(GetLastError() == ERROR_INVALID_PARAMETER, "GetLastError: expecting %u got %u\n",
93        ERROR_INVALID_PARAMETER, GetLastError());
94
95     c.X = sbSize.X - 1;
96     c.Y = sbSize.Y;
97     ok(SetConsoleCursorPosition(hCon, c) == 0, "Cursor is outside\n");
98     ok(GetLastError() == ERROR_INVALID_PARAMETER, "GetLastError: expecting %u got %u\n",
99        ERROR_INVALID_PARAMETER, GetLastError());
100
101     c.X = -1;
102     c.Y = 0;
103     ok(SetConsoleCursorPosition(hCon, c) == 0, "Cursor is outside\n");
104     ok(GetLastError() == ERROR_INVALID_PARAMETER, "GetLastError: expecting %u got %u\n",
105        ERROR_INVALID_PARAMETER, GetLastError());
106
107     c.X = 0;
108     c.Y = -1;
109     ok(SetConsoleCursorPosition(hCon, c) == 0, "Cursor is outside\n");
110     ok(GetLastError() == ERROR_INVALID_PARAMETER, "GetLastError: expecting %u got %u\n",
111        ERROR_INVALID_PARAMETER, GetLastError());
112 }
113
114 static void testWriteSimple(HANDLE hCon, COORD sbSize)
115 {
116     COORD               c;
117     DWORD               len;
118     const char*         mytest = "abcdefg";
119     const int   mylen = strlen(mytest);
120
121     /* single line write */
122     c.X = c.Y = 0;
123     ok(SetConsoleCursorPosition(hCon, c) != 0, "Cursor in upper-left\n");
124
125     ok(WriteConsole(hCon, mytest, mylen, &len, NULL) != 0 && len == mylen, "WriteConsole\n");
126     c.Y = 0;
127     for (c.X = 0; c.X < mylen; c.X++)
128     {
129         okCHAR(hCon, c, mytest[c.X], TEST_ATTRIB);
130     }
131
132     okCURSOR(hCon, c);
133     okCHAR(hCon, c, ' ', DEFAULT_ATTRIB);
134 }
135
136 static void testWriteNotWrappedNotProcessed(HANDLE hCon, COORD sbSize)
137 {
138     COORD               c;
139     DWORD               len, mode;
140     const char*         mytest = "123";
141     const int           mylen = strlen(mytest);
142     int                 ret;
143     int                 p;
144
145     ok(GetConsoleMode(hCon, &mode) && SetConsoleMode(hCon, mode & ~(ENABLE_PROCESSED_OUTPUT|ENABLE_WRAP_AT_EOL_OUTPUT)),
146        "clearing wrap at EOL & processed output\n");
147
148     /* write line, wrapping disabled, buffer exceeds sb width */
149     c.X = sbSize.X - 3; c.Y = 0;
150     ok(SetConsoleCursorPosition(hCon, c) != 0, "Cursor in upper-left-3\n");
151
152     ret = WriteConsole(hCon, mytest, mylen, &len, NULL);
153     ok(ret != 0 && len == mylen, "Couldn't write, ret = %d, len = %d\n", ret, len);
154     c.Y = 0;
155     for (p = mylen - 3; p < mylen; p++)
156     {
157         c.X = sbSize.X - 3 + p % 3;
158         okCHAR(hCon, c, mytest[p], TEST_ATTRIB);
159     }
160
161     c.X = 0; c.Y = 1;
162     okCHAR(hCon, c, ' ', DEFAULT_ATTRIB);
163
164     p = sbSize.X - 3 + mylen % 3;
165     c.X = p; c.Y = 0;
166
167     /* write line, wrapping disabled, strings end on end of line */
168     c.X = sbSize.X - mylen; c.Y = 0;
169     ok(SetConsoleCursorPosition(hCon, c) != 0, "Cursor in upper-left-3\n");
170
171     ok(WriteConsole(hCon, mytest, mylen, &len, NULL) != 0 && len == mylen, "WriteConsole\n");
172 }
173
174 static void testWriteNotWrappedProcessed(HANDLE hCon, COORD sbSize)
175 {
176     COORD               c;
177     DWORD               len, mode;
178     const char*         mytest = "abcd\nf\tg";
179     const int   mylen = strlen(mytest);
180     const int   mylen2 = strchr(mytest, '\n') - mytest;
181     int                 p;
182
183     ok(GetConsoleMode(hCon, &mode) && SetConsoleMode(hCon, (mode | ENABLE_PROCESSED_OUTPUT) & ~ENABLE_WRAP_AT_EOL_OUTPUT),
184        "clearing wrap at EOL & setting processed output\n");
185
186     /* write line, wrapping disabled, buffer exceeds sb width */
187     c.X = sbSize.X - 5; c.Y = 0;
188     ok(SetConsoleCursorPosition(hCon, c) != 0, "Cursor in upper-left-5\n");
189
190     ok(WriteConsole(hCon, mytest, mylen, &len, NULL) != 0 && len == mylen, "WriteConsole\n");
191     c.Y = 0;
192     for (c.X = sbSize.X - 5; c.X < sbSize.X - 1; c.X++)
193     {
194         okCHAR(hCon, c, mytest[c.X - sbSize.X + 5], TEST_ATTRIB);
195     }
196     okCHAR(hCon, c, ' ', DEFAULT_ATTRIB);
197
198     c.X = 0; c.Y++;
199     okCHAR(hCon, c, mytest[5], TEST_ATTRIB);
200     for (c.X = 1; c.X < 8; c.X++)
201         okCHAR(hCon, c, ' ', TEST_ATTRIB);
202     okCHAR(hCon, c, mytest[7], TEST_ATTRIB);
203     c.X++;
204     okCHAR(hCon, c, ' ', DEFAULT_ATTRIB);
205
206     okCURSOR(hCon, c);
207
208     /* write line, wrapping disabled, strings end on end of line */
209     c.X = sbSize.X - 4; c.Y = 0;
210     ok(SetConsoleCursorPosition(hCon, c) != 0, "Cursor in upper-left-4\n");
211
212     ok(WriteConsole(hCon, mytest, mylen, &len, NULL) != 0 && len == mylen, "WriteConsole\n");
213     c.Y = 0;
214     for (c.X = sbSize.X - 4; c.X < sbSize.X; c.X++)
215     {
216         okCHAR(hCon, c, mytest[c.X - sbSize.X + 4], TEST_ATTRIB);
217     }
218     c.X = 0; c.Y++;
219     okCHAR(hCon, c, mytest[5], TEST_ATTRIB);
220     for (c.X = 1; c.X < 8; c.X++)
221         okCHAR(hCon, c, ' ', TEST_ATTRIB);
222     okCHAR(hCon, c, mytest[7], TEST_ATTRIB);
223     c.X++;
224     okCHAR(hCon, c, ' ', DEFAULT_ATTRIB);
225
226     okCURSOR(hCon, c);
227
228     /* write line, wrapping disabled, strings end after end of line */
229     c.X = sbSize.X - 3; c.Y = 0;
230     ok(SetConsoleCursorPosition(hCon, c) != 0, "Cursor in upper-left-4\n");
231
232     ok(WriteConsole(hCon, mytest, mylen, &len, NULL) != 0 && len == mylen, "WriteConsole\n");
233     c.Y = 0;
234     for (p = mylen2 - 3; p < mylen2; p++)
235     {
236         c.X = sbSize.X - 3 + p % 3;
237         okCHAR(hCon, c, mytest[p], TEST_ATTRIB);
238     }
239     c.X = 0; c.Y = 1;
240     okCHAR(hCon, c, mytest[5], TEST_ATTRIB);
241     for (c.X = 1; c.X < 8; c.X++)
242         okCHAR(hCon, c, ' ', TEST_ATTRIB);
243     okCHAR(hCon, c, mytest[7], TEST_ATTRIB);
244     c.X++;
245     okCHAR(hCon, c, ' ', DEFAULT_ATTRIB);
246
247     okCURSOR(hCon, c);
248 }
249
250 static void testWriteWrappedNotProcessed(HANDLE hCon, COORD sbSize)
251 {
252     COORD               c;
253     DWORD               len, mode;
254     const char*         mytest = "abcd\nf\tg";
255     const int   mylen = strlen(mytest);
256     int                 p;
257
258     ok(GetConsoleMode(hCon, &mode) && SetConsoleMode(hCon,(mode | ENABLE_WRAP_AT_EOL_OUTPUT) & ~(ENABLE_PROCESSED_OUTPUT)),
259        "setting wrap at EOL & clearing processed output\n");
260
261     /* write line, wrapping enabled, buffer doesn't exceed sb width */
262     c.X = sbSize.X - 9; c.Y = 0;
263     ok(SetConsoleCursorPosition(hCon, c) != 0, "Cursor in upper-left-9\n");
264
265     ok(WriteConsole(hCon, mytest, mylen, &len, NULL) != 0 && len == mylen, "WriteConsole\n");
266     c.Y = 0;
267     for (p = 0; p < mylen; p++)
268     {
269         c.X = sbSize.X - 9 + p;
270         okCHAR(hCon, c, mytest[p], TEST_ATTRIB);
271     }
272     c.X = sbSize.X - 9 + mylen;
273     okCHAR(hCon, c, ' ', DEFAULT_ATTRIB);
274     c.X = 0; c.Y = 1;
275     okCHAR(hCon, c, ' ', DEFAULT_ATTRIB);
276
277     /* write line, wrapping enabled, buffer does exceed sb width */
278     c.X = sbSize.X - 3; c.Y = 0;
279     ok(SetConsoleCursorPosition(hCon, c) != 0, "Cursor in upper-left-3\n");
280
281     c.Y = 1;
282     c.X = mylen - 3;
283     okCHAR(hCon, c, ' ', DEFAULT_ATTRIB);
284 }
285
286 static void testWriteWrappedProcessed(HANDLE hCon, COORD sbSize)
287 {
288     COORD               c;
289     DWORD               len, mode;
290     const char*         mytest = "abcd\nf\tg";
291     const int   mylen = strlen(mytest);
292     int                 p;
293
294     ok(GetConsoleMode(hCon, &mode) && SetConsoleMode(hCon, mode | (ENABLE_WRAP_AT_EOL_OUTPUT|ENABLE_PROCESSED_OUTPUT)),
295        "setting wrap at EOL & processed output\n");
296
297     /* write line, wrapping enabled, buffer doesn't exceed sb width */
298     c.X = sbSize.X - 9; c.Y = 0;
299     ok(SetConsoleCursorPosition(hCon, c) != 0, "Cursor in upper-left-9\n");
300
301     ok(WriteConsole(hCon, mytest, mylen, &len, NULL) != 0 && len == mylen, "WriteConsole\n");
302     for (p = 0; p < 4; p++)
303     {
304         c.X = sbSize.X - 9 + p;
305         okCHAR(hCon, c, mytest[p], TEST_ATTRIB);
306     }
307     c.X = sbSize.X - 9 + p;
308     okCHAR(hCon, c, ' ', DEFAULT_ATTRIB);
309     c.X = 0; c.Y++;
310     okCHAR(hCon, c, mytest[5], TEST_ATTRIB);
311     for (c.X = 1; c.X < 8; c.X++)
312         okCHAR(hCon, c, ' ', TEST_ATTRIB);
313     okCHAR(hCon, c, mytest[7], TEST_ATTRIB);
314     c.X++;
315     okCHAR(hCon, c, ' ', DEFAULT_ATTRIB);
316     okCURSOR(hCon, c);
317
318     /* write line, wrapping enabled, buffer does exceed sb width */
319     c.X = sbSize.X - 3; c.Y = 2;
320     ok(SetConsoleCursorPosition(hCon, c) != 0, "Cursor in upper-left-3\n");
321
322     ok(WriteConsole(hCon, mytest, mylen, &len, NULL) != 0 && len == mylen, "WriteConsole\n");
323     for (p = 0; p < 3; p++)
324     {
325         c.X = sbSize.X - 3 + p;
326         okCHAR(hCon, c, mytest[p], TEST_ATTRIB);
327     }
328     c.X = 0; c.Y++;
329     okCHAR(hCon, c, mytest[3], TEST_ATTRIB);
330     c.X++;
331     okCHAR(hCon, c, ' ', DEFAULT_ATTRIB);
332
333     c.X = 0; c.Y++;
334     okCHAR(hCon, c, mytest[5], TEST_ATTRIB);
335     for (c.X = 1; c.X < 8; c.X++)
336         okCHAR(hCon, c, ' ', TEST_ATTRIB);
337     okCHAR(hCon, c, mytest[7], TEST_ATTRIB);
338     c.X++;
339     okCHAR(hCon, c, ' ', DEFAULT_ATTRIB);
340     okCURSOR(hCon, c);
341 }
342
343 static void testWrite(HANDLE hCon, COORD sbSize)
344 {
345     /* FIXME: should in fact insure that the sb is at least 10 character wide */
346     ok(SetConsoleTextAttribute(hCon, TEST_ATTRIB), "Setting default text color\n");
347     resetContent(hCon, sbSize, FALSE);
348     testWriteSimple(hCon, sbSize);
349     resetContent(hCon, sbSize, FALSE);
350     testWriteNotWrappedNotProcessed(hCon, sbSize);
351     resetContent(hCon, sbSize, FALSE);
352     testWriteNotWrappedProcessed(hCon, sbSize);
353     resetContent(hCon, sbSize, FALSE);
354     testWriteWrappedNotProcessed(hCon, sbSize);
355     resetContent(hCon, sbSize, FALSE);
356     testWriteWrappedProcessed(hCon, sbSize);
357 }
358
359 static void testScroll(HANDLE hCon, COORD sbSize)
360 {
361     SMALL_RECT  scroll, clip;
362     COORD       dst, c, tc;
363     CHAR_INFO   ci;
364
365 #define W 11
366 #define H 7
367
368 #define IN_SRECT(r,c) ((r).Left <= (c).X && (c).X <= (r).Right && (r).Top <= (c).Y && (c).Y <= (r).Bottom)
369 #define IN_SRECT2(r,d,c) ((d).X <= (c).X && (c).X <= (d).X + (r).Right - (r).Left && (d).Y <= (c).Y && (c).Y <= (d).Y + (r).Bottom - (r).Top)
370
371     /* no clipping, src & dst rect don't overlap */
372     resetContent(hCon, sbSize, TRUE);
373
374     scroll.Left = 0;
375     scroll.Right = W - 1;
376     scroll.Top = 0;
377     scroll.Bottom = H - 1;
378     dst.X = W + 3;
379     dst.Y = H + 3;
380     ci.Char.UnicodeChar = '#';
381     ci.Attributes = TEST_ATTRIB;
382
383     clip.Left = 0;
384     clip.Right = sbSize.X - 1;
385     clip.Top = 0;
386     clip.Bottom = sbSize.Y - 1;
387
388     ok(ScrollConsoleScreenBuffer(hCon, &scroll, NULL, dst, &ci), "Scrolling SB\n");
389
390     for (c.Y = 0; c.Y < sbSize.Y; c.Y++)
391     {
392         for (c.X = 0; c.X < sbSize.X; c.X++)
393         {
394             if (IN_SRECT2(scroll, dst, c) && IN_SRECT(clip, c))
395             {
396                 tc.X = c.X - dst.X;
397                 tc.Y = c.Y - dst.Y;
398                 okCHAR(hCon, c, CONTENT(tc), DEFAULT_ATTRIB);
399             }
400             else if (IN_SRECT(scroll, c) && IN_SRECT(clip, c))
401                 okCHAR(hCon, c, '#', TEST_ATTRIB);
402             else okCHAR(hCon, c, CONTENT(c), DEFAULT_ATTRIB);
403         }
404     }
405
406     /* no clipping, src & dst rect do overlap */
407     resetContent(hCon, sbSize, TRUE);
408
409     scroll.Left = 0;
410     scroll.Right = W - 1;
411     scroll.Top = 0;
412     scroll.Bottom = H - 1;
413     dst.X = W /2;
414     dst.Y = H / 2;
415     ci.Char.UnicodeChar = '#';
416     ci.Attributes = TEST_ATTRIB;
417
418     clip.Left = 0;
419     clip.Right = sbSize.X - 1;
420     clip.Top = 0;
421     clip.Bottom = sbSize.Y - 1;
422
423     ok(ScrollConsoleScreenBuffer(hCon, &scroll, NULL, dst, &ci), "Scrolling SB\n");
424
425     for (c.Y = 0; c.Y < sbSize.Y; c.Y++)
426     {
427         for (c.X = 0; c.X < sbSize.X; c.X++)
428         {
429             if (dst.X <= c.X && c.X < dst.X + W && dst.Y <= c.Y && c.Y < dst.Y + H)
430             {
431                 tc.X = c.X - dst.X;
432                 tc.Y = c.Y - dst.Y;
433                 okCHAR(hCon, c, CONTENT(tc), DEFAULT_ATTRIB);
434             }
435             else if (c.X < W && c.Y < H) okCHAR(hCon, c, '#', TEST_ATTRIB);
436             else okCHAR(hCon, c, CONTENT(c), DEFAULT_ATTRIB);
437         }
438     }
439
440     /* clipping, src & dst rect don't overlap */
441     resetContent(hCon, sbSize, TRUE);
442
443     scroll.Left = 0;
444     scroll.Right = W - 1;
445     scroll.Top = 0;
446     scroll.Bottom = H - 1;
447     dst.X = W + 3;
448     dst.Y = H + 3;
449     ci.Char.UnicodeChar = '#';
450     ci.Attributes = TEST_ATTRIB;
451
452     clip.Left = W / 2;
453     clip.Right = min(W + W / 2, sbSize.X - 1);
454     clip.Top = H / 2;
455     clip.Bottom = min(H + H / 2, sbSize.Y - 1);
456
457     ok(ScrollConsoleScreenBuffer(hCon, &scroll, &clip, dst, &ci), "Scrolling SB\n");
458
459     for (c.Y = 0; c.Y < sbSize.Y; c.Y++)
460     {
461         for (c.X = 0; c.X < sbSize.X; c.X++)
462         {
463             if (IN_SRECT2(scroll, dst, c) && IN_SRECT(clip, c))
464             {
465                 tc.X = c.X - dst.X;
466                 tc.Y = c.Y - dst.Y;
467                 okCHAR(hCon, c, CONTENT(tc), DEFAULT_ATTRIB);
468             }
469             else if (IN_SRECT(scroll, c) && IN_SRECT(clip, c))
470                 okCHAR(hCon, c, '#', TEST_ATTRIB);
471             else okCHAR(hCon, c, CONTENT(c), DEFAULT_ATTRIB);
472         }
473     }
474
475     /* clipping, src & dst rect do overlap */
476     resetContent(hCon, sbSize, TRUE);
477
478     scroll.Left = 0;
479     scroll.Right = W - 1;
480     scroll.Top = 0;
481     scroll.Bottom = H - 1;
482     dst.X = W / 2 - 3;
483     dst.Y = H / 2 - 3;
484     ci.Char.UnicodeChar = '#';
485     ci.Attributes = TEST_ATTRIB;
486
487     clip.Left = W / 2;
488     clip.Right = min(W + W / 2, sbSize.X - 1);
489     clip.Top = H / 2;
490     clip.Bottom = min(H + H / 2, sbSize.Y - 1);
491
492     ok(ScrollConsoleScreenBuffer(hCon, &scroll, &clip, dst, &ci), "Scrolling SB\n");
493
494     for (c.Y = 0; c.Y < sbSize.Y; c.Y++)
495     {
496         for (c.X = 0; c.X < sbSize.X; c.X++)
497         {
498             if (IN_SRECT2(scroll, dst, c) && IN_SRECT(clip, c))
499             {
500                 tc.X = c.X - dst.X;
501                 tc.Y = c.Y - dst.Y;
502                 okCHAR(hCon, c, CONTENT(tc), DEFAULT_ATTRIB);
503             }
504             else if (IN_SRECT(scroll, c) && IN_SRECT(clip, c))
505                 okCHAR(hCon, c, '#', TEST_ATTRIB);
506             else okCHAR(hCon, c, CONTENT(c), DEFAULT_ATTRIB);
507         }
508     }
509 }
510
511 static int mch_count;
512 /* we need the event as Wine console event generation isn't synchronous
513  * (ie GenerateConsoleCtrlEvent returns before all ctrl-handlers in all
514  * processes have been called).
515  */
516 static HANDLE mch_event;
517 static BOOL WINAPI mch(DWORD event)
518 {
519     mch_count++;
520     SetEvent(mch_event);
521     return TRUE;
522 }
523
524 static void testCtrlHandler(void)
525 {
526     ok(!SetConsoleCtrlHandler(mch, FALSE), "Shouldn't succeed\n");
527     ok(GetLastError() == ERROR_INVALID_PARAMETER, "Bad error %u\n", GetLastError());
528     ok(SetConsoleCtrlHandler(mch, TRUE), "Couldn't set handler\n");
529     /* wine requires the event for the test, as we cannot insure, so far, that event
530      * are processed synchronously in GenerateConsoleCtrlEvent()
531      */
532     mch_event = CreateEventA(NULL, TRUE, FALSE, NULL);
533     mch_count = 0;
534     ok(GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0), "Couldn't send ctrl-c event\n");
535     /* FIXME: it isn't synchronous on wine but it can still happen before we test */
536     if (0) ok(mch_count == 1, "Event isn't synchronous\n");
537     ok(WaitForSingleObject(mch_event, 3000) == WAIT_OBJECT_0, "event sending didn't work\n");
538     CloseHandle(mch_event);
539
540     /* Turning off ctrl-c handling doesn't work on win9x such way ... */
541     ok(SetConsoleCtrlHandler(NULL, TRUE), "Couldn't turn off ctrl-c handling\n");
542     mch_event = CreateEventA(NULL, TRUE, FALSE, NULL);
543     mch_count = 0;
544     if(!(GetVersion() & 0x80000000))
545         /* ... and next line leads to an unhandled exception on 9x.  Avoid it on 9x. */
546         ok(GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0), "Couldn't send ctrl-c event\n");
547     ok(WaitForSingleObject(mch_event, 3000) == WAIT_TIMEOUT && mch_count == 0, "Event shouldn't have been sent\n");
548     CloseHandle(mch_event);
549     ok(SetConsoleCtrlHandler(mch, FALSE), "Couldn't remove handler\n");
550     ok(!SetConsoleCtrlHandler(mch, FALSE), "Shouldn't succeed\n");
551     ok(GetLastError() == ERROR_INVALID_PARAMETER, "Bad error %u\n", GetLastError());
552 }
553
554 START_TEST(console)
555 {
556     HANDLE hConIn, hConOut;
557     BOOL ret;
558     CONSOLE_SCREEN_BUFFER_INFO  sbi;
559
560     /* be sure we have a clean console (and that's our own)
561      * FIXME: this will make the test fail (currently) if we don't run
562      * under X11
563      * Another solution would be to rerun the test under wineconsole with
564      * the curses backend
565      */
566
567     /* first, we detach and open a fresh console to play with */
568     FreeConsole();
569     ok(AllocConsole(), "Couldn't alloc console\n");
570     hConIn = CreateFileA("CONIN$", GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0);
571     hConOut = CreateFileA("CONOUT$", GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0);
572
573     /* now verify everything's ok */
574     ok(hConIn != INVALID_HANDLE_VALUE, "Opening ConIn\n");
575     ok(hConOut != INVALID_HANDLE_VALUE, "Opening ConOut\n");
576
577     ok(ret = GetConsoleScreenBufferInfo(hConOut, &sbi), "Getting sb info\n");
578     if (!ret) return;
579
580     /* Non interactive tests */
581     testCursor(hConOut, sbi.dwSize);
582     /* will test wrapped (on/off) & processed (on/off) strings output */
583     testWrite(hConOut, sbi.dwSize);
584     /* will test line scrolling at the bottom of the screen */
585     /* testBottomScroll(); */
586     /* will test all the scrolling operations */
587     testScroll(hConOut, sbi.dwSize);
588     /* will test sb creation / modification... */
589     /* testScreenBuffer() */
590     testCtrlHandler();
591     /* still to be done: access rights & access on objects */
592 }