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