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