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