1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         ReactOS Console Server DLL
4  * FILE:            win32ss/user/winsrv/consrv/frontends/gui/guiterm.c
5  * PURPOSE:         GUI Terminal Front-End
6  * PROGRAMMERS:     G� van Geldorp
7  *                  Johannes Anderwald
8  *                  Jeffrey Morlan
9  *                  Hermes Belusca-Maito (hermes.belusca@sfr.fr)
10  */
11 
12 /* INCLUDES *******************************************************************/
13 
14 #include <consrv.h>
15 
16 #define NDEBUG
17 #include <debug.h>
18 
19 #include "guiterm.h"
20 #include "resource.h"
21 
22 // HACK!! Remove it when the hack in GuiWriteStream is fixed
23 #define CONGUI_UPDATE_TIME    0
24 #define CONGUI_UPDATE_TIMER   1
25 
26 #define PM_CREATE_CONSOLE     (WM_APP + 1)
27 #define PM_DESTROY_CONSOLE    (WM_APP + 2)
28 
29 
30 /* GLOBALS ********************************************************************/
31 
32 typedef struct _GUI_INIT_INFO
33 {
34     HANDLE GuiThreadStartupEvent;
35     ULONG_PTR InputThreadId;
36     HWINSTA WinSta;
37     HDESK Desktop;
38     HICON hIcon;
39     HICON hIconSm;
40     BOOLEAN IsWindowVisible;
41     GUI_CONSOLE_INFO TermInfo;
42 } GUI_INIT_INFO, *PGUI_INIT_INFO;
43 
44 static BOOL ConsInitialized = FALSE;
45 
46 extern HICON   ghDefaultIcon;
47 extern HICON   ghDefaultIconSm;
48 extern HCURSOR ghDefaultCursor;
49 
50 VOID
51 SetConWndConsoleLeaderCID(IN PGUI_CONSOLE_DATA GuiData);
52 BOOLEAN
53 RegisterConWndClass(IN HINSTANCE hInstance);
54 BOOLEAN
55 UnRegisterConWndClass(HINSTANCE hInstance);
56 
57 /* FUNCTIONS ******************************************************************/
58 
59 VOID
60 GuiConsoleMoveWindow(PGUI_CONSOLE_DATA GuiData)
61 {
62     /* Move the window if needed (not positioned by the system) */
63     if (!GuiData->GuiInfo.AutoPosition)
64     {
65         SetWindowPos(GuiData->hWindow,
66                      NULL,
67                      GuiData->GuiInfo.WindowOrigin.x,
68                      GuiData->GuiInfo.WindowOrigin.y,
69                      0, 0,
70                      SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
71     }
72 }
73 
74 static VOID
75 DrawRegion(PGUI_CONSOLE_DATA GuiData,
76            SMALL_RECT* Region)
77 {
78     RECT RegionRect;
79 
80     SmallRectToRect(GuiData, &RegionRect, Region);
81     /* Do not erase the background: it speeds up redrawing and reduce flickering */
82     InvalidateRect(GuiData->hWindow, &RegionRect, FALSE);
83     /**UpdateWindow(GuiData->hWindow);**/
84 }
85 
86 VOID
87 InvalidateCell(PGUI_CONSOLE_DATA GuiData,
88                SHORT x, SHORT y)
89 {
90     SMALL_RECT CellRect = { x, y, x, y };
91     DrawRegion(GuiData, &CellRect);
92 }
93 
94 
95 /******************************************************************************
96  *                        GUI Terminal Initialization                         *
97  ******************************************************************************/
98 
99 VOID
100 SwitchFullScreen(PGUI_CONSOLE_DATA GuiData, BOOL FullScreen);
101 VOID
102 CreateSysMenu(HWND hWnd);
103 
104 static ULONG NTAPI
105 GuiConsoleInputThread(PVOID Param)
106 {
107     NTSTATUS Status;
108     PCSR_THREAD pcsrt = NULL;
109     PGUI_INIT_INFO GuiInitInfo = (PGUI_INIT_INFO)Param;
110     DESKTOP_CONSOLE_THREAD DesktopConsoleThreadInfo;
111     ULONG_PTR InputThreadId = HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread);
112     HANDLE hThread = NULL;
113 
114     LONG WindowCount = 0;
115     MSG msg;
116 
117     /*
118      * This thread dispatches all the console notifications to the
119      * notification window. It is common for all the console windows
120      * in a given desktop in a window station.
121      */
122 
123     /* Assign this console input thread to this desktop */
124     DesktopConsoleThreadInfo.DesktopHandle = GuiInitInfo->Desktop; // Duplicated desktop handle
125     DesktopConsoleThreadInfo.ThreadId = InputThreadId;
126     Status = NtUserConsoleControl(ConsoleCtrlDesktopConsoleThread,
127                                   &DesktopConsoleThreadInfo,
128                                   sizeof(DesktopConsoleThreadInfo));
129     if (!NT_SUCCESS(Status)) goto Quit;
130 
131     /* Connect this CSR thread to the USER subsystem */
132     pcsrt = CsrConnectToUser();
133     if (pcsrt == NULL) goto Quit;
134     hThread = pcsrt->ThreadHandle;
135 
136     /* Assign the desktop to this thread */
137     if (!SetThreadDesktop(DesktopConsoleThreadInfo.DesktopHandle)) goto Quit;
138 
139     /* The thread has been initialized, set the event */
140     NtSetEvent(GuiInitInfo->GuiThreadStartupEvent, NULL);
141     Status = STATUS_SUCCESS;
142 
143     while (GetMessageW(&msg, NULL, 0, 0))
144     {
145         switch (msg.message)
146         {
147             case PM_CREATE_CONSOLE:
148             {
149                 PGUI_CONSOLE_DATA GuiData = (PGUI_CONSOLE_DATA)msg.lParam;
150                 PCONSRV_CONSOLE Console = GuiData->Console;
151                 HWND NewWindow;
152                 RECT rcWnd;
153 
154                 DPRINT("PM_CREATE_CONSOLE -- creating window\n");
155 
156                 NewWindow = CreateWindowExW(WS_EX_CLIENTEDGE,
157                                             GUI_CONWND_CLASS,
158                                             Console->Title.Buffer,
159                                             WS_OVERLAPPEDWINDOW,
160                                             CW_USEDEFAULT,
161                                             CW_USEDEFAULT,
162                                             CW_USEDEFAULT,
163                                             CW_USEDEFAULT,
164                                             GuiData->IsWindowVisible ? HWND_DESKTOP : HWND_MESSAGE,
165                                             NULL,
166                                             ConSrvDllInstance,
167                                             (PVOID)GuiData);
168                 if (NewWindow == NULL)
169                 {
170                     DPRINT1("Failed to create a new console window\n");
171                     continue;
172                 }
173 
174                 ASSERT(NewWindow == GuiData->hWindow);
175 
176                 InterlockedIncrement(&WindowCount);
177 
178                 //
179                 // FIXME: TODO: Move everything there into conwnd.c!OnNcCreate()
180                 //
181 
182                 /* Retrieve our real position */
183                 // See conwnd.c!OnMove()
184                 GetWindowRect(GuiData->hWindow, &rcWnd);
185                 GuiData->GuiInfo.WindowOrigin.x = rcWnd.left;
186                 GuiData->GuiInfo.WindowOrigin.y = rcWnd.top;
187 
188                 if (GuiData->IsWindowVisible)
189                 {
190                     /* Move and resize the window to the user's values */
191                     /* CAN WE DEADLOCK ?? */
192                     GuiConsoleMoveWindow(GuiData); // FIXME: This MUST be done via the CreateWindowExW call.
193                     SendMessageW(GuiData->hWindow, PM_RESIZE_TERMINAL, 0, 0);
194                 }
195 
196                 // FIXME: HACK: Potential HACK for CORE-8129; see revision 63595.
197                 CreateSysMenu(GuiData->hWindow);
198 
199                 if (GuiData->IsWindowVisible)
200                 {
201                     /* Switch to full-screen mode if necessary */
202                     // FIXME: Move elsewhere, it cause misdrawings of the window.
203                     if (GuiData->GuiInfo.FullScreen) SwitchFullScreen(GuiData, TRUE);
204 
205                     DPRINT("PM_CREATE_CONSOLE -- showing window\n");
206                     // ShowWindow(NewWindow, (int)GuiData->GuiInfo.ShowWindow);
207                     ShowWindowAsync(NewWindow, (int)GuiData->GuiInfo.ShowWindow);
208                     DPRINT("Window showed\n");
209                 }
210                 else
211                 {
212                     DPRINT("PM_CREATE_CONSOLE -- hidden window\n");
213                     ShowWindowAsync(NewWindow, SW_HIDE);
214                 }
215 
216                 continue;
217             }
218 
219             case PM_DESTROY_CONSOLE:
220             {
221                 PGUI_CONSOLE_DATA GuiData = (PGUI_CONSOLE_DATA)msg.lParam;
222                 MSG TempMsg;
223 
224                 /* Exit the full screen mode if it was already set */
225                 // LeaveFullScreen(GuiData);
226 
227                 /*
228                  * Window creation is done using a PostMessage(), so it's possible
229                  * that the window that we want to destroy doesn't exist yet.
230                  * So first empty the message queue.
231                  */
232                 while (PeekMessageW(&TempMsg, NULL, 0, 0, PM_REMOVE))
233                 {
234                     DispatchMessageW(&TempMsg);
235                 }
236 
237                 if (GuiData->hWindow == NULL) continue;
238 
239                 DestroyWindow(GuiData->hWindow);
240 
241                 NtSetEvent(GuiData->hGuiTermEvent, NULL);
242 
243                 if (InterlockedDecrement(&WindowCount) == 0)
244                 {
245                     DPRINT("CONSRV: Going to quit the Input Thread 0x%p\n", InputThreadId);
246                     goto Quit;
247                 }
248 
249                 continue;
250             }
251         }
252 
253         TranslateMessage(&msg);
254         DispatchMessageW(&msg);
255     }
256 
257 Quit:
258     DPRINT("CONSRV: Quit the Input Thread 0x%p, Status = 0x%08lx\n", InputThreadId, Status);
259 
260     /* Remove this console input thread from this desktop */
261     // DesktopConsoleThreadInfo.DesktopHandle;
262     DesktopConsoleThreadInfo.ThreadId = 0;
263     NtUserConsoleControl(ConsoleCtrlDesktopConsoleThread,
264                          &DesktopConsoleThreadInfo,
265                          sizeof(DesktopConsoleThreadInfo));
266 
267     /* Close the duplicated desktop handle */
268     CloseDesktop(DesktopConsoleThreadInfo.DesktopHandle); // NtUserCloseDesktop
269 
270     /* Cleanup CSR thread */
271     if (pcsrt)
272     {
273         if (hThread != pcsrt->ThreadHandle)
274             DPRINT1("WARNING!! hThread (0x%p) != pcsrt->ThreadHandle (0x%p), you may expect crashes soon!!\n", hThread, pcsrt->ThreadHandle);
275 
276         CsrDereferenceThread(pcsrt);
277     }
278 
279     /* Exit the thread */
280     RtlExitUserThread(Status);
281     return 0;
282 }
283 
284 // FIXME: Maybe return a NTSTATUS
285 static BOOL
286 GuiInit(IN PCONSOLE_INIT_INFO ConsoleInitInfo,
287         IN HANDLE ConsoleLeaderProcessHandle,
288         IN OUT PGUI_INIT_INFO GuiInitInfo)
289 {
290     BOOL Success = TRUE;
291     UNICODE_STRING DesktopPath;
292     DESKTOP_CONSOLE_THREAD DesktopConsoleThreadInfo;
293     HWINSTA hWinSta;
294     HDESK hDesk;
295 
296     NTSTATUS Status;
297     HANDLE hInputThread;
298     CLIENT_ID ClientId;
299 
300     /*
301      * Initialize and register the console window class, if needed.
302      */
303     if (!ConsInitialized)
304     {
305         if (!RegisterConWndClass(ConSrvDllInstance)) return FALSE;
306         ConsInitialized = TRUE;
307     }
308 
309     /*
310      * Set-up the console input thread. We have
311      * one console input thread per desktop.
312      */
313 
314     if (!CsrImpersonateClient(NULL))
315         // return STATUS_BAD_IMPERSONATION_LEVEL;
316         return FALSE;
317 
318     if (ConsoleInitInfo->DesktopLength)
319     {
320         DesktopPath.MaximumLength = ConsoleInitInfo->DesktopLength;
321         DesktopPath.Length = DesktopPath.MaximumLength - sizeof(UNICODE_NULL);
322         DesktopPath.Buffer = ConsoleInitInfo->Desktop;
323     }
324     else
325     {
326         RtlInitUnicodeString(&DesktopPath, L"Default");
327     }
328 
329     hDesk = NtUserResolveDesktop(ConsoleLeaderProcessHandle,
330                                  &DesktopPath,
331                                  0,
332                                  &hWinSta);
333     DPRINT("NtUserResolveDesktop(DesktopPath = '%wZ') returned hDesk = 0x%p; hWinSta = 0x%p\n",
334            &DesktopPath, hDesk, hWinSta);
335 
336     CsrRevertToSelf();
337 
338     if (hDesk == NULL) return FALSE;
339 
340     /*
341      * We need to see whether we need to create a
342      * new console input thread for this desktop.
343      */
344     DesktopConsoleThreadInfo.DesktopHandle = hDesk;
345     DesktopConsoleThreadInfo.ThreadId = (ULONG_PTR)INVALID_HANDLE_VALUE; // Special value to say we just want to retrieve the thread ID.
346     NtUserConsoleControl(ConsoleCtrlDesktopConsoleThread,
347                          &DesktopConsoleThreadInfo,
348                          sizeof(DesktopConsoleThreadInfo));
349     DPRINT("NtUserConsoleControl returned ThreadId = 0x%p\n", DesktopConsoleThreadInfo.ThreadId);
350 
351     /*
352      * Save the opened window station and desktop handles in the initialization
353      * structure. They will be used later on, and released, by the GUI frontend.
354      */
355     GuiInitInfo->WinSta  = hWinSta;
356     GuiInitInfo->Desktop = hDesk;
357 
358     /* Here GuiInitInfo contains original handles */
359 
360     /* If we already have a console input thread on this desktop... */
361     if (DesktopConsoleThreadInfo.ThreadId != 0)
362     {
363         /* ... just use it... */
364         DPRINT("Using input thread InputThreadId = 0x%p\n", DesktopConsoleThreadInfo.ThreadId);
365         GuiInitInfo->InputThreadId = DesktopConsoleThreadInfo.ThreadId;
366         goto Quit;
367     }
368 
369     /* ... otherwise create a new one. */
370 
371     /* Initialize a startup event for the thread to signal it */
372     Status = NtCreateEvent(&GuiInitInfo->GuiThreadStartupEvent, EVENT_ALL_ACCESS,
373                            NULL, SynchronizationEvent, FALSE);
374     if (!NT_SUCCESS(Status))
375     {
376         Success = FALSE;
377         goto Quit;
378     }
379 
380     /*
381      * Duplicate the desktop handle for the console input thread internal needs.
382      * If it happens to need also a window station handle in the future, then
383      * it is there that you also need to duplicate the window station handle!
384      *
385      * Note also that we are going to temporarily overwrite the stored handles
386      * in GuiInitInfo because it happens that we use also this structure to give
387      * the duplicated handles to the input thread that is going to initialize.
388      * After the input thread finishes its initialization, we restore the handles
389      * in GuiInitInfo to their old values.
390      */
391     Status = NtDuplicateObject(NtCurrentProcess(),
392                                hDesk,
393                                NtCurrentProcess(),
394                                (PHANDLE)&GuiInitInfo->Desktop,
395                                0, 0, DUPLICATE_SAME_ACCESS);
396     if (!NT_SUCCESS(Status))
397     {
398         Success = FALSE;
399         goto Quit;
400     }
401 
402     /* Here GuiInitInfo contains duplicated handles */
403 
404     Status = RtlCreateUserThread(NtCurrentProcess(),
405                                  NULL,
406                                  TRUE, // Start the thread in suspended state
407                                  0,
408                                  0,
409                                  0,
410                                  (PVOID)GuiConsoleInputThread,
411                                  (PVOID)GuiInitInfo,
412                                  &hInputThread,
413                                  &ClientId);
414     if (NT_SUCCESS(Status))
415     {
416         /* Add it as a static server thread and resume it */
417         CsrAddStaticServerThread(hInputThread, &ClientId, 0);
418         Status = NtResumeThread(hInputThread, NULL);
419     }
420     DPRINT("Thread creation hInputThread = 0x%p, InputThreadId = 0x%p, Status = 0x%08lx\n",
421            hInputThread, ClientId.UniqueThread, Status);
422 
423     if (!NT_SUCCESS(Status) || hInputThread == NULL)
424     {
425         /* Close the thread's handle */
426         if (hInputThread) NtClose(hInputThread);
427 
428         /* We need to close here the duplicated desktop handle */
429         CloseDesktop(GuiInitInfo->Desktop); // NtUserCloseDesktop
430 
431         /* Close the startup event and bail out */
432         NtClose(GuiInitInfo->GuiThreadStartupEvent);
433 
434         DPRINT1("CONSRV: Failed to create graphics console thread.\n");
435         Success = FALSE;
436         goto Quit;
437     }
438 
439     /* No need to close hInputThread, this is done by CSR automatically */
440 
441     /* Wait for the thread to finish its initialization, and close the startup event */
442     NtWaitForSingleObject(GuiInitInfo->GuiThreadStartupEvent, FALSE, NULL);
443     NtClose(GuiInitInfo->GuiThreadStartupEvent);
444 
445     /*
446      * Save the input thread ID for later use, and restore the original handles.
447      * The copies are held by the console input thread.
448      */
449     GuiInitInfo->InputThreadId = (ULONG_PTR)ClientId.UniqueThread;
450     GuiInitInfo->WinSta  = hWinSta;
451     GuiInitInfo->Desktop = hDesk;
452 
453     /* Here GuiInitInfo contains again original handles */
454 
455 Quit:
456     if (!Success)
457     {
458         /*
459          * Close the original handles. Do not use the copies in GuiInitInfo
460          * because we may have failed in the middle of the duplicate operation
461          * and the handles stored in GuiInitInfo may have changed.
462          */
463         CloseDesktop(hDesk); // NtUserCloseDesktop
464         CloseWindowStation(hWinSta); // NtUserCloseWindowStation
465     }
466 
467     return Success;
468 }
469 
470 
471 /******************************************************************************
472  *                             GUI Console Driver                             *
473  ******************************************************************************/
474 
475 static VOID NTAPI
476 GuiDeinitFrontEnd(IN OUT PFRONTEND This);
477 
478 static NTSTATUS NTAPI
479 GuiInitFrontEnd(IN OUT PFRONTEND This,
480                 IN PCONSRV_CONSOLE Console)
481 {
482     PGUI_INIT_INFO GuiInitInfo;
483     PGUI_CONSOLE_DATA GuiData;
484 
485     if (This == NULL || Console == NULL || This->Context2 == NULL)
486         return STATUS_INVALID_PARAMETER;
487 
488     ASSERT(This->Console == Console);
489 
490     GuiInitInfo = This->Context2;
491 
492     /* Terminal data allocation */
493     GuiData = ConsoleAllocHeap(HEAP_ZERO_MEMORY, sizeof(*GuiData));
494     if (!GuiData)
495     {
496         DPRINT1("CONSRV: Failed to create GUI_CONSOLE_DATA\n");
497         return STATUS_UNSUCCESSFUL;
498     }
499     /// /* HACK */ Console->FrontEndIFace.Context = (PVOID)GuiData; /* HACK */
500     GuiData->Console      = Console;
501     GuiData->ActiveBuffer = Console->ActiveBuffer;
502     GuiData->hWindow = NULL;
503     GuiData->IsWindowVisible = GuiInitInfo->IsWindowVisible;
504 
505     /* The console can be resized */
506     Console->FixedSize = FALSE;
507 
508     InitializeCriticalSection(&GuiData->Lock);
509 
510     /*
511      * Set up GUI data
512      */
513     RtlCopyMemory(&GuiData->GuiInfo, &GuiInitInfo->TermInfo, sizeof(GuiInitInfo->TermInfo));
514 
515     /* Initialize the icon handles */
516     if (GuiInitInfo->hIcon != NULL)
517         GuiData->hIcon = GuiInitInfo->hIcon;
518     else
519         GuiData->hIcon = ghDefaultIcon;
520 
521     if (GuiInitInfo->hIconSm != NULL)
522         GuiData->hIconSm = GuiInitInfo->hIconSm;
523     else
524         GuiData->hIconSm = ghDefaultIconSm;
525 
526     ASSERT(GuiData->hIcon && GuiData->hIconSm);
527 
528     /* Mouse is shown by default with its default cursor shape */
529     GuiData->hCursor = ghDefaultCursor;
530     GuiData->MouseCursorRefCount = 0;
531 
532     /* A priori don't ignore mouse signals */
533     GuiData->IgnoreNextMouseSignal = FALSE;
534     /* Initialize HACK FOR CORE-8394. See conwnd.c!OnMouse for more details. */
535     GuiData->HackCORE8394IgnoreNextMove = FALSE;
536 
537     /* Close button and the corresponding system menu item are enabled by default */
538     GuiData->IsCloseButtonEnabled = TRUE;
539 
540     /* There is no user-reserved menu id range by default */
541     GuiData->CmdIdLow = GuiData->CmdIdHigh = 0;
542 
543     /* Initialize the selection */
544     RtlZeroMemory(&GuiData->Selection, sizeof(GuiData->Selection));
545     GuiData->Selection.dwFlags = CONSOLE_NO_SELECTION;
546     RtlZeroMemory(&GuiData->dwSelectionCursor, sizeof(GuiData->dwSelectionCursor));
547     GuiData->LineSelection = FALSE; // Default to block selection
548     // TODO: Retrieve the selection mode via the registry.
549 
550     GuiData->InputThreadId = GuiInitInfo->InputThreadId;
551     GuiData->WinSta  = GuiInitInfo->WinSta;
552     GuiData->Desktop = GuiInitInfo->Desktop;
553 
554     /* Finally, finish to initialize the frontend structure */
555     This->Context  = GuiData;
556     ConsoleFreeHeap(This->Context2);
557     This->Context2 = NULL;
558 
559     /*
560      * We need to wait until the GUI has been fully initialized
561      * to retrieve custom settings i.e. WindowSize etc...
562      * Ideally we could use SendNotifyMessage for this but its not
563      * yet implemented.
564      */
565     NtCreateEvent(&GuiData->hGuiInitEvent, EVENT_ALL_ACCESS,
566                   NULL, SynchronizationEvent, FALSE);
567     NtCreateEvent(&GuiData->hGuiTermEvent, EVENT_ALL_ACCESS,
568                   NULL, SynchronizationEvent, FALSE);
569 
570     DPRINT("GUI - Checkpoint\n");
571 
572     /* Create the terminal window */
573     PostThreadMessageW(GuiData->InputThreadId, PM_CREATE_CONSOLE, 0, (LPARAM)GuiData);
574 
575     /* Wait until initialization has finished */
576     NtWaitForSingleObject(GuiData->hGuiInitEvent, FALSE, NULL);
577     DPRINT("OK we created the console window\n");
578     NtClose(GuiData->hGuiInitEvent);
579     GuiData->hGuiInitEvent = NULL;
580 
581     /* Check whether we really succeeded in initializing the terminal window */
582     if (GuiData->hWindow == NULL)
583     {
584         DPRINT("GuiInitConsole - We failed at creating a new terminal window\n");
585         GuiDeinitFrontEnd(This);
586         return STATUS_UNSUCCESSFUL;
587     }
588 
589     return STATUS_SUCCESS;
590 }
591 
592 static VOID NTAPI
593 GuiDeinitFrontEnd(IN OUT PFRONTEND This)
594 {
595     PGUI_CONSOLE_DATA GuiData = This->Context;
596 
597     DPRINT("Send PM_DESTROY_CONSOLE message and wait on hGuiTermEvent...\n");
598     PostThreadMessageW(GuiData->InputThreadId, PM_DESTROY_CONSOLE, 0, (LPARAM)GuiData);
599     NtWaitForSingleObject(GuiData->hGuiTermEvent, FALSE, NULL);
600     DPRINT("hGuiTermEvent set\n");
601     NtClose(GuiData->hGuiTermEvent);
602     GuiData->hGuiTermEvent = NULL;
603 
604     CloseDesktop(GuiData->Desktop); // NtUserCloseDesktop
605     CloseWindowStation(GuiData->WinSta); // NtUserCloseWindowStation
606 
607     DPRINT("Destroying icons !! - GuiData->hIcon = 0x%p ; ghDefaultIcon = 0x%p ; GuiData->hIconSm = 0x%p ; ghDefaultIconSm = 0x%p\n",
608             GuiData->hIcon, ghDefaultIcon, GuiData->hIconSm, ghDefaultIconSm);
609     if (GuiData->hIcon != NULL && GuiData->hIcon != ghDefaultIcon)
610     {
611         DPRINT("Destroy hIcon\n");
612         DestroyIcon(GuiData->hIcon);
613     }
614     if (GuiData->hIconSm != NULL && GuiData->hIconSm != ghDefaultIconSm)
615     {
616         DPRINT("Destroy hIconSm\n");
617         DestroyIcon(GuiData->hIconSm);
618     }
619 
620     This->Context = NULL;
621     DeleteCriticalSection(&GuiData->Lock);
622     ConsoleFreeHeap(GuiData);
623 
624     DPRINT("Quit GuiDeinitFrontEnd\n");
625 }
626 
627 static VOID NTAPI
628 GuiDrawRegion(IN OUT PFRONTEND This,
629               SMALL_RECT* Region)
630 {
631     PGUI_CONSOLE_DATA GuiData = This->Context;
632 
633     /* Do nothing if the window is hidden */
634     if (!GuiData->IsWindowVisible) return;
635 
636     DrawRegion(GuiData, Region);
637 }
638 
639 static VOID NTAPI
640 GuiWriteStream(IN OUT PFRONTEND This,
641                SMALL_RECT* Region,
642                SHORT CursorStartX,
643                SHORT CursorStartY,
644                UINT ScrolledLines,
645                PWCHAR Buffer,
646                UINT Length)
647 {
648     PGUI_CONSOLE_DATA GuiData = This->Context;
649     PCONSOLE_SCREEN_BUFFER Buff;
650     SHORT CursorEndX, CursorEndY;
651     RECT ScrollRect;
652 
653     if (NULL == GuiData || NULL == GuiData->hWindow) return;
654 
655     /* Do nothing if the window is hidden */
656     if (!GuiData->IsWindowVisible) return;
657 
658     Buff = GuiData->ActiveBuffer;
659     if (GetType(Buff) != TEXTMODE_BUFFER) return;
660 
661     if (0 != ScrolledLines)
662     {
663         ScrollRect.left = 0;
664         ScrollRect.top = 0;
665         ScrollRect.right = Buff->ViewSize.X * GuiData->CharWidth;
666         ScrollRect.bottom = Region->Top * GuiData->CharHeight;
667 
668         ScrollWindowEx(GuiData->hWindow,
669                        0,
670                        -(int)(ScrolledLines * GuiData->CharHeight),
671                        &ScrollRect,
672                        NULL,
673                        NULL,
674                        NULL,
675                        SW_INVALIDATE);
676     }
677 
678     DrawRegion(GuiData, Region);
679 
680     if (CursorStartX < Region->Left || Region->Right < CursorStartX
681             || CursorStartY < Region->Top || Region->Bottom < CursorStartY)
682     {
683         InvalidateCell(GuiData, CursorStartX, CursorStartY);
684     }
685 
686     CursorEndX = Buff->CursorPosition.X;
687     CursorEndY = Buff->CursorPosition.Y;
688     if ((CursorEndX < Region->Left || Region->Right < CursorEndX
689             || CursorEndY < Region->Top || Region->Bottom < CursorEndY)
690             && (CursorEndX != CursorStartX || CursorEndY != CursorStartY))
691     {
692         InvalidateCell(GuiData, CursorEndX, CursorEndY);
693     }
694 
695     // HACK!!
696     // Set up the update timer (very short interval) - this is a "hack" for getting the OS to
697     // repaint the window without having it just freeze up and stay on the screen permanently.
698     Buff->CursorBlinkOn = TRUE;
699     SetTimer(GuiData->hWindow, CONGUI_UPDATE_TIMER, CONGUI_UPDATE_TIME, NULL);
700 }
701 
702 /* static */ VOID NTAPI
703 GuiRingBell(IN OUT PFRONTEND This)
704 {
705     PGUI_CONSOLE_DATA GuiData = This->Context;
706 
707     /* Emit an error beep sound */
708     SendNotifyMessage(GuiData->hWindow, PM_CONSOLE_BEEP, 0, 0);
709 }
710 
711 static BOOL NTAPI
712 GuiSetCursorInfo(IN OUT PFRONTEND This,
713                  PCONSOLE_SCREEN_BUFFER Buff)
714 {
715     PGUI_CONSOLE_DATA GuiData = This->Context;
716 
717     /* Do nothing if the window is hidden */
718     if (!GuiData->IsWindowVisible) return TRUE;
719 
720     if (GuiData->ActiveBuffer == Buff)
721     {
722         InvalidateCell(GuiData, Buff->CursorPosition.X, Buff->CursorPosition.Y);
723     }
724 
725     return TRUE;
726 }
727 
728 static BOOL NTAPI
729 GuiSetScreenInfo(IN OUT PFRONTEND This,
730                  PCONSOLE_SCREEN_BUFFER Buff,
731                  SHORT OldCursorX,
732                  SHORT OldCursorY)
733 {
734     PGUI_CONSOLE_DATA GuiData = This->Context;
735 
736     /* Do nothing if the window is hidden */
737     if (!GuiData->IsWindowVisible) return TRUE;
738 
739     if (GuiData->ActiveBuffer == Buff)
740     {
741         /* Redraw char at old position (remove cursor) */
742         InvalidateCell(GuiData, OldCursorX, OldCursorY);
743         /* Redraw char at new position (show cursor) */
744         InvalidateCell(GuiData, Buff->CursorPosition.X, Buff->CursorPosition.Y);
745     }
746 
747     return TRUE;
748 }
749 
750 static VOID NTAPI
751 GuiResizeTerminal(IN OUT PFRONTEND This)
752 {
753     PGUI_CONSOLE_DATA GuiData = This->Context;
754 
755     /* Resize the window to the user's values */
756     PostMessageW(GuiData->hWindow, PM_RESIZE_TERMINAL, 0, 0);
757 }
758 
759 static VOID NTAPI
760 GuiSetActiveScreenBuffer(IN OUT PFRONTEND This)
761 {
762     PGUI_CONSOLE_DATA GuiData = This->Context;
763     PCONSOLE_SCREEN_BUFFER ActiveBuffer;
764     HPALETTE hPalette;
765 
766     EnterCriticalSection(&GuiData->Lock);
767     GuiData->WindowSizeLock = TRUE;
768 
769     InterlockedExchangePointer((PVOID*)&GuiData->ActiveBuffer,
770                                ConDrvGetActiveScreenBuffer(GuiData->Console));
771 
772     GuiData->WindowSizeLock = FALSE;
773     LeaveCriticalSection(&GuiData->Lock);
774 
775     ActiveBuffer = GuiData->ActiveBuffer;
776 
777     /* Change the current palette */
778     if (ActiveBuffer->PaletteHandle == NULL)
779         hPalette = GuiData->hSysPalette;
780     else
781         hPalette = ActiveBuffer->PaletteHandle;
782 
783     DPRINT("GuiSetActiveScreenBuffer using palette 0x%p\n", hPalette);
784 
785     /* Set the new palette for the framebuffer */
786     SelectPalette(GuiData->hMemDC, hPalette, FALSE);
787 
788     /* Specify the use of the system palette for the framebuffer */
789     SetSystemPaletteUse(GuiData->hMemDC, ActiveBuffer->PaletteUsage);
790 
791     /* Realize the (logical) palette */
792     RealizePalette(GuiData->hMemDC);
793 
794     GuiResizeTerminal(This);
795     // ConioDrawConsole(Console);
796 }
797 
798 static VOID NTAPI
799 GuiReleaseScreenBuffer(IN OUT PFRONTEND This,
800                        IN PCONSOLE_SCREEN_BUFFER ScreenBuffer)
801 {
802     PGUI_CONSOLE_DATA GuiData = This->Context;
803 
804     /*
805      * If we were notified to release a screen buffer that is not actually
806      * ours, then just ignore the notification...
807      */
808     if (ScreenBuffer != GuiData->ActiveBuffer) return;
809 
810     /*
811      * ... else, we must release our active buffer. Two cases are present:
812      * - If ScreenBuffer (== GuiData->ActiveBuffer) IS NOT the console
813      *   active screen buffer, then we can safely switch to it.
814      * - If ScreenBuffer IS the console active screen buffer, we must release
815      *   it ONLY.
816      */
817 
818     /* Release the old active palette and set the default one */
819     if (GetCurrentObject(GuiData->hMemDC, OBJ_PAL) == ScreenBuffer->PaletteHandle)
820     {
821         /* Set the new palette */
822         SelectPalette(GuiData->hMemDC, GuiData->hSysPalette, FALSE);
823     }
824 
825     /* Set the adequate active screen buffer */
826     if (ScreenBuffer != GuiData->Console->ActiveBuffer)
827     {
828         GuiSetActiveScreenBuffer(This);
829     }
830     else
831     {
832         EnterCriticalSection(&GuiData->Lock);
833         GuiData->WindowSizeLock = TRUE;
834 
835         InterlockedExchangePointer((PVOID*)&GuiData->ActiveBuffer, NULL);
836 
837         GuiData->WindowSizeLock = FALSE;
838         LeaveCriticalSection(&GuiData->Lock);
839     }
840 }
841 
842 static BOOL NTAPI
843 GuiSetMouseCursor(IN OUT PFRONTEND This,
844                   HCURSOR CursorHandle);
845 
846 static VOID NTAPI
847 GuiRefreshInternalInfo(IN OUT PFRONTEND This)
848 {
849     PGUI_CONSOLE_DATA GuiData = This->Context;
850 
851     /* Update the console leader information held by the window */
852     SetConWndConsoleLeaderCID(GuiData);
853 
854     /*
855      * HACK:
856      * We reset the cursor here so that, when a console app quits, we reset
857      * the cursor to the default one. It's quite a hack since it doesn't proceed
858      * per - console process... This must be fixed.
859      *
860      * See GuiInitConsole(...) for more information.
861      */
862 
863     /* Mouse is shown by default with its default cursor shape */
864     GuiData->MouseCursorRefCount = 0; // Reinitialize the reference counter
865     GuiSetMouseCursor(This, NULL);
866 }
867 
868 static VOID NTAPI
869 GuiChangeTitle(IN OUT PFRONTEND This)
870 {
871     PGUI_CONSOLE_DATA GuiData = This->Context;
872     // PostMessageW(GuiData->hWindow, PM_CONSOLE_SET_TITLE, 0, 0);
873     SetWindowTextW(GuiData->hWindow, GuiData->Console->Title.Buffer);
874 }
875 
876 static BOOL NTAPI
877 GuiChangeIcon(IN OUT PFRONTEND This,
878               HICON IconHandle)
879 {
880     PGUI_CONSOLE_DATA GuiData = This->Context;
881     HICON hIcon, hIconSm;
882 
883     if (IconHandle == NULL)
884     {
885         hIcon   = ghDefaultIcon;
886         hIconSm = ghDefaultIconSm;
887     }
888     else
889     {
890         hIcon   = CopyIcon(IconHandle);
891         hIconSm = CopyIcon(IconHandle);
892     }
893 
894     if (hIcon == NULL)
895         return FALSE;
896 
897     if (hIcon != GuiData->hIcon)
898     {
899         if (GuiData->hIcon != NULL && GuiData->hIcon != ghDefaultIcon)
900         {
901             DestroyIcon(GuiData->hIcon);
902         }
903         if (GuiData->hIconSm != NULL && GuiData->hIconSm != ghDefaultIconSm)
904         {
905             DestroyIcon(GuiData->hIconSm);
906         }
907 
908         GuiData->hIcon   = hIcon;
909         GuiData->hIconSm = hIconSm;
910 
911         DPRINT("Set icons in GuiChangeIcon\n");
912         PostMessageW(GuiData->hWindow, WM_SETICON, ICON_BIG  , (LPARAM)GuiData->hIcon  );
913         PostMessageW(GuiData->hWindow, WM_SETICON, ICON_SMALL, (LPARAM)GuiData->hIconSm);
914     }
915 
916     return TRUE;
917 }
918 
919 static HWND NTAPI
920 GuiGetConsoleWindowHandle(IN OUT PFRONTEND This)
921 {
922     PGUI_CONSOLE_DATA GuiData = This->Context;
923     return GuiData->hWindow;
924 }
925 
926 static VOID NTAPI
927 GuiGetLargestConsoleWindowSize(IN OUT PFRONTEND This,
928                                PCOORD pSize)
929 {
930     PGUI_CONSOLE_DATA GuiData = This->Context;
931     PCONSOLE_SCREEN_BUFFER ActiveBuffer;
932     HMONITOR hMonitor;
933     MONITORINFO MonitorInfo;
934     LONG Width, Height;
935     UINT WidthUnit, HeightUnit;
936 
937     if (!pSize) return;
938 
939     /*
940      * Retrieve the monitor that is mostly covered by the current console window;
941      * default to primary monitor otherwise.
942      */
943     MonitorInfo.cbSize = sizeof(MonitorInfo);
944     hMonitor = MonitorFromWindow(GuiData->hWindow, MONITOR_DEFAULTTOPRIMARY);
945     if (hMonitor && GetMonitorInfoW(hMonitor, &MonitorInfo))
946     {
947         /* Retrieve the width and height of the client area of this monitor */
948         Width  = MonitorInfo.rcWork.right - MonitorInfo.rcWork.left;
949         Height = MonitorInfo.rcWork.bottom - MonitorInfo.rcWork.top;
950     }
951     else
952     {
953         /*
954          * Retrieve the width and height of the client area for a full-screen
955          * window on the primary display monitor.
956          */
957         Width  = GetSystemMetrics(SM_CXFULLSCREEN);
958         Height = GetSystemMetrics(SM_CYFULLSCREEN);
959 
960         // RECT WorkArea;
961         // SystemParametersInfoW(SPI_GETWORKAREA, 0, &WorkArea, 0);
962         // Width  = WorkArea.right;
963         // Height = WorkArea.bottom;
964     }
965 
966     ActiveBuffer = GuiData->ActiveBuffer;
967 #if 0
968     // NOTE: This would be surprising if we wouldn't have an associated buffer...
969     if (ActiveBuffer)
970 #endif
971         GetScreenBufferSizeUnits(ActiveBuffer, GuiData, &WidthUnit, &HeightUnit);
972 #if 0
973     else
974         /* Default: graphics mode */
975         WidthUnit = HeightUnit = 1;
976 #endif
977 
978     Width  -= (2 * (GetSystemMetrics(SM_CXFRAME) + GetSystemMetrics(SM_CXEDGE)));
979     Height -= (2 * (GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYEDGE)) + GetSystemMetrics(SM_CYCAPTION));
980 
981     if (Width  < 0) Width  = 0;
982     if (Height < 0) Height = 0;
983 
984     pSize->X = (SHORT)(Width  / (int)WidthUnit ) /* HACK */ + 2;
985     pSize->Y = (SHORT)(Height / (int)HeightUnit) /* HACK */ + 1;
986 }
987 
988 static BOOL NTAPI
989 GuiGetSelectionInfo(IN OUT PFRONTEND This,
990                     PCONSOLE_SELECTION_INFO pSelectionInfo)
991 {
992     PGUI_CONSOLE_DATA GuiData = This->Context;
993 
994     if (pSelectionInfo == NULL) return FALSE;
995 
996     ZeroMemory(pSelectionInfo, sizeof(*pSelectionInfo));
997     if (GuiData->Selection.dwFlags != CONSOLE_NO_SELECTION)
998         RtlCopyMemory(pSelectionInfo, &GuiData->Selection, sizeof(*pSelectionInfo));
999 
1000     return TRUE;
1001 }
1002 
1003 static BOOL NTAPI
1004 GuiSetPalette(IN OUT PFRONTEND This,
1005               HPALETTE PaletteHandle,
1006               UINT PaletteUsage)
1007 {
1008     PGUI_CONSOLE_DATA GuiData = This->Context;
1009     HPALETTE OldPalette;
1010 
1011     // if (GetType(GuiData->ActiveBuffer) != GRAPHICS_BUFFER) return FALSE;
1012     if (PaletteHandle == NULL) return FALSE;
1013 
1014     /* Set the new palette for the framebuffer */
1015     OldPalette = SelectPalette(GuiData->hMemDC, PaletteHandle, FALSE);
1016     if (OldPalette == NULL) return FALSE;
1017 
1018     /* Specify the use of the system palette for the framebuffer */
1019     SetSystemPaletteUse(GuiData->hMemDC, PaletteUsage);
1020 
1021     /* Realize the (logical) palette */
1022     RealizePalette(GuiData->hMemDC);
1023 
1024     /* Save the original system palette handle */
1025     if (GuiData->hSysPalette == NULL) GuiData->hSysPalette = OldPalette;
1026 
1027     return TRUE;
1028 }
1029 
1030 static ULONG NTAPI
1031 GuiGetDisplayMode(IN OUT PFRONTEND This)
1032 {
1033     PGUI_CONSOLE_DATA GuiData = This->Context;
1034     ULONG DisplayMode = 0;
1035 
1036     if (GuiData->GuiInfo.FullScreen)
1037         DisplayMode |= CONSOLE_FULLSCREEN_HARDWARE; // CONSOLE_FULLSCREEN
1038     else
1039         DisplayMode |= CONSOLE_WINDOWED;
1040 
1041     return DisplayMode;
1042 }
1043 
1044 static BOOL NTAPI
1045 GuiSetDisplayMode(IN OUT PFRONTEND This,
1046                   ULONG NewMode)
1047 {
1048     PGUI_CONSOLE_DATA GuiData = This->Context;
1049     BOOL FullScreen;
1050 
1051     if (NewMode & ~(CONSOLE_FULLSCREEN_MODE | CONSOLE_WINDOWED_MODE))
1052         return FALSE;
1053 
1054     /* Do nothing if the window is hidden */
1055     if (!GuiData->IsWindowVisible) return TRUE;
1056 
1057     FullScreen = ((NewMode & CONSOLE_FULLSCREEN_MODE) != 0);
1058 
1059     if (FullScreen != GuiData->GuiInfo.FullScreen)
1060     {
1061         SwitchFullScreen(GuiData, FullScreen);
1062     }
1063 
1064     return TRUE;
1065 }
1066 
1067 static INT NTAPI
1068 GuiShowMouseCursor(IN OUT PFRONTEND This,
1069                    BOOL Show)
1070 {
1071     PGUI_CONSOLE_DATA GuiData = This->Context;
1072 
1073     if (GuiData->IsWindowVisible)
1074     {
1075         /* Set the reference count */
1076         if (Show) ++GuiData->MouseCursorRefCount;
1077         else      --GuiData->MouseCursorRefCount;
1078 
1079         /* Effectively show (or hide) the cursor (use special values for (w|l)Param) */
1080         PostMessageW(GuiData->hWindow, WM_SETCURSOR, -1, -1);
1081     }
1082 
1083     return GuiData->MouseCursorRefCount;
1084 }
1085 
1086 static BOOL NTAPI
1087 GuiSetMouseCursor(IN OUT PFRONTEND This,
1088                   HCURSOR CursorHandle)
1089 {
1090     PGUI_CONSOLE_DATA GuiData = This->Context;
1091 
1092     /* Do nothing if the window is hidden */
1093     if (!GuiData->IsWindowVisible) return TRUE;
1094 
1095     /*
1096      * Set the cursor's handle. If the given handle is NULL,
1097      * then restore the default cursor.
1098      */
1099     GuiData->hCursor = (CursorHandle ? CursorHandle : ghDefaultCursor);
1100 
1101     /* Effectively modify the shape of the cursor (use special values for (w|l)Param) */
1102     PostMessageW(GuiData->hWindow, WM_SETCURSOR, -1, -1);
1103 
1104     return TRUE;
1105 }
1106 
1107 static HMENU NTAPI
1108 GuiMenuControl(IN OUT PFRONTEND This,
1109                UINT CmdIdLow,
1110                UINT CmdIdHigh)
1111 {
1112     PGUI_CONSOLE_DATA GuiData = This->Context;
1113 
1114     GuiData->CmdIdLow  = CmdIdLow ;
1115     GuiData->CmdIdHigh = CmdIdHigh;
1116 
1117     return GetSystemMenu(GuiData->hWindow, FALSE);
1118 }
1119 
1120 static BOOL NTAPI
1121 GuiSetMenuClose(IN OUT PFRONTEND This,
1122                 BOOL Enable)
1123 {
1124     /*
1125      * NOTE: See http://www.mail-archive.com/harbour@harbour-project.org/msg27509.html
1126      * or http://harbour-devel.1590103.n2.nabble.com/Question-about-hb-gt-win-CtrlHandler-usage-td4670862i20.html
1127      * for more information.
1128      */
1129 
1130     PGUI_CONSOLE_DATA GuiData = This->Context;
1131     HMENU hSysMenu = GetSystemMenu(GuiData->hWindow, FALSE);
1132 
1133     if (hSysMenu == NULL) return FALSE;
1134 
1135     GuiData->IsCloseButtonEnabled = Enable;
1136     EnableMenuItem(hSysMenu, SC_CLOSE, MF_BYCOMMAND | (Enable ? MF_ENABLED : MF_GRAYED));
1137 
1138     return TRUE;
1139 }
1140 
1141 static FRONTEND_VTBL GuiVtbl =
1142 {
1143     GuiInitFrontEnd,
1144     GuiDeinitFrontEnd,
1145     GuiDrawRegion,
1146     GuiWriteStream,
1147     GuiRingBell,
1148     GuiSetCursorInfo,
1149     GuiSetScreenInfo,
1150     GuiResizeTerminal,
1151     GuiSetActiveScreenBuffer,
1152     GuiReleaseScreenBuffer,
1153     GuiRefreshInternalInfo,
1154     GuiChangeTitle,
1155     GuiChangeIcon,
1156     GuiGetConsoleWindowHandle,
1157     GuiGetLargestConsoleWindowSize,
1158     GuiGetSelectionInfo,
1159     GuiSetPalette,
1160     GuiGetDisplayMode,
1161     GuiSetDisplayMode,
1162     GuiShowMouseCursor,
1163     GuiSetMouseCursor,
1164     GuiMenuControl,
1165     GuiSetMenuClose,
1166 };
1167 
1168 
1169 NTSTATUS NTAPI
1170 GuiLoadFrontEnd(IN OUT PFRONTEND FrontEnd,
1171                 IN OUT PCONSOLE_STATE_INFO ConsoleInfo,
1172                 IN OUT PCONSOLE_INIT_INFO ConsoleInitInfo,
1173                 IN HANDLE ConsoleLeaderProcessHandle)
1174 {
1175     PCONSOLE_START_INFO ConsoleStartInfo;
1176     PGUI_INIT_INFO GuiInitInfo;
1177 
1178     if (FrontEnd == NULL || ConsoleInfo == NULL || ConsoleInitInfo == NULL)
1179         return STATUS_INVALID_PARAMETER;
1180 
1181     ConsoleStartInfo = ConsoleInitInfo->ConsoleStartInfo;
1182 
1183     /*
1184      * Initialize a private initialization info structure for later use.
1185      * It must be freed by a call to GuiUnloadFrontEnd or GuiInitFrontEnd.
1186      */
1187     GuiInitInfo = ConsoleAllocHeap(HEAP_ZERO_MEMORY, sizeof(*GuiInitInfo));
1188     if (GuiInitInfo == NULL) return STATUS_NO_MEMORY;
1189 
1190     /* Initialize GUI terminal emulator common functionalities */
1191     if (!GuiInit(ConsoleInitInfo, ConsoleLeaderProcessHandle, GuiInitInfo))
1192     {
1193         ConsoleFreeHeap(GuiInitInfo);
1194         return STATUS_UNSUCCESSFUL;
1195     }
1196 
1197     /*
1198      * Load terminal settings
1199      */
1200 #if 0
1201     /* Impersonate the caller in order to retrieve settings in its context */
1202     // if (!CsrImpersonateClient(NULL))
1203         // return STATUS_UNSUCCESSFUL;
1204     CsrImpersonateClient(NULL);
1205 
1206     /* 1. Load the default settings */
1207     GuiConsoleGetDefaultSettings(&GuiInitInfo->TermInfo);
1208 #endif
1209 
1210     GuiInitInfo->TermInfo.ShowWindow = SW_SHOWNORMAL;
1211 
1212     if (ConsoleInitInfo->IsWindowVisible)
1213     {
1214         /* 2. Load the remaining console settings via the registry */
1215         if ((ConsoleStartInfo->dwStartupFlags & STARTF_TITLEISLINKNAME) == 0)
1216         {
1217 #if 0
1218             /* Load the terminal infos from the registry */
1219             GuiConsoleReadUserSettings(&GuiInitInfo->TermInfo);
1220 #endif
1221 
1222             /*
1223              * Now, update them with the properties the user might gave to us
1224              * via the STARTUPINFO structure before calling CreateProcess
1225              * (and which was transmitted via the ConsoleStartInfo structure).
1226              * We therefore overwrite the values read in the registry.
1227              */
1228             if (ConsoleStartInfo->dwStartupFlags & STARTF_USESHOWWINDOW)
1229             {
1230                 GuiInitInfo->TermInfo.ShowWindow = ConsoleStartInfo->wShowWindow;
1231             }
1232             if (ConsoleStartInfo->dwStartupFlags & STARTF_USEPOSITION)
1233             {
1234                 ConsoleInfo->AutoPosition = FALSE;
1235                 ConsoleInfo->WindowPosition.x = ConsoleStartInfo->dwWindowOrigin.X;
1236                 ConsoleInfo->WindowPosition.y = ConsoleStartInfo->dwWindowOrigin.Y;
1237             }
1238             if (ConsoleStartInfo->dwStartupFlags & STARTF_RUNFULLSCREEN)
1239             {
1240                 ConsoleInfo->FullScreen = TRUE;
1241             }
1242         }
1243     }
1244 
1245 #if 0
1246     /* Revert impersonation */
1247     CsrRevertToSelf();
1248 #endif
1249 
1250     // Font data
1251     StringCchCopyNW(GuiInitInfo->TermInfo.FaceName, ARRAYSIZE(GuiInitInfo->TermInfo.FaceName),
1252                     ConsoleInfo->FaceName, ARRAYSIZE(ConsoleInfo->FaceName));
1253     GuiInitInfo->TermInfo.FontFamily = ConsoleInfo->FontFamily;
1254     GuiInitInfo->TermInfo.FontSize   = ConsoleInfo->FontSize;
1255     GuiInitInfo->TermInfo.FontWeight = ConsoleInfo->FontWeight;
1256 
1257     // Display
1258     GuiInitInfo->TermInfo.FullScreen   = ConsoleInfo->FullScreen;
1259     // GuiInitInfo->TermInfo.ShowWindow;
1260     GuiInitInfo->TermInfo.AutoPosition = ConsoleInfo->AutoPosition;
1261     GuiInitInfo->TermInfo.WindowOrigin = ConsoleInfo->WindowPosition;
1262 
1263     /* Initialize the icon handles */
1264     // if (ConsoleStartInfo->hIcon != NULL)
1265         GuiInitInfo->hIcon = ConsoleStartInfo->hIcon;
1266     // else
1267         // GuiInitInfo->hIcon = ghDefaultIcon;
1268 
1269     // if (ConsoleStartInfo->hIconSm != NULL)
1270         GuiInitInfo->hIconSm = ConsoleStartInfo->hIconSm;
1271     // else
1272         // GuiInitInfo->hIconSm = ghDefaultIconSm;
1273 
1274     // ASSERT(GuiInitInfo->hIcon && GuiInitInfo->hIconSm);
1275 
1276     GuiInitInfo->IsWindowVisible = ConsoleInitInfo->IsWindowVisible;
1277 
1278     /* Finally, initialize the frontend structure */
1279     FrontEnd->Vtbl     = &GuiVtbl;
1280     FrontEnd->Context  = NULL;
1281     FrontEnd->Context2 = GuiInitInfo;
1282 
1283     return STATUS_SUCCESS;
1284 }
1285 
1286 NTSTATUS NTAPI
1287 GuiUnloadFrontEnd(IN OUT PFRONTEND FrontEnd)
1288 {
1289     if (FrontEnd == NULL) return STATUS_INVALID_PARAMETER;
1290 
1291     if (FrontEnd->Context ) GuiDeinitFrontEnd(FrontEnd);
1292     if (FrontEnd->Context2) ConsoleFreeHeap(FrontEnd->Context2);
1293 
1294     return STATUS_SUCCESS;
1295 }
1296 
1297 /* EOF */
1298