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