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                 /*
233                 while (PeekMessageW(&TempMsg, NULL, 0, 0, PM_REMOVE))
234                 {
235                     TranslateMessage(&TempMsg);
236                     DispatchMessageW(&TempMsg);
237                 }*/
238                 while (PeekMessageW(&TempMsg, NULL, 0, 0, PM_REMOVE)) ;
239 
240                 if (GuiData->hWindow == NULL) continue;
241 
242                 DestroyWindow(GuiData->hWindow);
243 
244                 NtSetEvent(GuiData->hGuiTermEvent, NULL);
245 
246                 if (InterlockedDecrement(&WindowCount) == 0)
247                 {
248                     DPRINT("CONSRV: Going to quit the Input Thread 0x%p\n", InputThreadId);
249                     goto Quit;
250                 }
251 
252                 continue;
253             }
254         }
255 
256         TranslateMessage(&msg);
257         DispatchMessageW(&msg);
258     }
259 
260 Quit:
261     DPRINT("CONSRV: Quit the Input Thread 0x%p, Status = 0x%08lx\n", InputThreadId, Status);
262 
263     /* Remove this console input thread from this desktop */
264     // DesktopConsoleThreadInfo.DesktopHandle;
265     DesktopConsoleThreadInfo.ThreadId = 0;
266     NtUserConsoleControl(ConsoleCtrlDesktopConsoleThread,
267                          &DesktopConsoleThreadInfo,
268                          sizeof(DesktopConsoleThreadInfo));
269 
270     /* Close the duplicated desktop handle */
271     CloseDesktop(DesktopConsoleThreadInfo.DesktopHandle); // NtUserCloseDesktop
272 
273     /* Cleanup CSR thread */
274     if (pcsrt)
275     {
276         if (hThread != pcsrt->ThreadHandle)
277             DPRINT1("WARNING!! hThread (0x%p) != pcsrt->ThreadHandle (0x%p), you may expect crashes soon!!\n", hThread, pcsrt->ThreadHandle);
278 
279         CsrDereferenceThread(pcsrt);
280     }
281 
282     /* Exit the thread */
283     RtlExitUserThread(Status);
284     return 0;
285 }
286 
287 // FIXME: Maybe return a NTSTATUS
288 static BOOL
289 GuiInit(IN PCONSOLE_INIT_INFO ConsoleInitInfo,
290         IN HANDLE ConsoleLeaderProcessHandle,
291         IN OUT PGUI_INIT_INFO GuiInitInfo)
292 {
293     BOOL Success = TRUE;
294     UNICODE_STRING DesktopPath;
295     DESKTOP_CONSOLE_THREAD DesktopConsoleThreadInfo;
296     HWINSTA hWinSta;
297     HDESK hDesk;
298 
299     NTSTATUS Status;
300     HANDLE hInputThread;
301     CLIENT_ID ClientId;
302 
303     /*
304      * Initialize and register the console window class, if needed.
305      */
306     if (!ConsInitialized)
307     {
308         if (!RegisterConWndClass(ConSrvDllInstance)) return FALSE;
309         ConsInitialized = TRUE;
310     }
311 
312     /*
313      * Set-up the console input thread. We have
314      * one console input thread per desktop.
315      */
316 
317     if (!CsrImpersonateClient(NULL))
318         // return STATUS_BAD_IMPERSONATION_LEVEL;
319         return FALSE;
320 
321     if (ConsoleInitInfo->DesktopLength)
322     {
323         DesktopPath.MaximumLength = ConsoleInitInfo->DesktopLength;
324         DesktopPath.Length = DesktopPath.MaximumLength - sizeof(UNICODE_NULL);
325         DesktopPath.Buffer = ConsoleInitInfo->Desktop;
326     }
327     else
328     {
329         RtlInitUnicodeString(&DesktopPath, L"Default");
330     }
331 
332     hDesk = NtUserResolveDesktop(ConsoleLeaderProcessHandle,
333                                  &DesktopPath,
334                                  0,
335                                  &hWinSta);
336     DPRINT("NtUserResolveDesktop(DesktopPath = '%wZ') returned hDesk = 0x%p; hWinSta = 0x%p\n",
337            &DesktopPath, hDesk, hWinSta);
338 
339     CsrRevertToSelf();
340 
341     if (hDesk == NULL) return FALSE;
342 
343     /*
344      * We need to see whether we need to create a
345      * new console input thread for this desktop.
346      */
347     DesktopConsoleThreadInfo.DesktopHandle = hDesk;
348     DesktopConsoleThreadInfo.ThreadId = (ULONG_PTR)INVALID_HANDLE_VALUE; // Special value to say we just want to retrieve the thread ID.
349     NtUserConsoleControl(ConsoleCtrlDesktopConsoleThread,
350                          &DesktopConsoleThreadInfo,
351                          sizeof(DesktopConsoleThreadInfo));
352     DPRINT("NtUserConsoleControl returned ThreadId = 0x%p\n", DesktopConsoleThreadInfo.ThreadId);
353 
354     /*
355      * Save the opened window station and desktop handles in the initialization
356      * structure. They will be used later on, and released, by the GUI frontend.
357      */
358     GuiInitInfo->WinSta  = hWinSta;
359     GuiInitInfo->Desktop = hDesk;
360 
361     /* Here GuiInitInfo contains original handles */
362 
363     /* If we already have a console input thread on this desktop... */
364     if (DesktopConsoleThreadInfo.ThreadId != 0)
365     {
366         /* ... just use it... */
367         DPRINT("Using input thread InputThreadId = 0x%p\n", DesktopConsoleThreadInfo.ThreadId);
368         GuiInitInfo->InputThreadId = DesktopConsoleThreadInfo.ThreadId;
369         goto Quit;
370     }
371 
372     /* ... otherwise create a new one. */
373 
374     /* Initialize a startup event for the thread to signal it */
375     Status = NtCreateEvent(&GuiInitInfo->GuiThreadStartupEvent, EVENT_ALL_ACCESS,
376                            NULL, SynchronizationEvent, FALSE);
377     if (!NT_SUCCESS(Status))
378     {
379         Success = FALSE;
380         goto Quit;
381     }
382 
383     /*
384      * Duplicate the desktop handle for the console input thread internal needs.
385      * If it happens to need also a window station handle in the future, then
386      * it is there that you also need to duplicate the window station handle!
387      *
388      * Note also that we are going to temporarily overwrite the stored handles
389      * in GuiInitInfo because it happens that we use also this structure to give
390      * the duplicated handles to the input thread that is going to initialize.
391      * After the input thread finishes its initialization, we restore the handles
392      * in GuiInitInfo to their old values.
393      */
394     Status = NtDuplicateObject(NtCurrentProcess(),
395                                hDesk,
396                                NtCurrentProcess(),
397                                (PHANDLE)&GuiInitInfo->Desktop,
398                                0, 0, DUPLICATE_SAME_ACCESS);
399     if (!NT_SUCCESS(Status))
400     {
401         Success = FALSE;
402         goto Quit;
403     }
404 
405     /* Here GuiInitInfo contains duplicated handles */
406 
407     Status = RtlCreateUserThread(NtCurrentProcess(),
408                                  NULL,
409                                  TRUE, // Start the thread in suspended state
410                                  0,
411                                  0,
412                                  0,
413                                  (PVOID)GuiConsoleInputThread,
414                                  (PVOID)GuiInitInfo,
415                                  &hInputThread,
416                                  &ClientId);
417     if (NT_SUCCESS(Status))
418     {
419         /* Add it as a static server thread and resume it */
420         CsrAddStaticServerThread(hInputThread, &ClientId, 0);
421         Status = NtResumeThread(hInputThread, NULL);
422     }
423     DPRINT("Thread creation hInputThread = 0x%p, InputThreadId = 0x%p, Status = 0x%08lx\n",
424            hInputThread, ClientId.UniqueThread, Status);
425 
426     if (!NT_SUCCESS(Status) || hInputThread == NULL)
427     {
428         /* Close the thread's handle */
429         if (hInputThread) NtClose(hInputThread);
430 
431         /* We need to close here the duplicated desktop handle */
432         CloseDesktop(GuiInitInfo->Desktop); // NtUserCloseDesktop
433 
434         /* Close the startup event and bail out */
435         NtClose(GuiInitInfo->GuiThreadStartupEvent);
436 
437         DPRINT1("CONSRV: Failed to create graphics console thread.\n");
438         Success = FALSE;
439         goto Quit;
440     }
441 
442     /* No need to close hInputThread, this is done by CSR automatically */
443 
444     /* Wait for the thread to finish its initialization, and close the startup event */
445     NtWaitForSingleObject(GuiInitInfo->GuiThreadStartupEvent, FALSE, NULL);
446     NtClose(GuiInitInfo->GuiThreadStartupEvent);
447 
448     /*
449      * Save the input thread ID for later use, and restore the original handles.
450      * The copies are held by the console input thread.
451      */
452     GuiInitInfo->InputThreadId = (ULONG_PTR)ClientId.UniqueThread;
453     GuiInitInfo->WinSta  = hWinSta;
454     GuiInitInfo->Desktop = hDesk;
455 
456     /* Here GuiInitInfo contains again original handles */
457 
458 Quit:
459     if (!Success)
460     {
461         /*
462          * Close the original handles. Do not use the copies in GuiInitInfo
463          * because we may have failed in the middle of the duplicate operation
464          * and the handles stored in GuiInitInfo may have changed.
465          */
466         CloseDesktop(hDesk); // NtUserCloseDesktop
467         CloseWindowStation(hWinSta); // NtUserCloseWindowStation
468     }
469 
470     return Success;
471 }
472 
473 
474 /******************************************************************************
475  *                             GUI Console Driver                             *
476  ******************************************************************************/
477 
478 static VOID NTAPI
479 GuiDeinitFrontEnd(IN OUT PFRONTEND This);
480 
481 static NTSTATUS NTAPI
482 GuiInitFrontEnd(IN OUT PFRONTEND This,
483                 IN PCONSRV_CONSOLE Console)
484 {
485     PGUI_INIT_INFO GuiInitInfo;
486     PGUI_CONSOLE_DATA GuiData;
487 
488     if (This == NULL || Console == NULL || This->Context2 == NULL)
489         return STATUS_INVALID_PARAMETER;
490 
491     ASSERT(This->Console == Console);
492 
493     GuiInitInfo = This->Context2;
494 
495     /* Terminal data allocation */
496     GuiData = ConsoleAllocHeap(HEAP_ZERO_MEMORY, sizeof(*GuiData));
497     if (!GuiData)
498     {
499         DPRINT1("CONSRV: Failed to create GUI_CONSOLE_DATA\n");
500         return STATUS_UNSUCCESSFUL;
501     }
502     /// /* HACK */ Console->FrontEndIFace.Context = (PVOID)GuiData; /* HACK */
503     GuiData->Console      = Console;
504     GuiData->ActiveBuffer = Console->ActiveBuffer;
505     GuiData->hWindow = NULL;
506     GuiData->IsWindowVisible = GuiInitInfo->IsWindowVisible;
507 
508     /* The console can be resized */
509     Console->FixedSize = FALSE;
510 
511     InitializeCriticalSection(&GuiData->Lock);
512 
513     /*
514      * Set up GUI data
515      */
516     RtlCopyMemory(&GuiData->GuiInfo, &GuiInitInfo->TermInfo, sizeof(GuiInitInfo->TermInfo));
517 
518     /* Initialize the icon handles */
519     if (GuiInitInfo->hIcon != NULL)
520         GuiData->hIcon = GuiInitInfo->hIcon;
521     else
522         GuiData->hIcon = ghDefaultIcon;
523 
524     if (GuiInitInfo->hIconSm != NULL)
525         GuiData->hIconSm = GuiInitInfo->hIconSm;
526     else
527         GuiData->hIconSm = ghDefaultIconSm;
528 
529     ASSERT(GuiData->hIcon && GuiData->hIconSm);
530 
531     /* Mouse is shown by default with its default cursor shape */
532     GuiData->hCursor = ghDefaultCursor;
533     GuiData->MouseCursorRefCount = 0;
534 
535     /* A priori don't ignore mouse signals */
536     GuiData->IgnoreNextMouseSignal = FALSE;
537     /* Initialize HACK FOR CORE-8394. See conwnd.c!OnMouse for more details. */
538     GuiData->HackCORE8394IgnoreNextMove = FALSE;
539 
540     /* Close button and the corresponding system menu item are enabled by default */
541     GuiData->IsCloseButtonEnabled = TRUE;
542 
543     /* There is no user-reserved menu id range by default */
544     GuiData->CmdIdLow = GuiData->CmdIdHigh = 0;
545 
546     /* Initialize the selection */
547     RtlZeroMemory(&GuiData->Selection, sizeof(GuiData->Selection));
548     GuiData->Selection.dwFlags = CONSOLE_NO_SELECTION;
549     RtlZeroMemory(&GuiData->dwSelectionCursor, sizeof(GuiData->dwSelectionCursor));
550     GuiData->LineSelection = FALSE; // Default to block selection
551     // TODO: Retrieve the selection mode via the registry.
552 
553     GuiData->InputThreadId = GuiInitInfo->InputThreadId;
554     GuiData->WinSta  = GuiInitInfo->WinSta;
555     GuiData->Desktop = GuiInitInfo->Desktop;
556 
557     /* Finally, finish to initialize the frontend structure */
558     This->Context  = GuiData;
559     ConsoleFreeHeap(This->Context2);
560     This->Context2 = NULL;
561 
562     /*
563      * We need to wait until the GUI has been fully initialized
564      * to retrieve custom settings i.e. WindowSize etc...
565      * Ideally we could use SendNotifyMessage for this but its not
566      * yet implemented.
567      */
568     NtCreateEvent(&GuiData->hGuiInitEvent, EVENT_ALL_ACCESS,
569                   NULL, SynchronizationEvent, FALSE);
570     NtCreateEvent(&GuiData->hGuiTermEvent, EVENT_ALL_ACCESS,
571                   NULL, SynchronizationEvent, FALSE);
572 
573     DPRINT("GUI - Checkpoint\n");
574 
575     /* Create the terminal window */
576     PostThreadMessageW(GuiData->InputThreadId, PM_CREATE_CONSOLE, 0, (LPARAM)GuiData);
577 
578     /* Wait until initialization has finished */
579     NtWaitForSingleObject(GuiData->hGuiInitEvent, FALSE, NULL);
580     DPRINT("OK we created the console window\n");
581     NtClose(GuiData->hGuiInitEvent);
582     GuiData->hGuiInitEvent = NULL;
583 
584     /* Check whether we really succeeded in initializing the terminal window */
585     if (GuiData->hWindow == NULL)
586     {
587         DPRINT("GuiInitConsole - We failed at creating a new terminal window\n");
588         GuiDeinitFrontEnd(This);
589         return STATUS_UNSUCCESSFUL;
590     }
591 
592     return STATUS_SUCCESS;
593 }
594 
595 static VOID NTAPI
596 GuiDeinitFrontEnd(IN OUT PFRONTEND This)
597 {
598     PGUI_CONSOLE_DATA GuiData = This->Context;
599 
600     DPRINT("Send PM_DESTROY_CONSOLE message and wait on hGuiTermEvent...\n");
601     PostThreadMessageW(GuiData->InputThreadId, PM_DESTROY_CONSOLE, 0, (LPARAM)GuiData);
602     NtWaitForSingleObject(GuiData->hGuiTermEvent, FALSE, NULL);
603     DPRINT("hGuiTermEvent set\n");
604     NtClose(GuiData->hGuiTermEvent);
605     GuiData->hGuiTermEvent = NULL;
606 
607     CloseDesktop(GuiData->Desktop); // NtUserCloseDesktop
608     CloseWindowStation(GuiData->WinSta); // NtUserCloseWindowStation
609 
610     DPRINT("Destroying icons !! - GuiData->hIcon = 0x%p ; ghDefaultIcon = 0x%p ; GuiData->hIconSm = 0x%p ; ghDefaultIconSm = 0x%p\n",
611             GuiData->hIcon, ghDefaultIcon, GuiData->hIconSm, ghDefaultIconSm);
612     if (GuiData->hIcon != NULL && GuiData->hIcon != ghDefaultIcon)
613     {
614         DPRINT("Destroy hIcon\n");
615         DestroyIcon(GuiData->hIcon);
616     }
617     if (GuiData->hIconSm != NULL && GuiData->hIconSm != ghDefaultIconSm)
618     {
619         DPRINT("Destroy hIconSm\n");
620         DestroyIcon(GuiData->hIconSm);
621     }
622 
623     This->Context = NULL;
624     DeleteCriticalSection(&GuiData->Lock);
625     ConsoleFreeHeap(GuiData);
626 
627     DPRINT("Quit GuiDeinitFrontEnd\n");
628 }
629 
630 static VOID NTAPI
631 GuiDrawRegion(IN OUT PFRONTEND This,
632               SMALL_RECT* Region)
633 {
634     PGUI_CONSOLE_DATA GuiData = This->Context;
635 
636     /* Do nothing if the window is hidden */
637     if (!GuiData->IsWindowVisible) return;
638 
639     DrawRegion(GuiData, Region);
640 }
641 
642 static VOID NTAPI
643 GuiWriteStream(IN OUT PFRONTEND This,
644                SMALL_RECT* Region,
645                SHORT CursorStartX,
646                SHORT CursorStartY,
647                UINT ScrolledLines,
648                PWCHAR Buffer,
649                UINT Length)
650 {
651     PGUI_CONSOLE_DATA GuiData = This->Context;
652     PCONSOLE_SCREEN_BUFFER Buff;
653     SHORT CursorEndX, CursorEndY;
654     RECT ScrollRect;
655 
656     if (NULL == GuiData || NULL == GuiData->hWindow) return;
657 
658     /* Do nothing if the window is hidden */
659     if (!GuiData->IsWindowVisible) return;
660 
661     Buff = GuiData->ActiveBuffer;
662     if (GetType(Buff) != TEXTMODE_BUFFER) return;
663 
664     if (0 != ScrolledLines)
665     {
666         ScrollRect.left = 0;
667         ScrollRect.top = 0;
668         ScrollRect.right = Buff->ViewSize.X * GuiData->CharWidth;
669         ScrollRect.bottom = Region->Top * GuiData->CharHeight;
670 
671         ScrollWindowEx(GuiData->hWindow,
672                        0,
673                        -(int)(ScrolledLines * GuiData->CharHeight),
674                        &ScrollRect,
675                        NULL,
676                        NULL,
677                        NULL,
678                        SW_INVALIDATE);
679     }
680 
681     DrawRegion(GuiData, Region);
682 
683     if (CursorStartX < Region->Left || Region->Right < CursorStartX
684             || CursorStartY < Region->Top || Region->Bottom < CursorStartY)
685     {
686         InvalidateCell(GuiData, CursorStartX, CursorStartY);
687     }
688 
689     CursorEndX = Buff->CursorPosition.X;
690     CursorEndY = Buff->CursorPosition.Y;
691     if ((CursorEndX < Region->Left || Region->Right < CursorEndX
692             || CursorEndY < Region->Top || Region->Bottom < CursorEndY)
693             && (CursorEndX != CursorStartX || CursorEndY != CursorStartY))
694     {
695         InvalidateCell(GuiData, CursorEndX, CursorEndY);
696     }
697 
698     // HACK!!
699     // Set up the update timer (very short interval) - this is a "hack" for getting the OS to
700     // repaint the window without having it just freeze up and stay on the screen permanently.
701     Buff->CursorBlinkOn = TRUE;
702     SetTimer(GuiData->hWindow, CONGUI_UPDATE_TIMER, CONGUI_UPDATE_TIME, NULL);
703 }
704 
705 /* static */ VOID NTAPI
706 GuiRingBell(IN OUT PFRONTEND This)
707 {
708     PGUI_CONSOLE_DATA GuiData = This->Context;
709 
710     /* Emit an error beep sound */
711     SendNotifyMessage(GuiData->hWindow, PM_CONSOLE_BEEP, 0, 0);
712 }
713 
714 static BOOL NTAPI
715 GuiSetCursorInfo(IN OUT PFRONTEND This,
716                  PCONSOLE_SCREEN_BUFFER Buff)
717 {
718     PGUI_CONSOLE_DATA GuiData = This->Context;
719 
720     /* Do nothing if the window is hidden */
721     if (!GuiData->IsWindowVisible) return TRUE;
722 
723     if (GuiData->ActiveBuffer == Buff)
724     {
725         InvalidateCell(GuiData, Buff->CursorPosition.X, Buff->CursorPosition.Y);
726     }
727 
728     return TRUE;
729 }
730 
731 static BOOL NTAPI
732 GuiSetScreenInfo(IN OUT PFRONTEND This,
733                  PCONSOLE_SCREEN_BUFFER Buff,
734                  SHORT OldCursorX,
735                  SHORT OldCursorY)
736 {
737     PGUI_CONSOLE_DATA GuiData = This->Context;
738 
739     /* Do nothing if the window is hidden */
740     if (!GuiData->IsWindowVisible) return TRUE;
741 
742     if (GuiData->ActiveBuffer == Buff)
743     {
744         /* Redraw char at old position (remove cursor) */
745         InvalidateCell(GuiData, OldCursorX, OldCursorY);
746         /* Redraw char at new position (show cursor) */
747         InvalidateCell(GuiData, Buff->CursorPosition.X, Buff->CursorPosition.Y);
748     }
749 
750     return TRUE;
751 }
752 
753 static VOID NTAPI
754 GuiResizeTerminal(IN OUT PFRONTEND This)
755 {
756     PGUI_CONSOLE_DATA GuiData = This->Context;
757 
758     /* Resize the window to the user's values */
759     PostMessageW(GuiData->hWindow, PM_RESIZE_TERMINAL, 0, 0);
760 }
761 
762 static VOID NTAPI
763 GuiSetActiveScreenBuffer(IN OUT PFRONTEND This)
764 {
765     PGUI_CONSOLE_DATA GuiData = This->Context;
766     PCONSOLE_SCREEN_BUFFER ActiveBuffer;
767     HPALETTE hPalette;
768 
769     EnterCriticalSection(&GuiData->Lock);
770     GuiData->WindowSizeLock = TRUE;
771 
772     InterlockedExchangePointer((PVOID*)&GuiData->ActiveBuffer,
773                                ConDrvGetActiveScreenBuffer(GuiData->Console));
774 
775     GuiData->WindowSizeLock = FALSE;
776     LeaveCriticalSection(&GuiData->Lock);
777 
778     ActiveBuffer = GuiData->ActiveBuffer;
779 
780     /* Change the current palette */
781     if (ActiveBuffer->PaletteHandle == NULL)
782         hPalette = GuiData->hSysPalette;
783     else
784         hPalette = ActiveBuffer->PaletteHandle;
785 
786     DPRINT("GuiSetActiveScreenBuffer using palette 0x%p\n", hPalette);
787 
788     /* Set the new palette for the framebuffer */
789     SelectPalette(GuiData->hMemDC, hPalette, FALSE);
790 
791     /* Specify the use of the system palette for the framebuffer */
792     SetSystemPaletteUse(GuiData->hMemDC, ActiveBuffer->PaletteUsage);
793 
794     /* Realize the (logical) palette */
795     RealizePalette(GuiData->hMemDC);
796 
797     GuiResizeTerminal(This);
798     // ConioDrawConsole(Console);
799 }
800 
801 static VOID NTAPI
802 GuiReleaseScreenBuffer(IN OUT PFRONTEND This,
803                        IN PCONSOLE_SCREEN_BUFFER ScreenBuffer)
804 {
805     PGUI_CONSOLE_DATA GuiData = This->Context;
806 
807     /*
808      * If we were notified to release a screen buffer that is not actually
809      * ours, then just ignore the notification...
810      */
811     if (ScreenBuffer != GuiData->ActiveBuffer) return;
812 
813     /*
814      * ... else, we must release our active buffer. Two cases are present:
815      * - If ScreenBuffer (== GuiData->ActiveBuffer) IS NOT the console
816      *   active screen buffer, then we can safely switch to it.
817      * - If ScreenBuffer IS the console active screen buffer, we must release
818      *   it ONLY.
819      */
820 
821     /* Release the old active palette and set the default one */
822     if (GetCurrentObject(GuiData->hMemDC, OBJ_PAL) == ScreenBuffer->PaletteHandle)
823     {
824         /* Set the new palette */
825         SelectPalette(GuiData->hMemDC, GuiData->hSysPalette, FALSE);
826     }
827 
828     /* Set the adequate active screen buffer */
829     if (ScreenBuffer != GuiData->Console->ActiveBuffer)
830     {
831         GuiSetActiveScreenBuffer(This);
832     }
833     else
834     {
835         EnterCriticalSection(&GuiData->Lock);
836         GuiData->WindowSizeLock = TRUE;
837 
838         InterlockedExchangePointer((PVOID*)&GuiData->ActiveBuffer, NULL);
839 
840         GuiData->WindowSizeLock = FALSE;
841         LeaveCriticalSection(&GuiData->Lock);
842     }
843 }
844 
845 static BOOL NTAPI
846 GuiSetMouseCursor(IN OUT PFRONTEND This,
847                   HCURSOR CursorHandle);
848 
849 static VOID NTAPI
850 GuiRefreshInternalInfo(IN OUT PFRONTEND This)
851 {
852     PGUI_CONSOLE_DATA GuiData = This->Context;
853 
854     /* Update the console leader information held by the window */
855     SetConWndConsoleLeaderCID(GuiData);
856 
857     /*
858      * HACK:
859      * We reset the cursor here so that, when a console app quits, we reset
860      * the cursor to the default one. It's quite a hack since it doesn't proceed
861      * per - console process... This must be fixed.
862      *
863      * See GuiInitConsole(...) for more information.
864      */
865 
866     /* Mouse is shown by default with its default cursor shape */
867     GuiData->MouseCursorRefCount = 0; // Reinitialize the reference counter
868     GuiSetMouseCursor(This, NULL);
869 }
870 
871 static VOID NTAPI
872 GuiChangeTitle(IN OUT PFRONTEND This)
873 {
874     PGUI_CONSOLE_DATA GuiData = This->Context;
875     // PostMessageW(GuiData->hWindow, PM_CONSOLE_SET_TITLE, 0, 0);
876     SetWindowTextW(GuiData->hWindow, GuiData->Console->Title.Buffer);
877 }
878 
879 static BOOL NTAPI
880 GuiChangeIcon(IN OUT PFRONTEND This,
881               HICON IconHandle)
882 {
883     PGUI_CONSOLE_DATA GuiData = This->Context;
884     HICON hIcon, hIconSm;
885 
886     if (IconHandle == NULL)
887     {
888         hIcon   = ghDefaultIcon;
889         hIconSm = ghDefaultIconSm;
890     }
891     else
892     {
893         hIcon   = CopyIcon(IconHandle);
894         hIconSm = CopyIcon(IconHandle);
895     }
896 
897     if (hIcon == NULL)
898         return FALSE;
899 
900     if (hIcon != GuiData->hIcon)
901     {
902         if (GuiData->hIcon != NULL && GuiData->hIcon != ghDefaultIcon)
903         {
904             DestroyIcon(GuiData->hIcon);
905         }
906         if (GuiData->hIconSm != NULL && GuiData->hIconSm != ghDefaultIconSm)
907         {
908             DestroyIcon(GuiData->hIconSm);
909         }
910 
911         GuiData->hIcon   = hIcon;
912         GuiData->hIconSm = hIconSm;
913 
914         DPRINT("Set icons in GuiChangeIcon\n");
915         PostMessageW(GuiData->hWindow, WM_SETICON, ICON_BIG  , (LPARAM)GuiData->hIcon  );
916         PostMessageW(GuiData->hWindow, WM_SETICON, ICON_SMALL, (LPARAM)GuiData->hIconSm);
917     }
918 
919     return TRUE;
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 GetSystemMenu(GuiData->hWindow, FALSE);
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     HMENU hSysMenu = GetSystemMenu(GuiData->hWindow, FALSE);
1135 
1136     if (hSysMenu == NULL) return FALSE;
1137 
1138     GuiData->IsCloseButtonEnabled = Enable;
1139     EnableMenuItem(hSysMenu, SC_CLOSE, MF_BYCOMMAND | (Enable ? MF_ENABLED : MF_GRAYED));
1140 
1141     return TRUE;
1142 }
1143 
1144 static FRONTEND_VTBL GuiVtbl =
1145 {
1146     GuiInitFrontEnd,
1147     GuiDeinitFrontEnd,
1148     GuiDrawRegion,
1149     GuiWriteStream,
1150     GuiRingBell,
1151     GuiSetCursorInfo,
1152     GuiSetScreenInfo,
1153     GuiResizeTerminal,
1154     GuiSetActiveScreenBuffer,
1155     GuiReleaseScreenBuffer,
1156     GuiRefreshInternalInfo,
1157     GuiChangeTitle,
1158     GuiChangeIcon,
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 
1181     if (FrontEnd == NULL || ConsoleInfo == NULL || ConsoleInitInfo == NULL)
1182         return STATUS_INVALID_PARAMETER;
1183 
1184     ConsoleStartInfo = ConsoleInitInfo->ConsoleStartInfo;
1185 
1186     /*
1187      * Initialize a private initialization info structure for later use.
1188      * It must be freed by a call to GuiUnloadFrontEnd or GuiInitFrontEnd.
1189      */
1190     GuiInitInfo = ConsoleAllocHeap(HEAP_ZERO_MEMORY, sizeof(*GuiInitInfo));
1191     if (GuiInitInfo == NULL) return STATUS_NO_MEMORY;
1192 
1193     /* Initialize GUI terminal emulator common functionalities */
1194     if (!GuiInit(ConsoleInitInfo, ConsoleLeaderProcessHandle, GuiInitInfo))
1195     {
1196         ConsoleFreeHeap(GuiInitInfo);
1197         return STATUS_UNSUCCESSFUL;
1198     }
1199 
1200     /*
1201      * Load terminal settings
1202      */
1203 #if 0
1204     /* Impersonate the caller in order to retrieve settings in its context */
1205     // if (!CsrImpersonateClient(NULL))
1206         // return STATUS_UNSUCCESSFUL;
1207     CsrImpersonateClient(NULL);
1208 
1209     /* 1. Load the default settings */
1210     GuiConsoleGetDefaultSettings(&GuiInitInfo->TermInfo);
1211 #endif
1212 
1213     GuiInitInfo->TermInfo.ShowWindow = SW_SHOWNORMAL;
1214 
1215     if (ConsoleInitInfo->IsWindowVisible)
1216     {
1217         /* 2. Load the remaining console settings via the registry */
1218         if ((ConsoleStartInfo->dwStartupFlags & STARTF_TITLEISLINKNAME) == 0)
1219         {
1220 #if 0
1221             /* Load the terminal infos from the registry */
1222             GuiConsoleReadUserSettings(&GuiInitInfo->TermInfo);
1223 #endif
1224 
1225             /*
1226              * Now, update them with the properties the user might gave to us
1227              * via the STARTUPINFO structure before calling CreateProcess
1228              * (and which was transmitted via the ConsoleStartInfo structure).
1229              * We therefore overwrite the values read in the registry.
1230              */
1231             if (ConsoleStartInfo->dwStartupFlags & STARTF_USESHOWWINDOW)
1232             {
1233                 GuiInitInfo->TermInfo.ShowWindow = ConsoleStartInfo->wShowWindow;
1234             }
1235             if (ConsoleStartInfo->dwStartupFlags & STARTF_USEPOSITION)
1236             {
1237                 ConsoleInfo->AutoPosition = FALSE;
1238                 ConsoleInfo->WindowPosition.x = ConsoleStartInfo->dwWindowOrigin.X;
1239                 ConsoleInfo->WindowPosition.y = ConsoleStartInfo->dwWindowOrigin.Y;
1240             }
1241             if (ConsoleStartInfo->dwStartupFlags & STARTF_RUNFULLSCREEN)
1242             {
1243                 ConsoleInfo->FullScreen = TRUE;
1244             }
1245         }
1246     }
1247 
1248 #if 0
1249     /* Revert impersonation */
1250     CsrRevertToSelf();
1251 #endif
1252 
1253     // Font data
1254     StringCchCopyNW(GuiInitInfo->TermInfo.FaceName, ARRAYSIZE(GuiInitInfo->TermInfo.FaceName),
1255                     ConsoleInfo->FaceName, ARRAYSIZE(ConsoleInfo->FaceName));
1256     GuiInitInfo->TermInfo.FontFamily = ConsoleInfo->FontFamily;
1257     GuiInitInfo->TermInfo.FontSize   = ConsoleInfo->FontSize;
1258     GuiInitInfo->TermInfo.FontWeight = ConsoleInfo->FontWeight;
1259 
1260     // Display
1261     GuiInitInfo->TermInfo.FullScreen   = ConsoleInfo->FullScreen;
1262     // GuiInitInfo->TermInfo.ShowWindow;
1263     GuiInitInfo->TermInfo.AutoPosition = ConsoleInfo->AutoPosition;
1264     GuiInitInfo->TermInfo.WindowOrigin = ConsoleInfo->WindowPosition;
1265 
1266     /* Initialize the icon handles */
1267     // if (ConsoleStartInfo->hIcon != NULL)
1268         GuiInitInfo->hIcon = ConsoleStartInfo->hIcon;
1269     // else
1270         // GuiInitInfo->hIcon = ghDefaultIcon;
1271 
1272     // if (ConsoleStartInfo->hIconSm != NULL)
1273         GuiInitInfo->hIconSm = ConsoleStartInfo->hIconSm;
1274     // else
1275         // GuiInitInfo->hIconSm = ghDefaultIconSm;
1276 
1277     // ASSERT(GuiInitInfo->hIcon && GuiInitInfo->hIconSm);
1278 
1279     GuiInitInfo->IsWindowVisible = ConsoleInitInfo->IsWindowVisible;
1280 
1281     /* Finally, initialize the frontend structure */
1282     FrontEnd->Vtbl     = &GuiVtbl;
1283     FrontEnd->Context  = NULL;
1284     FrontEnd->Context2 = GuiInitInfo;
1285 
1286     return STATUS_SUCCESS;
1287 }
1288 
1289 NTSTATUS NTAPI
1290 GuiUnloadFrontEnd(IN OUT PFRONTEND FrontEnd)
1291 {
1292     if (FrontEnd == NULL) return STATUS_INVALID_PARAMETER;
1293 
1294     if (FrontEnd->Context ) GuiDeinitFrontEnd(FrontEnd);
1295     if (FrontEnd->Context2) ConsoleFreeHeap(FrontEnd->Context2);
1296 
1297     return STATUS_SUCCESS;
1298 }
1299 
1300 /* EOF */
1301