* Sync up to trunk HEAD (r62975).
[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
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 DefWindowProcW(msg->hwnd, msg->message, msg->wParam, msg->lParam);
810 return TRUE;
811 }
812
813 return FALSE;
814 }
815
816 static BOOL NTAPI
817 GuiSetMouseCursor(IN OUT PFRONTEND This,
818 HCURSOR CursorHandle);
819
820 static VOID NTAPI
821 GuiRefreshInternalInfo(IN OUT PFRONTEND This)
822 {
823 PGUI_CONSOLE_DATA GuiData = This->Data;
824
825 /* Update the console leader information held by the window */
826 SetConWndConsoleLeaderCID(GuiData);
827
828 /*
829 * HACK:
830 * We reset the cursor here so that, when a console app quits, we reset
831 * the cursor to the default one. It's quite a hack since it doesn't proceed
832 * per - console process... This must be fixed.
833 *
834 * See GuiInitConsole(...) for more information.
835 */
836
837 /* Mouse is shown by default with its default cursor shape */
838 GuiData->MouseCursorRefCount = 0; // Reinitialize the reference counter
839 GuiSetMouseCursor(This, NULL);
840 }
841
842 static VOID NTAPI
843 GuiChangeTitle(IN OUT PFRONTEND This)
844 {
845 PGUI_CONSOLE_DATA GuiData = This->Data;
846 // PostMessageW(GuiData->hWindow, PM_CONSOLE_SET_TITLE, 0, 0);
847 SetWindowText(GuiData->hWindow, GuiData->Console->Title.Buffer);
848 }
849
850 static BOOL NTAPI
851 GuiChangeIcon(IN OUT PFRONTEND This,
852 HICON IconHandle)
853 {
854 PGUI_CONSOLE_DATA GuiData = This->Data;
855 HICON hIcon, hIconSm;
856
857 if (IconHandle == NULL)
858 {
859 hIcon = ghDefaultIcon;
860 hIconSm = ghDefaultIconSm;
861 }
862 else
863 {
864 hIcon = CopyIcon(IconHandle);
865 hIconSm = CopyIcon(IconHandle);
866 }
867
868 if (hIcon == NULL)
869 {
870 return FALSE;
871 }
872
873 if (hIcon != GuiData->hIcon)
874 {
875 if (GuiData->hIcon != NULL && GuiData->hIcon != ghDefaultIcon)
876 {
877 DestroyIcon(GuiData->hIcon);
878 }
879 if (GuiData->hIconSm != NULL && GuiData->hIconSm != ghDefaultIconSm)
880 {
881 DestroyIcon(GuiData->hIconSm);
882 }
883
884 GuiData->hIcon = hIcon;
885 GuiData->hIconSm = hIconSm;
886
887 DPRINT("Set icons in GuiChangeIcon\n");
888 PostMessageW(GuiData->hWindow, WM_SETICON, ICON_BIG , (LPARAM)GuiData->hIcon );
889 PostMessageW(GuiData->hWindow, WM_SETICON, ICON_SMALL, (LPARAM)GuiData->hIconSm);
890 }
891
892 return TRUE;
893 }
894
895 static HWND NTAPI
896 GuiGetConsoleWindowHandle(IN OUT PFRONTEND This)
897 {
898 PGUI_CONSOLE_DATA GuiData = This->Data;
899 return GuiData->hWindow;
900 }
901
902 static VOID NTAPI
903 GuiGetLargestConsoleWindowSize(IN OUT PFRONTEND This,
904 PCOORD pSize)
905 {
906 PGUI_CONSOLE_DATA GuiData = This->Data;
907 PCONSOLE_SCREEN_BUFFER ActiveBuffer;
908 RECT WorkArea;
909 LONG width, height;
910 UINT WidthUnit, HeightUnit;
911
912 if (!pSize) return;
913
914 if (!SystemParametersInfoW(SPI_GETWORKAREA, 0, &WorkArea, 0))
915 {
916 DPRINT1("SystemParametersInfoW failed - What to do ??\n");
917 return;
918 }
919
920 ActiveBuffer = GuiData->ActiveBuffer;
921 if (ActiveBuffer)
922 {
923 GetScreenBufferSizeUnits(ActiveBuffer, GuiData, &WidthUnit, &HeightUnit);
924 }
925 else
926 {
927 /* Default: text mode */
928 WidthUnit = GuiData->CharWidth ;
929 HeightUnit = GuiData->CharHeight;
930 }
931
932 width = WorkArea.right;
933 height = WorkArea.bottom;
934
935 width -= (2 * (GetSystemMetrics(SM_CXFRAME) + GetSystemMetrics(SM_CXEDGE)));
936 height -= (2 * (GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYEDGE)) + GetSystemMetrics(SM_CYCAPTION));
937
938 if (width < 0) width = 0;
939 if (height < 0) height = 0;
940
941 pSize->X = (SHORT)(width / (int)WidthUnit ) /* HACK */ + 2;
942 pSize->Y = (SHORT)(height / (int)HeightUnit) /* HACK */ + 1;
943 }
944
945 static BOOL NTAPI
946 GuiGetSelectionInfo(IN OUT PFRONTEND This,
947 PCONSOLE_SELECTION_INFO pSelectionInfo)
948 {
949 PGUI_CONSOLE_DATA GuiData = This->Data;
950
951 if (pSelectionInfo == NULL) return FALSE;
952
953 ZeroMemory(pSelectionInfo, sizeof(CONSOLE_SELECTION_INFO));
954 if (GuiData->Selection.dwFlags != CONSOLE_NO_SELECTION)
955 RtlCopyMemory(pSelectionInfo, &GuiData->Selection, sizeof(CONSOLE_SELECTION_INFO));
956
957 return TRUE;
958 }
959
960 static BOOL NTAPI
961 GuiSetPalette(IN OUT PFRONTEND This,
962 HPALETTE PaletteHandle,
963 UINT PaletteUsage)
964 {
965 PGUI_CONSOLE_DATA GuiData = This->Data;
966 HPALETTE OldPalette;
967
968 // if (GetType(GuiData->ActiveBuffer) != GRAPHICS_BUFFER) return FALSE;
969 if (PaletteHandle == NULL) return FALSE;
970
971 /* Set the new palette for the framebuffer */
972 OldPalette = SelectPalette(GuiData->hMemDC, PaletteHandle, FALSE);
973 if (OldPalette == NULL) return FALSE;
974
975 /* Specify the use of the system palette for the framebuffer */
976 SetSystemPaletteUse(GuiData->hMemDC, PaletteUsage);
977
978 /* Realize the (logical) palette */
979 RealizePalette(GuiData->hMemDC);
980
981 /* Save the original system palette handle */
982 if (GuiData->hSysPalette == NULL) GuiData->hSysPalette = OldPalette;
983
984 return TRUE;
985 }
986
987 static ULONG NTAPI
988 GuiGetDisplayMode(IN OUT PFRONTEND This)
989 {
990 PGUI_CONSOLE_DATA GuiData = This->Data;
991 ULONG DisplayMode = 0;
992
993 if (GuiData->GuiInfo.FullScreen)
994 DisplayMode |= CONSOLE_FULLSCREEN_HARDWARE; // CONSOLE_FULLSCREEN
995 else
996 DisplayMode |= CONSOLE_WINDOWED;
997
998 return DisplayMode;
999 }
1000
1001 static BOOL NTAPI
1002 GuiSetDisplayMode(IN OUT PFRONTEND This,
1003 ULONG NewMode)
1004 {
1005 PGUI_CONSOLE_DATA GuiData = This->Data;
1006 BOOL FullScreen;
1007
1008 if (NewMode & ~(CONSOLE_FULLSCREEN_MODE | CONSOLE_WINDOWED_MODE))
1009 return FALSE;
1010
1011 FullScreen = ((NewMode & CONSOLE_FULLSCREEN_MODE) != 0);
1012
1013 if (FullScreen != GuiData->GuiInfo.FullScreen)
1014 {
1015 SwitchFullScreen(GuiData, FullScreen);
1016 }
1017
1018 return TRUE;
1019 }
1020
1021 static INT NTAPI
1022 GuiShowMouseCursor(IN OUT PFRONTEND This,
1023 BOOL Show)
1024 {
1025 PGUI_CONSOLE_DATA GuiData = This->Data;
1026
1027 /* Set the reference count */
1028 if (Show) ++GuiData->MouseCursorRefCount;
1029 else --GuiData->MouseCursorRefCount;
1030
1031 /* Effectively show (or hide) the cursor (use special values for (w|l)Param) */
1032 PostMessageW(GuiData->hWindow, WM_SETCURSOR, -1, -1);
1033
1034 return GuiData->MouseCursorRefCount;
1035 }
1036
1037 static BOOL NTAPI
1038 GuiSetMouseCursor(IN OUT PFRONTEND This,
1039 HCURSOR CursorHandle)
1040 {
1041 PGUI_CONSOLE_DATA GuiData = This->Data;
1042
1043 /*
1044 * Set the cursor's handle. If the given handle is NULL,
1045 * then restore the default cursor.
1046 */
1047 GuiData->hCursor = (CursorHandle ? CursorHandle : ghDefaultCursor);
1048
1049 /* Effectively modify the shape of the cursor (use special values for (w|l)Param) */
1050 PostMessageW(GuiData->hWindow, WM_SETCURSOR, -1, -1);
1051
1052 return TRUE;
1053 }
1054
1055 static HMENU NTAPI
1056 GuiMenuControl(IN OUT PFRONTEND This,
1057 UINT CmdIdLow,
1058 UINT CmdIdHigh)
1059 {
1060 PGUI_CONSOLE_DATA GuiData = This->Data;
1061
1062 GuiData->CmdIdLow = CmdIdLow ;
1063 GuiData->CmdIdHigh = CmdIdHigh;
1064
1065 return GetSystemMenu(GuiData->hWindow, FALSE);
1066 }
1067
1068 static BOOL NTAPI
1069 GuiSetMenuClose(IN OUT PFRONTEND This,
1070 BOOL Enable)
1071 {
1072 /*
1073 * NOTE: See http://www.mail-archive.com/harbour@harbour-project.org/msg27509.html
1074 * or http://harbour-devel.1590103.n2.nabble.com/Question-about-hb-gt-win-CtrlHandler-usage-td4670862i20.html
1075 * for more information.
1076 */
1077
1078 PGUI_CONSOLE_DATA GuiData = This->Data;
1079 HMENU hSysMenu = GetSystemMenu(GuiData->hWindow, FALSE);
1080
1081 if (hSysMenu == NULL) return FALSE;
1082
1083 GuiData->IsCloseButtonEnabled = Enable;
1084 EnableMenuItem(hSysMenu, SC_CLOSE, MF_BYCOMMAND | (Enable ? MF_ENABLED : MF_GRAYED));
1085
1086 return TRUE;
1087 }
1088
1089 static FRONTEND_VTBL GuiVtbl =
1090 {
1091 GuiInitFrontEnd,
1092 GuiDeinitFrontEnd,
1093 GuiDrawRegion,
1094 GuiWriteStream,
1095 GuiSetCursorInfo,
1096 GuiSetScreenInfo,
1097 GuiResizeTerminal,
1098 GuiSetActiveScreenBuffer,
1099 GuiReleaseScreenBuffer,
1100 GuiProcessKeyCallback,
1101 GuiRefreshInternalInfo,
1102 GuiChangeTitle,
1103 GuiChangeIcon,
1104 GuiGetConsoleWindowHandle,
1105 GuiGetLargestConsoleWindowSize,
1106 GuiGetSelectionInfo,
1107 GuiSetPalette,
1108 GuiGetDisplayMode,
1109 GuiSetDisplayMode,
1110 GuiShowMouseCursor,
1111 GuiSetMouseCursor,
1112 GuiMenuControl,
1113 GuiSetMenuClose,
1114 };
1115
1116
1117 static BOOL
1118 LoadShellLinkConsoleInfo(IN OUT PCONSOLE_START_INFO ConsoleStartInfo,
1119 IN OUT PCONSOLE_INFO ConsoleInfo)
1120 {
1121 #define PATH_SEPARATOR L'\\'
1122
1123 BOOL RetVal = FALSE;
1124 HRESULT hRes = S_OK;
1125 LPWSTR LinkName = NULL;
1126 SIZE_T Length = 0;
1127
1128 if ((ConsoleStartInfo->dwStartupFlags & STARTF_TITLEISLINKNAME) == 0)
1129 return FALSE;
1130
1131 ConsoleStartInfo->IconPath[0] = L'\0';
1132 ConsoleStartInfo->IconIndex = 0;
1133
1134 /* 1- Find the last path separator if any */
1135 LinkName = wcsrchr(ConsoleStartInfo->ConsoleTitle, PATH_SEPARATOR);
1136 if (LinkName == NULL)
1137 {
1138 LinkName = ConsoleStartInfo->ConsoleTitle;
1139 }
1140 else
1141 {
1142 /* Skip the path separator */
1143 ++LinkName;
1144 }
1145
1146 /* 2- Check for the link extension. The name ".lnk" is considered invalid. */
1147 Length = wcslen(LinkName);
1148 if ( (Length <= 4) || (wcsicmp(LinkName + (Length - 4), L".lnk") != 0) )
1149 return FALSE;
1150
1151 /* 3- It may be a link. Try to retrieve some properties */
1152 hRes = CoInitialize(NULL);
1153 if (SUCCEEDED(hRes))
1154 {
1155 /* Get a pointer to the IShellLink interface */
1156 IShellLinkW* pshl = NULL;
1157 hRes = CoCreateInstance(&CLSID_ShellLink,
1158 NULL,
1159 CLSCTX_INPROC_SERVER,
1160 &IID_IShellLinkW,
1161 (LPVOID*)&pshl);
1162 if (SUCCEEDED(hRes))
1163 {
1164 /* Get a pointer to the IPersistFile interface */
1165 IPersistFile* ppf = NULL;
1166 hRes = IPersistFile_QueryInterface(pshl, &IID_IPersistFile, (LPVOID*)&ppf);
1167 if (SUCCEEDED(hRes))
1168 {
1169 /* Load the shortcut */
1170 hRes = IPersistFile_Load(ppf, ConsoleStartInfo->ConsoleTitle, STGM_READ);
1171 if (SUCCEEDED(hRes))
1172 {
1173 /*
1174 * Finally we can get the properties !
1175 * Update the old ones if needed.
1176 */
1177 INT ShowCmd = 0;
1178 // WORD HotKey = 0;
1179
1180 /* Reset the name of the console with the name of the shortcut */
1181 Length = min(/*Length*/ Length - 4, // 4 == len(".lnk")
1182 sizeof(ConsoleInfo->ConsoleTitle) / sizeof(ConsoleInfo->ConsoleTitle[0]) - 1);
1183 wcsncpy(ConsoleInfo->ConsoleTitle, LinkName, Length);
1184 ConsoleInfo->ConsoleTitle[Length] = L'\0';
1185
1186 /* Get the window showing command */
1187 hRes = IShellLinkW_GetShowCmd(pshl, &ShowCmd);
1188 if (SUCCEEDED(hRes)) ConsoleStartInfo->wShowWindow = (WORD)ShowCmd;
1189
1190 /* Get the hotkey */
1191 // hRes = pshl->GetHotkey(&ShowCmd);
1192 // if (SUCCEEDED(hRes)) ConsoleStartInfo->HotKey = HotKey;
1193
1194 /* Get the icon location, if any */
1195
1196 hRes = IShellLinkW_GetIconLocation(pshl,
1197 ConsoleStartInfo->IconPath,
1198 sizeof(ConsoleStartInfo->IconPath)/sizeof(ConsoleStartInfo->IconPath[0]) - 1, // == MAX_PATH
1199 &ConsoleStartInfo->IconIndex);
1200 if (!SUCCEEDED(hRes))
1201 {
1202 ConsoleStartInfo->IconPath[0] = L'\0';
1203 ConsoleStartInfo->IconIndex = 0;
1204 }
1205
1206 // FIXME: Since we still don't load console properties from the shortcut,
1207 // return false. When this will be done, we will return true instead.
1208 RetVal = FALSE;
1209 }
1210 IPersistFile_Release(ppf);
1211 }
1212 IShellLinkW_Release(pshl);
1213 }
1214 }
1215 CoUninitialize();
1216
1217 return RetVal;
1218 }
1219
1220 NTSTATUS NTAPI
1221 GuiLoadFrontEnd(IN OUT PFRONTEND FrontEnd,
1222 IN OUT PCONSOLE_INFO ConsoleInfo,
1223 IN OUT PVOID ExtraConsoleInfo,
1224 IN ULONG ProcessId)
1225 {
1226 PCONSOLE_START_INFO ConsoleStartInfo = ExtraConsoleInfo;
1227 PGUI_INIT_INFO GuiInitInfo;
1228
1229 if (FrontEnd == NULL || ConsoleInfo == NULL || ConsoleStartInfo == NULL)
1230 return STATUS_INVALID_PARAMETER;
1231
1232 /* Initialize GUI terminal emulator common functionalities */
1233 if (!GuiInit()) return STATUS_UNSUCCESSFUL;
1234
1235 /*
1236 * Load per-application terminal settings.
1237 *
1238 * Check whether the process creating the console was launched via
1239 * a shell-link. ConsoleInfo->ConsoleTitle may be updated with the
1240 * name of the shortcut, and ConsoleStartInfo->Icon[Path|Index] too.
1241 */
1242 if (ConsoleStartInfo->dwStartupFlags & STARTF_TITLEISLINKNAME)
1243 {
1244 if (!LoadShellLinkConsoleInfo(ConsoleStartInfo, ConsoleInfo))
1245 {
1246 ConsoleStartInfo->dwStartupFlags &= ~STARTF_TITLEISLINKNAME;
1247 }
1248 }
1249
1250 /*
1251 * Initialize a private initialization info structure for later use.
1252 * It must be freed by a call to GuiUnloadFrontEnd or GuiInitFrontEnd.
1253 */
1254 GuiInitInfo = ConsoleAllocHeap(HEAP_ZERO_MEMORY, sizeof(GUI_INIT_INFO));
1255 if (GuiInitInfo == NULL) return STATUS_NO_MEMORY;
1256
1257 // HACK: We suppose that the pointers will be valid in GuiInitFrontEnd...
1258 GuiInitInfo->ConsoleInfo = ConsoleInfo;
1259 GuiInitInfo->ConsoleStartInfo = ConsoleStartInfo;
1260 GuiInitInfo->ProcessId = ProcessId;
1261
1262 /* Finally, initialize the frontend structure */
1263 FrontEnd->Vtbl = &GuiVtbl;
1264 FrontEnd->Data = NULL;
1265 FrontEnd->OldData = GuiInitInfo;
1266
1267 return STATUS_SUCCESS;
1268 }
1269
1270 NTSTATUS NTAPI
1271 GuiUnloadFrontEnd(IN OUT PFRONTEND FrontEnd)
1272 {
1273 if (FrontEnd == NULL) return STATUS_INVALID_PARAMETER;
1274
1275 if (FrontEnd->Data) GuiDeinitFrontEnd(FrontEnd);
1276 if (FrontEnd->OldData) ConsoleFreeHeap(FrontEnd->OldData);
1277
1278 return STATUS_SUCCESS;
1279 }
1280
1281 /* EOF */