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