Fixed (finally) ScrollConsoleScreenBuffer implementation.
[wine] / dlls / kernel / 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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 %lu\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 %lu\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 %lu\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 %lu\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 %lu\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     char*               mytest;
141     int                 mylen;
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     mytest = "123";
153
154     mylen = strlen(mytest);
155
156     ret = WriteConsole(hCon, mytest, mylen, &len, NULL);
157     ok(ret != 0 && len == mylen, "Couldn't write, ret = %d, len = %ld\n", ret, len);
158     c.Y = 0;
159     for (p = mylen - 3; p < mylen; p++)
160     {
161         c.X = sbSize.X - 3 + p % 3;
162         okCHAR(hCon, c, mytest[p], TEST_ATTRIB);
163     }
164
165     c.X = 0; c.Y = 1;
166     okCHAR(hCon, c, ' ', DEFAULT_ATTRIB);
167
168     p = sbSize.X - 3 + mylen % 3;
169     c.X = p; c.Y = 0;
170
171     /* write line, wrapping disabled, strings end on end of line */
172     c.X = sbSize.X - mylen; c.Y = 0;
173     ok(SetConsoleCursorPosition(hCon, c) != 0, "Cursor in upper-left-3\n");
174
175     ok(WriteConsole(hCon, mytest, mylen, &len, NULL) != 0 && len == mylen, "WriteConsole\n");
176 }
177
178 static void testWriteNotWrappedProcessed(HANDLE hCon, COORD sbSize)
179 {
180     COORD               c;
181     DWORD               len, mode;
182     const char*         mytest = "abcd\nf\tg";
183     const int   mylen = strlen(mytest);
184     const int   mylen2 = strchr(mytest, '\n') - mytest;
185     int                 p;
186
187     ok(GetConsoleMode(hCon, &mode) && SetConsoleMode(hCon, (mode | ENABLE_PROCESSED_OUTPUT) & ~ENABLE_WRAP_AT_EOL_OUTPUT),
188        "clearing wrap at EOL & setting processed output\n");
189
190     /* write line, wrapping disabled, buffer exceeds sb width */
191     c.X = sbSize.X - 5; c.Y = 0;
192     ok(SetConsoleCursorPosition(hCon, c) != 0, "Cursor in upper-left-5\n");
193
194     ok(WriteConsole(hCon, mytest, mylen, &len, NULL) != 0 && len == mylen, "WriteConsole\n");
195     c.Y = 0;
196     for (c.X = sbSize.X - 5; c.X < sbSize.X - 1; c.X++)
197     {
198         okCHAR(hCon, c, mytest[c.X - sbSize.X + 5], TEST_ATTRIB);
199     }
200     okCHAR(hCon, c, ' ', DEFAULT_ATTRIB);
201
202     c.X = 0; c.Y++;
203     okCHAR(hCon, c, mytest[5], TEST_ATTRIB);
204     for (c.X = 1; c.X < 8; c.X++)
205         okCHAR(hCon, c, ' ', TEST_ATTRIB);
206     okCHAR(hCon, c, mytest[7], TEST_ATTRIB);
207     c.X++;
208     okCHAR(hCon, c, ' ', DEFAULT_ATTRIB);
209
210     okCURSOR(hCon, c);
211
212     /* write line, wrapping disabled, strings end on end of line */
213     c.X = sbSize.X - 4; c.Y = 0;
214     ok(SetConsoleCursorPosition(hCon, c) != 0, "Cursor in upper-left-4\n");
215
216     ok(WriteConsole(hCon, mytest, mylen, &len, NULL) != 0 && len == mylen, "WriteConsole\n");
217     c.Y = 0;
218     for (c.X = sbSize.X - 4; c.X < sbSize.X; c.X++)
219     {
220         okCHAR(hCon, c, mytest[c.X - sbSize.X + 4], TEST_ATTRIB);
221     }
222     c.X = 0; c.Y++;
223     okCHAR(hCon, c, mytest[5], TEST_ATTRIB);
224     for (c.X = 1; c.X < 8; c.X++)
225         okCHAR(hCon, c, ' ', TEST_ATTRIB);
226     okCHAR(hCon, c, mytest[7], TEST_ATTRIB);
227     c.X++;
228     okCHAR(hCon, c, ' ', DEFAULT_ATTRIB);
229
230     okCURSOR(hCon, c);
231
232     /* write line, wrapping disabled, strings end after end of line */
233     c.X = sbSize.X - 3; c.Y = 0;
234     ok(SetConsoleCursorPosition(hCon, c) != 0, "Cursor in upper-left-4\n");
235
236     ok(WriteConsole(hCon, mytest, mylen, &len, NULL) != 0 && len == mylen, "WriteConsole\n");
237     c.Y = 0;
238     for (p = mylen2 - 3; p < mylen2; p++)
239     {
240         c.X = sbSize.X - 3 + p % 3;
241         okCHAR(hCon, c, mytest[p], TEST_ATTRIB);
242     }
243     c.X = 0; c.Y = 1;
244     okCHAR(hCon, c, mytest[5], TEST_ATTRIB);
245     for (c.X = 1; c.X < 8; c.X++)
246         okCHAR(hCon, c, ' ', TEST_ATTRIB);
247     okCHAR(hCon, c, mytest[7], TEST_ATTRIB);
248     c.X++;
249     okCHAR(hCon, c, ' ', DEFAULT_ATTRIB);
250
251     okCURSOR(hCon, c);
252 }
253
254 static void testWriteWrappedNotProcessed(HANDLE hCon, COORD sbSize)
255 {
256     COORD               c;
257     DWORD               len, mode;
258     const char*         mytest = "abcd\nf\tg";
259     const int   mylen = strlen(mytest);
260     int                 p;
261
262     ok(GetConsoleMode(hCon, &mode) && SetConsoleMode(hCon,(mode | ENABLE_WRAP_AT_EOL_OUTPUT) & ~(ENABLE_PROCESSED_OUTPUT)),
263        "setting wrap at EOL & clearing processed output\n");
264
265     /* write line, wrapping enabled, buffer doesn't exceed sb width */
266     c.X = sbSize.X - 9; c.Y = 0;
267     ok(SetConsoleCursorPosition(hCon, c) != 0, "Cursor in upper-left-9\n");
268
269     ok(WriteConsole(hCon, mytest, mylen, &len, NULL) != 0 && len == mylen, "WriteConsole\n");
270     c.Y = 0;
271     for (p = 0; p < mylen; p++)
272     {
273         c.X = sbSize.X - 9 + p;
274         okCHAR(hCon, c, mytest[p], TEST_ATTRIB);
275     }
276     c.X = sbSize.X - 9 + mylen;
277     okCHAR(hCon, c, ' ', DEFAULT_ATTRIB);
278     c.X = 0; c.Y = 1;
279     okCHAR(hCon, c, ' ', DEFAULT_ATTRIB);
280
281     /* write line, wrapping enabled, buffer does exceed sb width */
282     c.X = sbSize.X - 3; c.Y = 0;
283     ok(SetConsoleCursorPosition(hCon, c) != 0, "Cursor in upper-left-3\n");
284
285     c.Y = 1;
286     c.X = mylen - 3;
287     okCHAR(hCon, c, ' ', DEFAULT_ATTRIB);
288 }
289
290 static void testWriteWrappedProcessed(HANDLE hCon, COORD sbSize)
291 {
292     COORD               c;
293     DWORD               len, mode;
294     const char*         mytest = "abcd\nf\tg";
295     const int   mylen = strlen(mytest);
296     int                 p;
297
298     ok(GetConsoleMode(hCon, &mode) && SetConsoleMode(hCon, mode | (ENABLE_WRAP_AT_EOL_OUTPUT|ENABLE_PROCESSED_OUTPUT)),
299        "setting wrap at EOL & processed output\n");
300
301     /* write line, wrapping enabled, buffer doesn't exceed sb width */
302     c.X = sbSize.X - 9; c.Y = 0;
303     ok(SetConsoleCursorPosition(hCon, c) != 0, "Cursor in upper-left-9\n");
304
305     ok(WriteConsole(hCon, mytest, mylen, &len, NULL) != 0 && len == mylen, "WriteConsole\n");
306     for (p = 0; p < 4; p++)
307     {
308         c.X = sbSize.X - 9 + p;
309         okCHAR(hCon, c, mytest[p], TEST_ATTRIB);
310     }
311     c.X = sbSize.X - 9 + p;
312     okCHAR(hCon, c, ' ', DEFAULT_ATTRIB);
313     c.X = 0; c.Y++;
314     okCHAR(hCon, c, mytest[5], TEST_ATTRIB);
315     for (c.X = 1; c.X < 8; c.X++)
316         okCHAR(hCon, c, ' ', TEST_ATTRIB);
317     okCHAR(hCon, c, mytest[7], TEST_ATTRIB);
318     c.X++;
319     okCHAR(hCon, c, ' ', DEFAULT_ATTRIB);
320     okCURSOR(hCon, c);
321
322     /* write line, wrapping enabled, buffer does exceed sb width */
323     c.X = sbSize.X - 3; c.Y = 2;
324     ok(SetConsoleCursorPosition(hCon, c) != 0, "Cursor in upper-left-3\n");
325
326     ok(WriteConsole(hCon, mytest, mylen, &len, NULL) != 0 && len == mylen, "WriteConsole\n");
327     for (p = 0; p < 3; p++)
328     {
329         c.X = sbSize.X - 3 + p;
330         okCHAR(hCon, c, mytest[p], TEST_ATTRIB);
331     }
332     c.X = 0; c.Y++;
333     okCHAR(hCon, c, mytest[3], TEST_ATTRIB);
334     c.X++;
335     okCHAR(hCon, c, ' ', DEFAULT_ATTRIB);
336
337     c.X = 0; c.Y++;
338     okCHAR(hCon, c, mytest[5], TEST_ATTRIB);
339     for (c.X = 1; c.X < 8; c.X++)
340         okCHAR(hCon, c, ' ', TEST_ATTRIB);
341     okCHAR(hCon, c, mytest[7], TEST_ATTRIB);
342     c.X++;
343     okCHAR(hCon, c, ' ', DEFAULT_ATTRIB);
344     okCURSOR(hCon, c);
345 }
346
347 static void testWrite(HANDLE hCon, COORD sbSize)
348 {
349     /* FIXME: should in fact insure that the sb is at least 10 character wide */
350     ok(SetConsoleTextAttribute(hCon, TEST_ATTRIB), "Setting default text color\n");
351     resetContent(hCon, sbSize, FALSE);
352     testWriteSimple(hCon, sbSize);
353     resetContent(hCon, sbSize, FALSE);
354     testWriteNotWrappedNotProcessed(hCon, sbSize);
355     resetContent(hCon, sbSize, FALSE);
356     testWriteNotWrappedProcessed(hCon, sbSize);
357     resetContent(hCon, sbSize, FALSE);
358     testWriteWrappedNotProcessed(hCon, sbSize);
359     resetContent(hCon, sbSize, FALSE);
360     testWriteWrappedProcessed(hCon, sbSize);
361 }
362
363 static void testScroll(HANDLE hCon, COORD sbSize)
364 {
365     SMALL_RECT  scroll, clip;
366     COORD       dst, c, tc;
367     CHAR_INFO   ci;
368
369 #define W 11
370 #define H 7
371
372 #define IN_SRECT(r,c) ((r).Left <= (c).X && (c).X <= (r).Right && (r).Top <= (c).Y && (c).Y <= (r).Bottom)
373 #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)
374
375     /* no clipping, src & dst rect don't overlap */
376     resetContent(hCon, sbSize, TRUE);
377
378     scroll.Left = 0;
379     scroll.Right = W - 1;
380     scroll.Top = 0;
381     scroll.Bottom = H - 1;
382     dst.X = W + 3;
383     dst.Y = H + 3;
384     ci.Char.UnicodeChar = '#';
385     ci.Attributes = TEST_ATTRIB;
386
387     clip.Left = 0;
388     clip.Right = sbSize.X - 1;
389     clip.Top = 0;
390     clip.Bottom = sbSize.Y - 1;
391
392     ok(ScrollConsoleScreenBuffer(hCon, &scroll, NULL, dst, &ci), "Scrolling SB\n");
393
394     for (c.Y = 0; c.Y < sbSize.Y; c.Y++)
395     {
396         for (c.X = 0; c.X < sbSize.X; c.X++)
397         {
398             if (IN_SRECT2(scroll, dst, c) && IN_SRECT(clip, c))
399             {
400                 tc.X = c.X - dst.X;
401                 tc.Y = c.Y - dst.Y;
402                 okCHAR(hCon, c, CONTENT(tc), DEFAULT_ATTRIB);
403             }
404             else if (IN_SRECT(scroll, c) && IN_SRECT(clip, c))
405                 okCHAR(hCon, c, '#', TEST_ATTRIB);
406             else okCHAR(hCon, c, CONTENT(c), DEFAULT_ATTRIB);
407         }
408     }
409
410     /* no clipping, src & dst rect do overlap */
411     resetContent(hCon, sbSize, TRUE);
412
413     scroll.Left = 0;
414     scroll.Right = W - 1;
415     scroll.Top = 0;
416     scroll.Bottom = H - 1;
417     dst.X = W /2;
418     dst.Y = H / 2;
419     ci.Char.UnicodeChar = '#';
420     ci.Attributes = TEST_ATTRIB;
421
422     clip.Left = 0;
423     clip.Right = sbSize.X - 1;
424     clip.Top = 0;
425     clip.Bottom = sbSize.Y - 1;
426
427     ok(ScrollConsoleScreenBuffer(hCon, &scroll, NULL, dst, &ci), "Scrolling SB\n");
428
429     for (c.Y = 0; c.Y < sbSize.Y; c.Y++)
430     {
431         for (c.X = 0; c.X < sbSize.X; c.X++)
432         {
433             if (dst.X <= c.X && c.X < dst.X + W && dst.Y <= c.Y && c.Y < dst.Y + H)
434             {
435                 tc.X = c.X - dst.X;
436                 tc.Y = c.Y - dst.Y;
437                 okCHAR(hCon, c, CONTENT(tc), DEFAULT_ATTRIB);
438             }
439             else if (c.X < W && c.Y < H) okCHAR(hCon, c, '#', TEST_ATTRIB);
440             else okCHAR(hCon, c, CONTENT(c), DEFAULT_ATTRIB);
441         }
442     }
443
444     /* clipping, src & dst rect don't overlap */
445     resetContent(hCon, sbSize, TRUE);
446
447     scroll.Left = 0;
448     scroll.Right = W - 1;
449     scroll.Top = 0;
450     scroll.Bottom = H - 1;
451     dst.X = W + 3;
452     dst.Y = H + 3;
453     ci.Char.UnicodeChar = '#';
454     ci.Attributes = TEST_ATTRIB;
455
456     clip.Left = W / 2;
457     clip.Right = min(W + W / 2, sbSize.X - 1);
458     clip.Top = H / 2;
459     clip.Bottom = min(H + H / 2, sbSize.Y - 1);
460
461     ok(ScrollConsoleScreenBuffer(hCon, &scroll, &clip, dst, &ci), "Scrolling SB\n");
462
463     for (c.Y = 0; c.Y < sbSize.Y; c.Y++)
464     {
465         for (c.X = 0; c.X < sbSize.X; c.X++)
466         {
467             if (IN_SRECT2(scroll, dst, c) && IN_SRECT(clip, c))
468             {
469                 tc.X = c.X - dst.X;
470                 tc.Y = c.Y - dst.Y;
471                 okCHAR(hCon, c, CONTENT(tc), DEFAULT_ATTRIB);
472             }
473             else if (IN_SRECT(scroll, c) && IN_SRECT(clip, c))
474                 okCHAR(hCon, c, '#', TEST_ATTRIB);
475             else okCHAR(hCon, c, CONTENT(c), DEFAULT_ATTRIB);
476         }
477     }
478
479     /* clipping, src & dst rect do overlap */
480     resetContent(hCon, sbSize, TRUE);
481
482     scroll.Left = 0;
483     scroll.Right = W - 1;
484     scroll.Top = 0;
485     scroll.Bottom = H - 1;
486     dst.X = W / 2 - 3;
487     dst.Y = H / 2 - 3;
488     ci.Char.UnicodeChar = '#';
489     ci.Attributes = TEST_ATTRIB;
490
491     clip.Left = W / 2;
492     clip.Right = min(W + W / 2, sbSize.X - 1);
493     clip.Top = H / 2;
494     clip.Bottom = min(H + H / 2, sbSize.Y - 1);
495
496     ok(ScrollConsoleScreenBuffer(hCon, &scroll, &clip, dst, &ci), "Scrolling SB\n");
497
498     for (c.Y = 0; c.Y < sbSize.Y; c.Y++)
499     {
500         for (c.X = 0; c.X < sbSize.X; c.X++)
501         {
502             if (IN_SRECT2(scroll, dst, c) && IN_SRECT(clip, c))
503             {
504                 tc.X = c.X - dst.X;
505                 tc.Y = c.Y - dst.Y;
506                 okCHAR(hCon, c, CONTENT(tc), DEFAULT_ATTRIB);
507             }
508             else if (IN_SRECT(scroll, c) && IN_SRECT(clip, c))
509                 okCHAR(hCon, c, '#', TEST_ATTRIB);
510             else okCHAR(hCon, c, CONTENT(c), DEFAULT_ATTRIB);
511         }
512     }
513 }
514
515 static int mch_count;
516 /* we need the event as Wine console event generation isn't synchronous
517  * (ie GenerateConsoleCtrlEvent returns before all ctrl-handlers in all
518  * processes have been called).
519  */
520 static HANDLE mch_event;
521 static BOOL WINAPI mch(DWORD event)
522 {
523     mch_count++;
524     SetEvent(mch_event);
525     return TRUE;
526 }
527
528 static void testCtrlHandler(void)
529 {
530     ok(!SetConsoleCtrlHandler(mch, FALSE), "Shouldn't succeed\n");
531     ok(GetLastError() == ERROR_INVALID_PARAMETER, "Bad error %lu\n", GetLastError());
532     ok(SetConsoleCtrlHandler(mch, TRUE), "Couldn't set handler\n");
533     /* wine requires the event for the test, as we cannot insure, so far, that event
534      * are processed synchronously in GenerateConsoleCtrlEvent()
535      */
536     mch_event = CreateEventA(NULL, TRUE, FALSE, NULL);
537     mch_count = 0;
538     ok(GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0), "Couldn't send ctrl-c event\n");
539 #if 0  /* FIXME: it isn't synchronous on wine but it can still happen before we test */
540     todo_wine ok(mch_count == 1, "Event isn't synchronous\n");
541 #endif
542     ok(WaitForSingleObject(mch_event, 3000) == WAIT_OBJECT_0, "event sending didn't work\n");
543     CloseHandle(mch_event);
544     ok(SetConsoleCtrlHandler(NULL, TRUE), "Couldn't turn off ctrl-c handling\n");
545     mch_event = CreateEventA(NULL, TRUE, FALSE, NULL);
546     mch_count = 0;
547     ok(GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0), "Couldn't send ctrl-c event\n");
548     ok(WaitForSingleObject(mch_event, 3000) == WAIT_TIMEOUT && mch_count == 0, "Event shouldn't have been sent\n");
549     CloseHandle(mch_event);
550     ok(SetConsoleCtrlHandler(mch, FALSE), "Couldn't remove handler\n");
551     ok(!SetConsoleCtrlHandler(mch, FALSE), "Shouldn't succeed\n");
552     ok(GetLastError() == ERROR_INVALID_PARAMETER, "Bad error %lu\n", GetLastError());
553 }
554
555 START_TEST(console)
556 {
557     HANDLE hConIn, hConOut;
558     BOOL ret;
559     CONSOLE_SCREEN_BUFFER_INFO  sbi;
560
561     /* be sure we have a clean console (and that's our own)
562      * FIXME: this will make the test fail (currently) if we don't run
563      * under X11
564      * Another solution would be to rerun the test under wineconsole with
565      * the curses backend
566      */
567
568     hConIn = CreateFileA("CONIN$", GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0);
569     hConOut = CreateFileA("CONOUT$", GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0);
570
571     /* first, we need to be sure we're attached to a console */
572     if (hConIn == INVALID_HANDLE_VALUE || hConOut == INVALID_HANDLE_VALUE)
573     {
574         /* we're not attached to a console, let's do it */
575         AllocConsole();
576         hConIn = CreateFileA("CONIN$", GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0);
577         hConOut = CreateFileA("CONOUT$", GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0);
578     }
579     /* now verify everything's ok */
580     ok(hConIn != INVALID_HANDLE_VALUE, "Opening ConIn\n");
581     ok(hConOut != INVALID_HANDLE_VALUE, "Opening ConOut\n");
582
583     ok(ret = GetConsoleScreenBufferInfo(hConOut, &sbi), "Getting sb info\n");
584     if (!ret) return;
585
586     /* Non interactive tests */
587     testCursor(hConOut, sbi.dwSize);
588     /* will test wrapped (on/off) & processed (on/off) strings output */
589     testWrite(hConOut, sbi.dwSize);
590     /* will test line scrolling at the bottom of the screen */
591     /* testBottomScroll(); */
592     /* will test all the scrolling operations */
593     /* this one is disabled for now, Wine's result are way too bad */
594     testScroll(hConOut, sbi.dwSize);
595     /* will test sb creation / modification... */
596     /* testScreenBuffer() */
597     testCtrlHandler();
598     /* still to be done: access rights & access on objects */
599 }