8b6bc26bd671470c5cae080f5ee8910141b35e3c
[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 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 // Font data
490 Length = min(wcslen(TermInfo.FaceName) + 1, LF_FACESIZE); // wcsnlen
491 wcsncpy(GuiData->GuiInfo.FaceName, TermInfo.FaceName, LF_FACESIZE);
492 GuiData->GuiInfo.FaceName[Length] = L'\0';
493 GuiData->GuiInfo.FontFamily = TermInfo.FontFamily;
494 GuiData->GuiInfo.FontSize = TermInfo.FontSize;
495 GuiData->GuiInfo.FontWeight = TermInfo.FontWeight;
496
497 // Display
498 GuiData->GuiInfo.FullScreen = TermInfo.FullScreen;
499 GuiData->GuiInfo.ShowWindow = TermInfo.ShowWindow;
500 GuiData->GuiInfo.AutoPosition = TermInfo.AutoPosition;
501 GuiData->GuiInfo.WindowOrigin = TermInfo.WindowOrigin;
502
503 /* Initialize the icon handles to their default values */
504 GuiData->hIcon = ghDefaultIcon;
505 GuiData->hIconSm = ghDefaultIconSm;
506
507 /* Get the associated icon, if any */
508 if (IconPath == NULL || IconPath[0] == L'\0')
509 {
510 IconPath = ConsoleStartInfo->AppPath;
511 IconIndex = 0;
512 }
513 DPRINT("IconPath = %S ; IconIndex = %lu\n", (IconPath ? IconPath : L"n/a"), IconIndex);
514 if (IconPath && IconPath[0] != L'\0')
515 {
516 HICON hIcon = NULL, hIconSm = NULL;
517 PrivateExtractIconExW(IconPath,
518 IconIndex,
519 &hIcon,
520 &hIconSm,
521 1);
522 DPRINT("hIcon = 0x%p ; hIconSm = 0x%p\n", hIcon, hIconSm);
523 if (hIcon != NULL) GuiData->hIcon = hIcon;
524 if (hIconSm != NULL) GuiData->hIconSm = hIconSm;
525 }
526 ASSERT(GuiData->hIcon && GuiData->hIconSm);
527
528 /* Mouse is shown by default with its default cursor shape */
529 GuiData->hCursor = ghDefaultCursor;
530 GuiData->MouseCursorRefCount = 0;
531
532 /* A priori don't ignore mouse signals */
533 GuiData->IgnoreNextMouseSignal = FALSE;
534
535 /* Close button and the corresponding system menu item are enabled by default */
536 GuiData->IsCloseButtonEnabled = TRUE;
537
538 /* There is no user-reserved menu id range by default */
539 GuiData->CmdIdLow = GuiData->CmdIdHigh = 0;
540
541 /* Initialize the selection */
542 RtlZeroMemory(&GuiData->Selection, sizeof(GuiData->Selection));
543 GuiData->Selection.dwFlags = CONSOLE_NO_SELECTION;
544 RtlZeroMemory(&GuiData->dwSelectionCursor, sizeof(GuiData->dwSelectionCursor));
545 GuiData->LineSelection = FALSE; // Default to block selection
546 // TODO: Retrieve the selection mode via the registry.
547
548 /*
549 * We need to wait until the GUI has been fully initialized
550 * to retrieve custom settings i.e. WindowSize etc...
551 * Ideally we could use SendNotifyMessage for this but its not
552 * yet implemented.
553 */
554 GuiData->hGuiInitEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
555
556 DPRINT("GUI - Checkpoint\n");
557
558 /* Create the terminal window */
559 PostMessageW(NotifyWnd, PM_CREATE_CONSOLE, GuiData->GuiInfo.ShowWindow, (LPARAM)GuiData);
560
561 /* Wait until initialization has finished */
562 WaitForSingleObject(GuiData->hGuiInitEvent, INFINITE);
563 DPRINT("OK we created the console window\n");
564 CloseHandle(GuiData->hGuiInitEvent);
565 GuiData->hGuiInitEvent = NULL;
566
567 /* Check whether we really succeeded in initializing the terminal window */
568 if (GuiData->hWindow == NULL)
569 {
570 DPRINT("GuiInitConsole - We failed at creating a new terminal window\n");
571 GuiDeinitFrontEnd(This);
572 return STATUS_UNSUCCESSFUL;
573 }
574
575 /* Finally, finish to initialize the frontend structure */
576 This->Data = GuiData;
577 if (This->OldData) ConsoleFreeHeap(This->OldData);
578 This->OldData = NULL;
579
580 return STATUS_SUCCESS;
581 }
582
583 static VOID NTAPI
584 GuiDeinitFrontEnd(IN OUT PFRONTEND This)
585 {
586 PGUI_CONSOLE_DATA GuiData = This->Data;
587
588 SendMessageW(NotifyWnd, PM_DESTROY_CONSOLE, 0, (LPARAM)GuiData);
589
590 DPRINT("Destroying icons !! - GuiData->hIcon = 0x%p ; ghDefaultIcon = 0x%p ; GuiData->hIconSm = 0x%p ; ghDefaultIconSm = 0x%p\n",
591 GuiData->hIcon, ghDefaultIcon, GuiData->hIconSm, ghDefaultIconSm);
592 if (GuiData->hIcon != NULL && GuiData->hIcon != ghDefaultIcon)
593 {
594 DPRINT("Destroy hIcon\n");
595 DestroyIcon(GuiData->hIcon);
596 }
597 if (GuiData->hIconSm != NULL && GuiData->hIconSm != ghDefaultIconSm)
598 {
599 DPRINT("Destroy hIconSm\n");
600 DestroyIcon(GuiData->hIconSm);
601 }
602
603 This->Data = NULL;
604 DeleteCriticalSection(&GuiData->Lock);
605 ConsoleFreeHeap(GuiData);
606
607 DPRINT("Quit GuiDeinitFrontEnd\n");
608 }
609
610 static VOID NTAPI
611 GuiDrawRegion(IN OUT PFRONTEND This,
612 SMALL_RECT* Region)
613 {
614 PGUI_CONSOLE_DATA GuiData = This->Data;
615 DrawRegion(GuiData, Region);
616 }
617
618 static VOID NTAPI
619 GuiWriteStream(IN OUT PFRONTEND This,
620 SMALL_RECT* Region,
621 SHORT CursorStartX,
622 SHORT CursorStartY,
623 UINT ScrolledLines,
624 PWCHAR Buffer,
625 UINT Length)
626 {
627 PGUI_CONSOLE_DATA GuiData = This->Data;
628 PCONSOLE_SCREEN_BUFFER Buff;
629 SHORT CursorEndX, CursorEndY;
630 RECT ScrollRect;
631
632 if (NULL == GuiData || NULL == GuiData->hWindow) return;
633
634 Buff = GuiData->ActiveBuffer;
635 if (GetType(Buff) != TEXTMODE_BUFFER) return;
636
637 if (0 != ScrolledLines)
638 {
639 ScrollRect.left = 0;
640 ScrollRect.top = 0;
641 ScrollRect.right = Buff->ViewSize.X * GuiData->CharWidth;
642 ScrollRect.bottom = Region->Top * GuiData->CharHeight;
643
644 ScrollWindowEx(GuiData->hWindow,
645 0,
646 -(int)(ScrolledLines * GuiData->CharHeight),
647 &ScrollRect,
648 NULL,
649 NULL,
650 NULL,
651 SW_INVALIDATE);
652 }
653
654 DrawRegion(GuiData, Region);
655
656 if (CursorStartX < Region->Left || Region->Right < CursorStartX
657 || CursorStartY < Region->Top || Region->Bottom < CursorStartY)
658 {
659 InvalidateCell(GuiData, CursorStartX, CursorStartY);
660 }
661
662 CursorEndX = Buff->CursorPosition.X;
663 CursorEndY = Buff->CursorPosition.Y;
664 if ((CursorEndX < Region->Left || Region->Right < CursorEndX
665 || CursorEndY < Region->Top || Region->Bottom < CursorEndY)
666 && (CursorEndX != CursorStartX || CursorEndY != CursorStartY))
667 {
668 InvalidateCell(GuiData, CursorEndX, CursorEndY);
669 }
670
671 // HACK!!
672 // Set up the update timer (very short interval) - this is a "hack" for getting the OS to
673 // repaint the window without having it just freeze up and stay on the screen permanently.
674 Buff->CursorBlinkOn = TRUE;
675 SetTimer(GuiData->hWindow, CONGUI_UPDATE_TIMER, CONGUI_UPDATE_TIME, NULL);
676 }
677
678 static BOOL NTAPI
679 GuiSetCursorInfo(IN OUT PFRONTEND This,
680 PCONSOLE_SCREEN_BUFFER Buff)
681 {
682 PGUI_CONSOLE_DATA GuiData = This->Data;
683
684 if (GuiData->ActiveBuffer == Buff)
685 {
686 InvalidateCell(GuiData, Buff->CursorPosition.X, Buff->CursorPosition.Y);
687 }
688
689 return TRUE;
690 }
691
692 static BOOL NTAPI
693 GuiSetScreenInfo(IN OUT PFRONTEND This,
694 PCONSOLE_SCREEN_BUFFER Buff,
695 SHORT OldCursorX,
696 SHORT OldCursorY)
697 {
698 PGUI_CONSOLE_DATA GuiData = This->Data;
699
700 if (GuiData->ActiveBuffer == Buff)
701 {
702 /* Redraw char at old position (remove cursor) */
703 InvalidateCell(GuiData, OldCursorX, OldCursorY);
704 /* Redraw char at new position (show cursor) */
705 InvalidateCell(GuiData, Buff->CursorPosition.X, Buff->CursorPosition.Y);
706 }
707
708 return TRUE;
709 }
710
711 static VOID NTAPI
712 GuiResizeTerminal(IN OUT PFRONTEND This)
713 {
714 PGUI_CONSOLE_DATA GuiData = This->Data;
715
716 /* Resize the window to the user's values */
717 PostMessageW(GuiData->hWindow, PM_RESIZE_TERMINAL, 0, 0);
718 }
719
720 static VOID NTAPI
721 GuiSetActiveScreenBuffer(IN OUT PFRONTEND This)
722 {
723 PGUI_CONSOLE_DATA GuiData = This->Data;
724 PCONSOLE_SCREEN_BUFFER ActiveBuffer;
725 HPALETTE hPalette;
726
727 EnterCriticalSection(&GuiData->Lock);
728 GuiData->WindowSizeLock = TRUE;
729
730 InterlockedExchangePointer(&GuiData->ActiveBuffer,
731 ConDrvGetActiveScreenBuffer(GuiData->Console));
732
733 GuiData->WindowSizeLock = FALSE;
734 LeaveCriticalSection(&GuiData->Lock);
735
736 ActiveBuffer = GuiData->ActiveBuffer;
737
738 /* Change the current palette */
739 if (ActiveBuffer->PaletteHandle == NULL)
740 {
741 hPalette = GuiData->hSysPalette;
742 }
743 else
744 {
745 hPalette = ActiveBuffer->PaletteHandle;
746 }
747
748 DPRINT1("GuiSetActiveScreenBuffer using palette 0x%p\n", hPalette);
749
750 /* Set the new palette for the framebuffer */
751 SelectPalette(GuiData->hMemDC, hPalette, FALSE);
752
753 /* Specify the use of the system palette for the framebuffer */
754 SetSystemPaletteUse(GuiData->hMemDC, ActiveBuffer->PaletteUsage);
755
756 /* Realize the (logical) palette */
757 RealizePalette(GuiData->hMemDC);
758
759 GuiResizeTerminal(This);
760 // ConioDrawConsole(Console);
761 }
762
763 static VOID NTAPI
764 GuiReleaseScreenBuffer(IN OUT PFRONTEND This,
765 IN PCONSOLE_SCREEN_BUFFER ScreenBuffer)
766 {
767 PGUI_CONSOLE_DATA GuiData = This->Data;
768
769 /*
770 * If we were notified to release a screen buffer that is not actually
771 * ours, then just ignore the notification...
772 */
773 if (ScreenBuffer != GuiData->ActiveBuffer) return;
774
775 /*
776 * ... else, we must release our active buffer. Two cases are present:
777 * - If ScreenBuffer (== GuiData->ActiveBuffer) IS NOT the console
778 * active screen buffer, then we can safely switch to it.
779 * - If ScreenBuffer IS the console active screen buffer, we must release
780 * it ONLY.
781 */
782
783 /* Release the old active palette and set the default one */
784 if (GetCurrentObject(GuiData->hMemDC, OBJ_PAL) == ScreenBuffer->PaletteHandle)
785 {
786 /* Set the new palette */
787 SelectPalette(GuiData->hMemDC, GuiData->hSysPalette, FALSE);
788 }
789
790 /* Set the adequate active screen buffer */
791 if (ScreenBuffer != GuiData->Console->ActiveBuffer)
792 {
793 GuiSetActiveScreenBuffer(This);
794 }
795 else
796 {
797 EnterCriticalSection(&GuiData->Lock);
798 GuiData->WindowSizeLock = TRUE;
799
800 InterlockedExchangePointer(&GuiData->ActiveBuffer, NULL);
801
802 GuiData->WindowSizeLock = FALSE;
803 LeaveCriticalSection(&GuiData->Lock);
804 }
805 }
806
807 static BOOL NTAPI
808 GuiSetMouseCursor(IN OUT PFRONTEND This,
809 HCURSOR CursorHandle);
810
811 static VOID NTAPI
812 GuiRefreshInternalInfo(IN OUT PFRONTEND This)
813 {
814 PGUI_CONSOLE_DATA GuiData = This->Data;
815
816 /* Update the console leader information held by the window */
817 SetConWndConsoleLeaderCID(GuiData);
818
819 /*
820 * HACK:
821 * We reset the cursor here so that, when a console app quits, we reset
822 * the cursor to the default one. It's quite a hack since it doesn't proceed
823 * per - console process... This must be fixed.
824 *
825 * See GuiInitConsole(...) for more information.
826 */
827
828 /* Mouse is shown by default with its default cursor shape */
829 GuiData->MouseCursorRefCount = 0; // Reinitialize the reference counter
830 GuiSetMouseCursor(This, NULL);
831 }
832
833 static VOID NTAPI
834 GuiChangeTitle(IN OUT PFRONTEND This)
835 {
836 PGUI_CONSOLE_DATA GuiData = This->Data;
837 // PostMessageW(GuiData->hWindow, PM_CONSOLE_SET_TITLE, 0, 0);
838 SetWindowText(GuiData->hWindow, GuiData->Console->Title.Buffer);
839 }
840
841 static BOOL NTAPI
842 GuiChangeIcon(IN OUT PFRONTEND This,
843 HICON IconHandle)
844 {
845 PGUI_CONSOLE_DATA GuiData = This->Data;
846 HICON hIcon, hIconSm;
847
848 if (IconHandle == NULL)
849 {
850 hIcon = ghDefaultIcon;
851 hIconSm = ghDefaultIconSm;
852 }
853 else
854 {
855 hIcon = CopyIcon(IconHandle);
856 hIconSm = CopyIcon(IconHandle);
857 }
858
859 if (hIcon == NULL)
860 {
861 return FALSE;
862 }
863
864 if (hIcon != GuiData->hIcon)
865 {
866 if (GuiData->hIcon != NULL && GuiData->hIcon != ghDefaultIcon)
867 {
868 DestroyIcon(GuiData->hIcon);
869 }
870 if (GuiData->hIconSm != NULL && GuiData->hIconSm != ghDefaultIconSm)
871 {
872 DestroyIcon(GuiData->hIconSm);
873 }
874
875 GuiData->hIcon = hIcon;
876 GuiData->hIconSm = hIconSm;
877
878 DPRINT("Set icons in GuiChangeIcon\n");
879 PostMessageW(GuiData->hWindow, WM_SETICON, ICON_BIG , (LPARAM)GuiData->hIcon );
880 PostMessageW(GuiData->hWindow, WM_SETICON, ICON_SMALL, (LPARAM)GuiData->hIconSm);
881 }
882
883 return TRUE;
884 }
885
886 static HWND NTAPI
887 GuiGetConsoleWindowHandle(IN OUT PFRONTEND This)
888 {
889 PGUI_CONSOLE_DATA GuiData = This->Data;
890 return GuiData->hWindow;
891 }
892
893 static VOID NTAPI
894 GuiGetLargestConsoleWindowSize(IN OUT PFRONTEND This,
895 PCOORD pSize)
896 {
897 PGUI_CONSOLE_DATA GuiData = This->Data;
898 PCONSOLE_SCREEN_BUFFER ActiveBuffer;
899 RECT WorkArea;
900 LONG width, height;
901 UINT WidthUnit, HeightUnit;
902
903 if (!pSize) return;
904
905 if (!SystemParametersInfoW(SPI_GETWORKAREA, 0, &WorkArea, 0))
906 {
907 DPRINT1("SystemParametersInfoW failed - What to do ??\n");
908 return;
909 }
910
911 ActiveBuffer = GuiData->ActiveBuffer;
912 if (ActiveBuffer)
913 {
914 GetScreenBufferSizeUnits(ActiveBuffer, GuiData, &WidthUnit, &HeightUnit);
915 }
916 else
917 {
918 /* Default: text mode */
919 WidthUnit = GuiData->CharWidth ;
920 HeightUnit = GuiData->CharHeight;
921 }
922
923 width = WorkArea.right;
924 height = WorkArea.bottom;
925
926 width -= (2 * (GetSystemMetrics(SM_CXFRAME) + GetSystemMetrics(SM_CXEDGE)));
927 height -= (2 * (GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYEDGE)) + GetSystemMetrics(SM_CYCAPTION));
928
929 if (width < 0) width = 0;
930 if (height < 0) height = 0;
931
932 pSize->X = (SHORT)(width / (int)WidthUnit ) /* HACK */ + 2;
933 pSize->Y = (SHORT)(height / (int)HeightUnit) /* HACK */ + 1;
934 }
935
936 static BOOL NTAPI
937 GuiGetSelectionInfo(IN OUT PFRONTEND This,
938 PCONSOLE_SELECTION_INFO pSelectionInfo)
939 {
940 PGUI_CONSOLE_DATA GuiData = This->Data;
941
942 if (pSelectionInfo == NULL) return FALSE;
943
944 ZeroMemory(pSelectionInfo, sizeof(CONSOLE_SELECTION_INFO));
945 if (GuiData->Selection.dwFlags != CONSOLE_NO_SELECTION)
946 RtlCopyMemory(pSelectionInfo, &GuiData->Selection, sizeof(CONSOLE_SELECTION_INFO));
947
948 return TRUE;
949 }
950
951 static BOOL NTAPI
952 GuiSetPalette(IN OUT PFRONTEND This,
953 HPALETTE PaletteHandle,
954 UINT PaletteUsage)
955 {
956 PGUI_CONSOLE_DATA GuiData = This->Data;
957 HPALETTE OldPalette;
958
959 // if (GetType(GuiData->ActiveBuffer) != GRAPHICS_BUFFER) return FALSE;
960 if (PaletteHandle == NULL) return FALSE;
961
962 /* Set the new palette for the framebuffer */
963 OldPalette = SelectPalette(GuiData->hMemDC, PaletteHandle, FALSE);
964 if (OldPalette == NULL) return FALSE;
965
966 /* Specify the use of the system palette for the framebuffer */
967 SetSystemPaletteUse(GuiData->hMemDC, PaletteUsage);
968
969 /* Realize the (logical) palette */
970 RealizePalette(GuiData->hMemDC);
971
972 /* Save the original system palette handle */
973 if (GuiData->hSysPalette == NULL) GuiData->hSysPalette = OldPalette;
974
975 return TRUE;
976 }
977
978 static ULONG NTAPI
979 GuiGetDisplayMode(IN OUT PFRONTEND This)
980 {
981 PGUI_CONSOLE_DATA GuiData = This->Data;
982 ULONG DisplayMode = 0;
983
984 if (GuiData->GuiInfo.FullScreen)
985 DisplayMode |= CONSOLE_FULLSCREEN_HARDWARE; // CONSOLE_FULLSCREEN
986 else
987 DisplayMode |= CONSOLE_WINDOWED;
988
989 return DisplayMode;
990 }
991
992 static BOOL NTAPI
993 GuiSetDisplayMode(IN OUT PFRONTEND This,
994 ULONG NewMode)
995 {
996 PGUI_CONSOLE_DATA GuiData = This->Data;
997 BOOL FullScreen;
998
999 if (NewMode & ~(CONSOLE_FULLSCREEN_MODE | CONSOLE_WINDOWED_MODE))
1000 return FALSE;
1001
1002 FullScreen = ((NewMode & CONSOLE_FULLSCREEN_MODE) != 0);
1003
1004 if (FullScreen != GuiData->GuiInfo.FullScreen)
1005 {
1006 SwitchFullScreen(GuiData, FullScreen);
1007 }
1008
1009 return TRUE;
1010 }
1011
1012 static INT NTAPI
1013 GuiShowMouseCursor(IN OUT PFRONTEND This,
1014 BOOL Show)
1015 {
1016 PGUI_CONSOLE_DATA GuiData = This->Data;
1017
1018 /* Set the reference count */
1019 if (Show) ++GuiData->MouseCursorRefCount;
1020 else --GuiData->MouseCursorRefCount;
1021
1022 /* Effectively show (or hide) the cursor (use special values for (w|l)Param) */
1023 PostMessageW(GuiData->hWindow, WM_SETCURSOR, -1, -1);
1024
1025 return GuiData->MouseCursorRefCount;
1026 }
1027
1028 static BOOL NTAPI
1029 GuiSetMouseCursor(IN OUT PFRONTEND This,
1030 HCURSOR CursorHandle)
1031 {
1032 PGUI_CONSOLE_DATA GuiData = This->Data;
1033
1034 /*
1035 * Set the cursor's handle. If the given handle is NULL,
1036 * then restore the default cursor.
1037 */
1038 GuiData->hCursor = (CursorHandle ? CursorHandle : ghDefaultCursor);
1039
1040 /* Effectively modify the shape of the cursor (use special values for (w|l)Param) */
1041 PostMessageW(GuiData->hWindow, WM_SETCURSOR, -1, -1);
1042
1043 return TRUE;
1044 }
1045
1046 static HMENU NTAPI
1047 GuiMenuControl(IN OUT PFRONTEND This,
1048 UINT CmdIdLow,
1049 UINT CmdIdHigh)
1050 {
1051 PGUI_CONSOLE_DATA GuiData = This->Data;
1052
1053 GuiData->CmdIdLow = CmdIdLow ;
1054 GuiData->CmdIdHigh = CmdIdHigh;
1055
1056 return GetSystemMenu(GuiData->hWindow, FALSE);
1057 }
1058
1059 static BOOL NTAPI
1060 GuiSetMenuClose(IN OUT PFRONTEND This,
1061 BOOL Enable)
1062 {
1063 /*
1064 * NOTE: See http://www.mail-archive.com/harbour@harbour-project.org/msg27509.html
1065 * or http://harbour-devel.1590103.n2.nabble.com/Question-about-hb-gt-win-CtrlHandler-usage-td4670862i20.html
1066 * for more information.
1067 */
1068
1069 PGUI_CONSOLE_DATA GuiData = This->Data;
1070 HMENU hSysMenu = GetSystemMenu(GuiData->hWindow, FALSE);
1071
1072 if (hSysMenu == NULL) return FALSE;
1073
1074 GuiData->IsCloseButtonEnabled = Enable;
1075 EnableMenuItem(hSysMenu, SC_CLOSE, MF_BYCOMMAND | (Enable ? MF_ENABLED : MF_GRAYED));
1076
1077 return TRUE;
1078 }
1079
1080 static FRONTEND_VTBL GuiVtbl =
1081 {
1082 GuiInitFrontEnd,
1083 GuiDeinitFrontEnd,
1084 GuiDrawRegion,
1085 GuiWriteStream,
1086 GuiSetCursorInfo,
1087 GuiSetScreenInfo,
1088 GuiResizeTerminal,
1089 GuiSetActiveScreenBuffer,
1090 GuiReleaseScreenBuffer,
1091 GuiRefreshInternalInfo,
1092 GuiChangeTitle,
1093 GuiChangeIcon,
1094 GuiGetConsoleWindowHandle,
1095 GuiGetLargestConsoleWindowSize,
1096 GuiGetSelectionInfo,
1097 GuiSetPalette,
1098 GuiGetDisplayMode,
1099 GuiSetDisplayMode,
1100 GuiShowMouseCursor,
1101 GuiSetMouseCursor,
1102 GuiMenuControl,
1103 GuiSetMenuClose,
1104 };
1105
1106
1107 static BOOL
1108 LoadShellLinkConsoleInfo(IN OUT PCONSOLE_START_INFO ConsoleStartInfo,
1109 IN OUT PCONSOLE_INFO ConsoleInfo)
1110 {
1111 #define PATH_SEPARATOR L'\\'
1112
1113 BOOL RetVal = FALSE;
1114 HRESULT hRes = S_OK;
1115 LPWSTR LinkName = NULL;
1116 SIZE_T Length = 0;
1117
1118 if ((ConsoleStartInfo->dwStartupFlags & STARTF_TITLEISLINKNAME) == 0)
1119 return FALSE;
1120
1121 ConsoleStartInfo->IconPath[0] = L'\0';
1122 ConsoleStartInfo->IconIndex = 0;
1123
1124 /* 1- Find the last path separator if any */
1125 LinkName = wcsrchr(ConsoleStartInfo->ConsoleTitle, PATH_SEPARATOR);
1126 if (LinkName == NULL)
1127 {
1128 LinkName = ConsoleStartInfo->ConsoleTitle;
1129 }
1130 else
1131 {
1132 /* Skip the path separator */
1133 ++LinkName;
1134 }
1135
1136 /* 2- Check for the link extension. The name ".lnk" is considered invalid. */
1137 Length = wcslen(LinkName);
1138 if ( (Length <= 4) || (wcsicmp(LinkName + (Length - 4), L".lnk") != 0) )
1139 return FALSE;
1140
1141 /* 3- It may be a link. Try to retrieve some properties */
1142 hRes = CoInitialize(NULL);
1143 if (SUCCEEDED(hRes))
1144 {
1145 /* Get a pointer to the IShellLink interface */
1146 IShellLinkW* pshl = NULL;
1147 hRes = CoCreateInstance(&CLSID_ShellLink,
1148 NULL,
1149 CLSCTX_INPROC_SERVER,
1150 &IID_IShellLinkW,
1151 (LPVOID*)&pshl);
1152 if (SUCCEEDED(hRes))
1153 {
1154 /* Get a pointer to the IPersistFile interface */
1155 IPersistFile* ppf = NULL;
1156 hRes = IPersistFile_QueryInterface(pshl, &IID_IPersistFile, (LPVOID*)&ppf);
1157 if (SUCCEEDED(hRes))
1158 {
1159 /* Load the shortcut */
1160 hRes = IPersistFile_Load(ppf, ConsoleStartInfo->ConsoleTitle, STGM_READ);
1161 if (SUCCEEDED(hRes))
1162 {
1163 /*
1164 * Finally we can get the properties !
1165 * Update the old ones if needed.
1166 */
1167 INT ShowCmd = 0;
1168 // WORD HotKey = 0;
1169
1170 /* Reset the name of the console with the name of the shortcut */
1171 Length = min(/*Length*/ Length - 4, // 4 == len(".lnk")
1172 sizeof(ConsoleInfo->ConsoleTitle) / sizeof(ConsoleInfo->ConsoleTitle[0]) - 1);
1173 wcsncpy(ConsoleInfo->ConsoleTitle, LinkName, Length);
1174 ConsoleInfo->ConsoleTitle[Length] = L'\0';
1175
1176 /* Get the window showing command */
1177 hRes = IShellLinkW_GetShowCmd(pshl, &ShowCmd);
1178 if (SUCCEEDED(hRes)) ConsoleStartInfo->wShowWindow = (WORD)ShowCmd;
1179
1180 /* Get the hotkey */
1181 // hRes = pshl->GetHotkey(&ShowCmd);
1182 // if (SUCCEEDED(hRes)) ConsoleStartInfo->HotKey = HotKey;
1183
1184 /* Get the icon location, if any */
1185
1186 hRes = IShellLinkW_GetIconLocation(pshl,
1187 ConsoleStartInfo->IconPath,
1188 sizeof(ConsoleStartInfo->IconPath)/sizeof(ConsoleStartInfo->IconPath[0]) - 1, // == MAX_PATH
1189 &ConsoleStartInfo->IconIndex);
1190 if (!SUCCEEDED(hRes))
1191 {
1192 ConsoleStartInfo->IconPath[0] = L'\0';
1193 ConsoleStartInfo->IconIndex = 0;
1194 }
1195
1196 // FIXME: Since we still don't load console properties from the shortcut,
1197 // return false. When this will be done, we will return true instead.
1198 RetVal = FALSE;
1199 }
1200 IPersistFile_Release(ppf);
1201 }
1202 IShellLinkW_Release(pshl);
1203 }
1204 }
1205 CoUninitialize();
1206
1207 return RetVal;
1208 }
1209
1210 NTSTATUS NTAPI
1211 GuiLoadFrontEnd(IN OUT PFRONTEND FrontEnd,
1212 IN OUT PCONSOLE_INFO ConsoleInfo,
1213 IN OUT PVOID ExtraConsoleInfo,
1214 IN ULONG ProcessId)
1215 {
1216 PCONSOLE_START_INFO ConsoleStartInfo = ExtraConsoleInfo;
1217 PGUI_INIT_INFO GuiInitInfo;
1218
1219 if (FrontEnd == NULL || ConsoleInfo == NULL || ConsoleStartInfo == NULL)
1220 return STATUS_INVALID_PARAMETER;
1221
1222 /* Initialize GUI terminal emulator common functionalities */
1223 if (!GuiInit()) return STATUS_UNSUCCESSFUL;
1224
1225 /*
1226 * Load per-application terminal settings.
1227 *
1228 * Check whether the process creating the console was launched via
1229 * a shell-link. ConsoleInfo->ConsoleTitle may be updated with the
1230 * name of the shortcut, and ConsoleStartInfo->Icon[Path|Index] too.
1231 */
1232 if (ConsoleStartInfo->dwStartupFlags & STARTF_TITLEISLINKNAME)
1233 {
1234 if (!LoadShellLinkConsoleInfo(ConsoleStartInfo, ConsoleInfo))
1235 {
1236 ConsoleStartInfo->dwStartupFlags &= ~STARTF_TITLEISLINKNAME;
1237 }
1238 }
1239
1240 /*
1241 * Initialize a private initialization info structure for later use.
1242 * It must be freed by a call to GuiUnloadFrontEnd or GuiInitFrontEnd.
1243 */
1244 GuiInitInfo = ConsoleAllocHeap(HEAP_ZERO_MEMORY, sizeof(GUI_INIT_INFO));
1245 if (GuiInitInfo == NULL) return STATUS_NO_MEMORY;
1246
1247 // HACK: We suppose that the pointers will be valid in GuiInitFrontEnd...
1248 GuiInitInfo->ConsoleInfo = ConsoleInfo;
1249 GuiInitInfo->ConsoleStartInfo = ConsoleStartInfo;
1250 GuiInitInfo->ProcessId = ProcessId;
1251
1252 /* Finally, initialize the frontend structure */
1253 FrontEnd->Vtbl = &GuiVtbl;
1254 FrontEnd->Data = NULL;
1255 FrontEnd->OldData = GuiInitInfo;
1256
1257 return STATUS_SUCCESS;
1258 }
1259
1260 NTSTATUS NTAPI
1261 GuiUnloadFrontEnd(IN OUT PFRONTEND FrontEnd)
1262 {
1263 if (FrontEnd == NULL) return STATUS_INVALID_PARAMETER;
1264
1265 if (FrontEnd->Data) GuiDeinitFrontEnd(FrontEnd);
1266 if (FrontEnd->OldData) ConsoleFreeHeap(FrontEnd->OldData);
1267
1268 return STATUS_SUCCESS;
1269 }
1270
1271 /* EOF */