1 /*
2  * PROJECT:         ReactOS api tests
3  * LICENSE:         GPLv2+ - See COPYING in the top level directory
4  * PURPOSE:         Test for SetConsoleWindowInfo
5  * PROGRAMMER:      Hermes Belusca-Maito
6  */
7 
8 #include "precomp.h"
9 
10 static VOID
11 ResizeTextConsole(
12     IN HANDLE hConOut,
13     IN OUT PCONSOLE_SCREEN_BUFFER_INFO pcsbi,
14     IN COORD Resolution,
15     IN PSMALL_RECT WindowSize OPTIONAL)
16 {
17     BOOL Success;
18     SMALL_RECT ConRect;
19 
20     if (Resolution.X != pcsbi->dwSize.X || Resolution.Y != pcsbi->dwSize.Y)
21     {
22         SHORT oldWidth, oldHeight;
23 
24         oldWidth  = pcsbi->srWindow.Right  - pcsbi->srWindow.Left + 1;
25         oldHeight = pcsbi->srWindow.Bottom - pcsbi->srWindow.Top  + 1;
26 
27         /*
28          * If the current console window is too large for
29          * the new screen buffer, resize it first.
30          */
31         if (oldWidth > Resolution.X || oldHeight > Resolution.Y)
32         {
33             ConRect.Left   = ConRect.Top = 0;
34             ConRect.Right  = ConRect.Left + min(oldWidth , Resolution.X) - 1;
35             ConRect.Bottom = ConRect.Top  + min(oldHeight, Resolution.Y) - 1;
36             Success = SetConsoleWindowInfo(hConOut, TRUE, &ConRect);
37             ok(Success, "Setting console wnd info failed with last error error %lu\n", GetLastError());
38         }
39 
40         /* Now resize the screen buffer */
41         Success = SetConsoleScreenBufferSize(hConOut, Resolution);
42         ok(Success, "Setting console SB size failed with last error error %lu\n", GetLastError());
43 
44         /*
45          * Setting a new screen buffer size can change other information,
46          * so update the saved console information.
47          */
48         Success = GetConsoleScreenBufferInfo(hConOut, pcsbi);
49         ok(Success, "Getting SB info\n");
50     }
51 
52     if (!WindowSize)
53     {
54         /* Always resize the console window within the permitted maximum size */
55         ConRect.Left   = 0;
56         ConRect.Right  = ConRect.Left + min(Resolution.X, pcsbi->dwMaximumWindowSize.X) - 1;
57         ConRect.Bottom = min(pcsbi->dwCursorPosition.Y, Resolution.Y - 1);
58         ConRect.Top    = ConRect.Bottom - min(Resolution.Y, pcsbi->dwMaximumWindowSize.Y) + 1;
59     }
60     else
61     {
62         /* Resize the console window according to user's wishes */
63         ConRect.Left   = ConRect.Top = 0;
64         ConRect.Right  = ConRect.Left + WindowSize->Right  - WindowSize->Left;
65         ConRect.Bottom = ConRect.Top  + WindowSize->Bottom - WindowSize->Top ;
66     }
67 
68     Success = SetConsoleWindowInfo(hConOut, TRUE, &ConRect);
69     ok(Success, "Setting console wnd info failed with last error error %lu\n", GetLastError());
70 
71     /* Update console screen buffer info */
72     Success = GetConsoleScreenBufferInfo(hConOut, pcsbi);
73     ok(Success, "Getting SB info\n");
74 }
75 
76 START_TEST(SetConsoleWindowInfo)
77 {
78     /*
79      * The aim of this test is to show that what MSDN says about the validity
80      * checks performed on the window size rect given to SetConsoleWindowInfo
81      * is partially wrong.
82      *
83      * Indeed, while it is claimed that:
84      *   "The function fails if the specified window rectangle extends beyond
85      *    the boundaries of the console screen buffer. This means that the Top
86      *    and Left members of the lpConsoleWindow rectangle (or the calculated
87      *    top and left coordinates, if bAbsolute is FALSE) cannot be less than
88      *    zero. Similarly, the Bottom and Right members (or the calculated
89      *    bottom and right coordinates) cannot be greater than (screen buffer
90      *    height – 1) and (screen buffer width – 1), respectively. The function
91      *    also fails if the Right member (or calculated right coordinate) is
92      *    less than or equal to the Left member (or calculated left coordinate)
93      *    or if the Bottom member (or calculated bottom coordinate) is less than
94      *    or equal to the Top member (or calculated top coordinate)."
95      *
96      * the really performed tests are fewer, and it appears that the console
97      * subsystem knows how to take proper actions when the window size rect
98      * has e.g. negative left/top coordinates...
99      *
100      * NOTE that we all perform those tests in "absolute mode" (second parameter
101      * of SetConsoleWindowInfo being TRUE), so that the specified window size rect
102      * is in absolute coordinates (i.e. relative to the console screen buffer),
103      * and not in coordinates relative to the current window-corner coordinates.
104      */
105 
106     BOOL Success;
107     DWORD dwLastError;
108     HANDLE hConOut;
109     COORD Resolution;
110     CONSOLE_SCREEN_BUFFER_INFO org_csbi, csbi, csbi2;
111     SMALL_RECT ConRect;
112 
113     /* First, retrieve a handle to the real console output, even if we are redirected */
114     hConOut = CreateFileW(L"CONOUT$", GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0);
115     ok(hConOut != INVALID_HANDLE_VALUE, "Opening ConOut\n");
116     if (hConOut == INVALID_HANDLE_VALUE)
117         return; // We cannot run this test if we failed...
118 
119     /*
120      * Retrieve the original console screen buffer info and save it
121      * for restoration at the end of the test. Use a copy after then.
122      */
123     Success = GetConsoleScreenBufferInfo(hConOut, &org_csbi);
124     ok(Success, "Getting SB info\n");
125     if (!Success)
126         goto Cleanup; // We cannot as well run this test if we failed...
127     csbi = org_csbi;
128 
129     /*
130      * Set the console screen buffer to a correct size that should not
131      * completely fill the computer screen. 'csbi' is correctly updated.
132      */
133     Resolution.X = 80;
134     Resolution.Y = 25;
135     ResizeTextConsole(hConOut, &csbi, Resolution, NULL);
136 
137     /* Test 1: Resize the console window to its possible maximum size (succeeds) */
138     ConRect.Left   = ConRect.Top = 0;
139     ConRect.Right  = ConRect.Left + min(csbi.dwSize.X, csbi.dwMaximumWindowSize.X) - 1;
140     ConRect.Bottom = ConRect.Top  + min(csbi.dwSize.Y, csbi.dwMaximumWindowSize.Y) - 1;
141     SetLastError(0xdeadbeef);
142     Success = SetConsoleWindowInfo(hConOut, TRUE, &ConRect);
143     dwLastError = GetLastError();
144     ok(Success, "Setting console wnd info\n");
145     ok(dwLastError != ERROR_INVALID_PARAMETER, "GetLastError: %lu\n", dwLastError);
146 
147     /* Test 2: Set negative Left/Top members, but correct Right/Bottom ones.
148      * The Left/Top members are shifted to zero while the Right/Bottom ones
149      * are shifted too in accordance.
150      * Situation where the Right/Bottom members will be ok after the shift
151      * (succeeds, disagrees with MSDN) */
152     ConRect.Left   = ConRect.Top = -5;
153     ConRect.Right  = csbi.dwSize.X - 7;
154     ConRect.Bottom = csbi.dwSize.Y - 7;
155     // Expected result: ConRect.Left == ConRect.Top == 0 and
156     // ConRect.Right == csbi.dwSize.X - 2, ConRect.Bottom == csbi.dwSize.Y - 2;
157     SetLastError(0xdeadbeef);
158     Success = SetConsoleWindowInfo(hConOut, TRUE, &ConRect);
159     dwLastError = GetLastError();
160     ok(Success, "Setting console wnd info should have succeeded!\n");
161     ok(dwLastError != ERROR_INVALID_PARAMETER, "GetLastError: %lu\n", dwLastError);
162 
163     /* Check the new reported window size rect */
164     Success = GetConsoleScreenBufferInfo(hConOut, &csbi2);
165     ok(Success, "Getting SB info\n");
166     if (Success)
167     {
168         ConRect.Right -= ConRect.Left;
169         ConRect.Left = 0;
170         ConRect.Bottom -= ConRect.Top;
171         ConRect.Top = 0;
172 
173         ok(csbi2.srWindow.Left == ConRect.Left, "srWindow.Left = %d, expected %d\n",
174            csbi2.srWindow.Left, ConRect.Left);
175         ok(csbi2.srWindow.Top == ConRect.Top, "srWindow.Top = %d, expected %d\n",
176            csbi2.srWindow.Top, ConRect.Top);
177         ok(csbi2.srWindow.Right == ConRect.Right, "srWindow.Right = %d, expected %d\n",
178            csbi2.srWindow.Right, ConRect.Right);
179         ok(csbi2.srWindow.Bottom == ConRect.Bottom, "srWindow.Bottom = %d, expected %d\n",
180            csbi2.srWindow.Bottom, ConRect.Bottom);
181     }
182 
183     /* Test 3: Similar to Test 2, but set the Right/Bottom members too large
184      * with respect to the screen buffer size, so that after their shift, they
185      * are still too large (fails, agrees with MSDN) */
186     ConRect.Left   = ConRect.Top = -5;
187     ConRect.Right  = csbi.dwSize.X + 2; // Bigger than SB size
188     ConRect.Bottom = csbi.dwSize.Y + 2; // Bigger than SB size
189     SetLastError(0xdeadbeef);
190     Success = SetConsoleWindowInfo(hConOut, TRUE, &ConRect);
191     dwLastError = GetLastError();
192     ok(!Success, "Setting console wnd info should have failed!\n");
193     ok(dwLastError == ERROR_INVALID_PARAMETER, "GetLastError: expecting %u got %lu\n",
194        ERROR_INVALID_PARAMETER, dwLastError);
195 
196     /* Check the new reported window size rect */
197     Success = GetConsoleScreenBufferInfo(hConOut, &csbi2);
198     ok(Success, "Getting SB info\n");
199     if (Success)
200     {
201         /* NOTE that here we compare against the old csbi data! */
202         ok(csbi2.srWindow.Left == 0, "srWindow.Left = %d, expected %d\n",
203            csbi2.srWindow.Left, 0);
204         ok(csbi2.srWindow.Top == 0, "srWindow.Top = %d, expected %d\n",
205            csbi2.srWindow.Top, 0);
206         ok(csbi2.srWindow.Right == csbi.dwSize.X - 2, "srWindow.Right = %d, expected %d\n",
207            csbi2.srWindow.Right, csbi.dwSize.X - 2);
208         ok(csbi2.srWindow.Bottom == csbi.dwSize.Y - 2, "srWindow.Bottom = %d, expected %d\n",
209            csbi2.srWindow.Bottom, csbi.dwSize.Y - 2);
210     }
211 
212     /* Test 4: Similar to Tests 2 and 3, but we here just check what happens for
213      * the Right/Bottom members when they are too large, without caring about the
214      * Left/Top members (the latter being set to valid values this time)
215      * (fails, agrees with MSDN) */
216     ConRect.Left   = ConRect.Top = 2; // OK
217     ConRect.Right  = csbi.dwSize.X + 7; // Bigger than SB size
218     ConRect.Bottom = csbi.dwSize.Y + 7; // Bigger than SB size
219     SetLastError(0xdeadbeef);
220     Success = SetConsoleWindowInfo(hConOut, TRUE, &ConRect);
221     dwLastError = GetLastError();
222     ok(!Success, "Setting console wnd info should have failed!\n");
223     ok(dwLastError == ERROR_INVALID_PARAMETER, "GetLastError: expecting %u got %lu\n",
224        ERROR_INVALID_PARAMETER, dwLastError);
225 
226     /* Check the new reported window size rect */
227     Success = GetConsoleScreenBufferInfo(hConOut, &csbi2);
228     ok(Success, "Getting SB info\n");
229     if (Success)
230     {
231         ok(csbi2.srWindow.Left == 0, "srWindow.Left = %d, expected %d\n",
232            csbi2.srWindow.Left, 0);
233         ok(csbi2.srWindow.Top == 0, "srWindow.Top = %d, expected %d\n",
234            csbi2.srWindow.Top, 0);
235 
236         /* NOTE that here we compare against the old csbi data! */
237         ok(csbi2.srWindow.Right == csbi.dwSize.X - 2, "srWindow.Right = %d, expected %d\n",
238            csbi2.srWindow.Right, csbi.dwSize.X - 2);
239         ok(csbi2.srWindow.Bottom == csbi.dwSize.Y - 2, "srWindow.Bottom = %d, expected %d\n",
240            csbi2.srWindow.Bottom, csbi.dwSize.Y - 2);
241     }
242 
243     /* Test 5: Set Right/Bottom members strictly smaller than Left/Top members
244      * (fails, agrees with MSDN) */
245     ConRect.Left   = csbi.dwSize.X - 5;
246     ConRect.Right  = 0;
247     ConRect.Top    = csbi.dwSize.Y - 5;
248     ConRect.Bottom = 0;
249     SetLastError(0xdeadbeef);
250     Success = SetConsoleWindowInfo(hConOut, TRUE, &ConRect);
251     dwLastError = GetLastError();
252     ok(!Success, "Setting console wnd info should have failed!\n");
253     ok(dwLastError == ERROR_INVALID_PARAMETER, "GetLastError: expecting %u got %lu\n",
254        ERROR_INVALID_PARAMETER, dwLastError);
255 
256     /* Test 6: Set Left/Top members equal to the Right/Bottom members respectively
257      * (succeeds, disagrees with MSDN) */
258     ConRect.Left = ConRect.Right  = 2;
259     ConRect.Top  = ConRect.Bottom = 5;
260     SetLastError(0xdeadbeef);
261     Success = SetConsoleWindowInfo(hConOut, TRUE, &ConRect);
262     dwLastError = GetLastError();
263     ok(Success, "Setting console wnd info should have succeeded!\n");
264     ok(dwLastError != ERROR_INVALID_PARAMETER, "GetLastError: %lu\n", dwLastError);
265 
266     /* Check the new reported window size rect */
267     Success = GetConsoleScreenBufferInfo(hConOut, &csbi2);
268     ok(Success, "Getting SB info\n");
269     if (Success)
270     {
271         ok(csbi2.srWindow.Left == ConRect.Left, "srWindow.Left = %d, expected %d\n",
272            csbi2.srWindow.Left, ConRect.Left);
273         ok(csbi2.srWindow.Top == ConRect.Top, "srWindow.Top = %d, expected %d\n",
274            csbi2.srWindow.Top, ConRect.Top);
275         ok(csbi2.srWindow.Right == ConRect.Right, "srWindow.Right = %d, expected %d\n",
276            csbi2.srWindow.Right, ConRect.Right);
277         ok(csbi2.srWindow.Bottom == ConRect.Bottom, "srWindow.Bottom = %d, expected %d\n",
278            csbi2.srWindow.Bottom, ConRect.Bottom);
279     }
280 
281     /*
282      * Test 7: Test how large can the console window be, for a given
283      * screen buffer size. For that we set the console screen buffer
284      * to a really large size, hoping that its corresponding window size
285      * is larger than the computer screen. The permitted maximum window
286      * size specified in csbi.dwMaximumWindowSize should be a boundary.
287      */
288     Resolution.X = 500;
289     Resolution.Y = 500;
290     ResizeTextConsole(hConOut, &csbi, Resolution, NULL);
291     /* Be sure that csbi.dwMaximumWindowSize is strictly smaller
292      * than the console screen buffer size, for our matters... */
293     ok((csbi.dwMaximumWindowSize.X < Resolution.X) && (csbi.dwMaximumWindowSize.Y < Resolution.Y),
294        "dwMaximumWindowSize = {%d, %d} was expected to be smaller than Resolution = {%d, %d}\n",
295        csbi.dwMaximumWindowSize.X, csbi.dwMaximumWindowSize.Y, Resolution.X, Resolution.Y);
296 
297     /* Now try to set first the console window to a size smaller than the maximum size */
298     ConRect.Left   = ConRect.Top = 0;
299     ConRect.Right  = csbi.dwMaximumWindowSize.X - 1;
300     ConRect.Bottom = csbi.dwMaximumWindowSize.Y - 1;
301     SetLastError(0xdeadbeef);
302     Success = SetConsoleWindowInfo(hConOut, TRUE, &ConRect);
303     dwLastError = GetLastError();
304     ok(Success, "Setting console wnd info should have succeeded!\n");
305     ok(dwLastError != ERROR_INVALID_PARAMETER, "GetLastError: %lu\n", dwLastError);
306 
307     /* Check the new reported window size rect */
308     Success = GetConsoleScreenBufferInfo(hConOut, &csbi2);
309     ok(Success, "Getting SB info\n");
310     if (Success)
311     {
312         ok(csbi2.srWindow.Left == ConRect.Left, "srWindow.Left = %d, expected %d\n",
313            csbi2.srWindow.Left, ConRect.Left);
314         ok(csbi2.srWindow.Top == ConRect.Top, "srWindow.Top = %d, expected %d\n",
315            csbi2.srWindow.Top, ConRect.Top);
316         ok(csbi2.srWindow.Right == ConRect.Right, "srWindow.Right = %d, expected %d\n",
317            csbi2.srWindow.Right, ConRect.Right);
318         ok(csbi2.srWindow.Bottom == ConRect.Bottom, "srWindow.Bottom = %d, expected %d\n",
319            csbi2.srWindow.Bottom, ConRect.Bottom);
320     }
321 
322     /* And now try to set the console window to a size larger than the maximum size.
323      * The SetConsoleWindowInfo call should fail */
324     ConRect.Left   = ConRect.Top = 0;
325     ConRect.Right  = csbi.dwMaximumWindowSize.X + 1;
326     ConRect.Bottom = csbi.dwMaximumWindowSize.Y + 1;
327     SetLastError(0xdeadbeef);
328     Success = SetConsoleWindowInfo(hConOut, TRUE, &ConRect);
329     dwLastError = GetLastError();
330     ok(!Success, "Setting console wnd info should have failed!\n");
331     ok(dwLastError == ERROR_INVALID_PARAMETER, "GetLastError: expecting %u got %lu\n",
332        ERROR_INVALID_PARAMETER, dwLastError);
333 
334     /* Check the new reported window size rect */
335     Success = GetConsoleScreenBufferInfo(hConOut, &csbi2);
336     ok(Success, "Getting SB info\n");
337     if (Success)
338     {
339         ok(csbi2.srWindow.Left == 0, "srWindow.Left = %d, expected %d\n",
340            csbi2.srWindow.Left, 0);
341         ok(csbi2.srWindow.Top == 0, "srWindow.Top = %d, expected %d\n",
342            csbi2.srWindow.Top, 0);
343         ok(csbi2.srWindow.Right == csbi.dwMaximumWindowSize.X - 1, "srWindow.Right = %d, expected %d\n",
344            csbi2.srWindow.Right, csbi.dwMaximumWindowSize.X - 1);
345         ok(csbi2.srWindow.Bottom == csbi.dwMaximumWindowSize.Y - 1, "srWindow.Bottom = %d, expected %d\n",
346            csbi2.srWindow.Bottom, csbi.dwMaximumWindowSize.Y - 1);
347     }
348 
349 
350     /* Done! Restore the original console screen buffer size and perform cleanup */
351     ResizeTextConsole(hConOut, &csbi, org_csbi.dwSize, &org_csbi.srWindow);
352 
353 Cleanup:
354     CloseHandle(hConOut);
355 }
356