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