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