* Sync up to trunk head (r64894).
[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 } 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 if (This == NULL || Console == NULL || This->OldData == NULL)
345 return STATUS_INVALID_PARAMETER;
346
347 ASSERT(This->Console == Console);
348
349 GuiInitInfo = This->OldData;
350
351 if (GuiInitInfo->ConsoleInfo == NULL || GuiInitInfo->ConsoleStartInfo == NULL)
352 return STATUS_INVALID_PARAMETER;
353
354 ConsoleInfo = GuiInitInfo->ConsoleInfo;
355 ConsoleStartInfo = GuiInitInfo->ConsoleStartInfo;
356
357 /* Terminal data allocation */
358 GuiData = ConsoleAllocHeap(HEAP_ZERO_MEMORY, sizeof(GUI_CONSOLE_DATA));
359 if (!GuiData)
360 {
361 DPRINT1("CONSRV: Failed to create GUI_CONSOLE_DATA\n");
362 return STATUS_UNSUCCESSFUL;
363 }
364 ///// /* HACK */ Console->FrontEndIFace.Data = (PVOID)GuiData; /* HACK */
365 GuiData->Console = Console;
366 GuiData->ActiveBuffer = Console->ActiveBuffer;
367 GuiData->hWindow = NULL;
368
369 /* The console can be resized */
370 Console->FixedSize = FALSE;
371
372 InitializeCriticalSection(&GuiData->Lock);
373
374
375 /*
376 * Load terminal settings
377 */
378
379 /* 1. Load the default settings */
380 GuiConsoleGetDefaultSettings(&TermInfo, GuiInitInfo->ProcessId);
381
382 /* 3. Load the remaining console settings via the registry */
383 if ((ConsoleStartInfo->dwStartupFlags & STARTF_TITLEISLINKNAME) == 0)
384 {
385 /* Load the terminal infos from the registry */
386 GuiConsoleReadUserSettings(&TermInfo,
387 ConsoleInfo->ConsoleTitle,
388 GuiInitInfo->ProcessId);
389
390 /*
391 * Now, update them with the properties the user might gave to us
392 * via the STARTUPINFO structure before calling CreateProcess
393 * (and which was transmitted via the ConsoleStartInfo structure).
394 * We therefore overwrite the values read in the registry.
395 */
396 if (ConsoleStartInfo->dwStartupFlags & STARTF_USESHOWWINDOW)
397 {
398 TermInfo.ShowWindow = ConsoleStartInfo->wShowWindow;
399 }
400 if (ConsoleStartInfo->dwStartupFlags & STARTF_USEPOSITION)
401 {
402 TermInfo.AutoPosition = FALSE;
403 TermInfo.WindowOrigin.x = ConsoleStartInfo->dwWindowOrigin.X;
404 TermInfo.WindowOrigin.y = ConsoleStartInfo->dwWindowOrigin.Y;
405 }
406 if (ConsoleStartInfo->dwStartupFlags & STARTF_RUNFULLSCREEN)
407 {
408 TermInfo.FullScreen = TRUE;
409 }
410 }
411
412
413 /*
414 * Set up GUI data
415 */
416
417 // Font data
418 wcsncpy(GuiData->GuiInfo.FaceName, TermInfo.FaceName, LF_FACESIZE);
419 GuiData->GuiInfo.FaceName[LF_FACESIZE - 1] = UNICODE_NULL;
420 GuiData->GuiInfo.FontFamily = TermInfo.FontFamily;
421 GuiData->GuiInfo.FontSize = TermInfo.FontSize;
422 GuiData->GuiInfo.FontWeight = TermInfo.FontWeight;
423
424 // Display
425 GuiData->GuiInfo.FullScreen = TermInfo.FullScreen;
426 GuiData->GuiInfo.ShowWindow = TermInfo.ShowWindow;
427 GuiData->GuiInfo.AutoPosition = TermInfo.AutoPosition;
428 GuiData->GuiInfo.WindowOrigin = TermInfo.WindowOrigin;
429
430 /* Initialize the icon handles */
431 if (ConsoleStartInfo->hIcon != NULL)
432 GuiData->hIcon = ConsoleStartInfo->hIcon;
433 else
434 GuiData->hIcon = ghDefaultIcon;
435
436 if (ConsoleStartInfo->hIconSm != NULL)
437 GuiData->hIconSm = ConsoleStartInfo->hIconSm;
438 else
439 GuiData->hIconSm = ghDefaultIconSm;
440
441 ASSERT(GuiData->hIcon && GuiData->hIconSm);
442
443 /* Mouse is shown by default with its default cursor shape */
444 GuiData->hCursor = ghDefaultCursor;
445 GuiData->MouseCursorRefCount = 0;
446
447 /* A priori don't ignore mouse signals */
448 GuiData->IgnoreNextMouseSignal = FALSE;
449
450 /* Close button and the corresponding system menu item are enabled by default */
451 GuiData->IsCloseButtonEnabled = TRUE;
452
453 /* There is no user-reserved menu id range by default */
454 GuiData->CmdIdLow = GuiData->CmdIdHigh = 0;
455
456 /* Initialize the selection */
457 RtlZeroMemory(&GuiData->Selection, sizeof(GuiData->Selection));
458 GuiData->Selection.dwFlags = CONSOLE_NO_SELECTION;
459 RtlZeroMemory(&GuiData->dwSelectionCursor, sizeof(GuiData->dwSelectionCursor));
460 GuiData->LineSelection = FALSE; // Default to block selection
461 // TODO: Retrieve the selection mode via the registry.
462
463 /* Finally, finish to initialize the frontend structure */
464 This->Data = GuiData;
465 if (This->OldData) ConsoleFreeHeap(This->OldData);
466 This->OldData = NULL;
467
468 /*
469 * We need to wait until the GUI has been fully initialized
470 * to retrieve custom settings i.e. WindowSize etc...
471 * Ideally we could use SendNotifyMessage for this but its not
472 * yet implemented.
473 */
474 GuiData->hGuiInitEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
475 GuiData->hGuiTermEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
476
477 DPRINT("GUI - Checkpoint\n");
478
479 /* Create the terminal window */
480 PostThreadMessageW(dwInputThreadId, PM_CREATE_CONSOLE, 0, (LPARAM)GuiData);
481
482 /* Wait until initialization has finished */
483 WaitForSingleObject(GuiData->hGuiInitEvent, INFINITE);
484 DPRINT("OK we created the console window\n");
485 CloseHandle(GuiData->hGuiInitEvent);
486 GuiData->hGuiInitEvent = NULL;
487
488 /* Check whether we really succeeded in initializing the terminal window */
489 if (GuiData->hWindow == NULL)
490 {
491 DPRINT("GuiInitConsole - We failed at creating a new terminal window\n");
492 GuiDeinitFrontEnd(This);
493 return STATUS_UNSUCCESSFUL;
494 }
495
496 return STATUS_SUCCESS;
497 }
498
499 static VOID NTAPI
500 GuiDeinitFrontEnd(IN OUT PFRONTEND This)
501 {
502 PGUI_CONSOLE_DATA GuiData = This->Data;
503
504 DPRINT("Send PM_DESTROY_CONSOLE message and wait on hGuiTermEvent...\n");
505 PostThreadMessageW(dwInputThreadId, PM_DESTROY_CONSOLE, 0, (LPARAM)GuiData);
506 WaitForSingleObject(GuiData->hGuiTermEvent, INFINITE);
507 DPRINT("hGuiTermEvent set\n");
508 CloseHandle(GuiData->hGuiTermEvent);
509 GuiData->hGuiTermEvent = NULL;
510
511 DPRINT("Destroying icons !! - GuiData->hIcon = 0x%p ; ghDefaultIcon = 0x%p ; GuiData->hIconSm = 0x%p ; ghDefaultIconSm = 0x%p\n",
512 GuiData->hIcon, ghDefaultIcon, GuiData->hIconSm, ghDefaultIconSm);
513 if (GuiData->hIcon != NULL && GuiData->hIcon != ghDefaultIcon)
514 {
515 DPRINT("Destroy hIcon\n");
516 DestroyIcon(GuiData->hIcon);
517 }
518 if (GuiData->hIconSm != NULL && GuiData->hIconSm != ghDefaultIconSm)
519 {
520 DPRINT("Destroy hIconSm\n");
521 DestroyIcon(GuiData->hIconSm);
522 }
523
524 This->Data = NULL;
525 DeleteCriticalSection(&GuiData->Lock);
526 ConsoleFreeHeap(GuiData);
527
528 DPRINT("Quit GuiDeinitFrontEnd\n");
529 }
530
531 static VOID NTAPI
532 GuiDrawRegion(IN OUT PFRONTEND This,
533 SMALL_RECT* Region)
534 {
535 PGUI_CONSOLE_DATA GuiData = This->Data;
536 DrawRegion(GuiData, Region);
537 }
538
539 static VOID NTAPI
540 GuiWriteStream(IN OUT PFRONTEND This,
541 SMALL_RECT* Region,
542 SHORT CursorStartX,
543 SHORT CursorStartY,
544 UINT ScrolledLines,
545 PWCHAR Buffer,
546 UINT Length)
547 {
548 PGUI_CONSOLE_DATA GuiData = This->Data;
549 PCONSOLE_SCREEN_BUFFER Buff;
550 SHORT CursorEndX, CursorEndY;
551 RECT ScrollRect;
552
553 if (NULL == GuiData || NULL == GuiData->hWindow) return;
554
555 Buff = GuiData->ActiveBuffer;
556 if (GetType(Buff) != TEXTMODE_BUFFER) return;
557
558 if (0 != ScrolledLines)
559 {
560 ScrollRect.left = 0;
561 ScrollRect.top = 0;
562 ScrollRect.right = Buff->ViewSize.X * GuiData->CharWidth;
563 ScrollRect.bottom = Region->Top * GuiData->CharHeight;
564
565 ScrollWindowEx(GuiData->hWindow,
566 0,
567 -(int)(ScrolledLines * GuiData->CharHeight),
568 &ScrollRect,
569 NULL,
570 NULL,
571 NULL,
572 SW_INVALIDATE);
573 }
574
575 DrawRegion(GuiData, Region);
576
577 if (CursorStartX < Region->Left || Region->Right < CursorStartX
578 || CursorStartY < Region->Top || Region->Bottom < CursorStartY)
579 {
580 InvalidateCell(GuiData, CursorStartX, CursorStartY);
581 }
582
583 CursorEndX = Buff->CursorPosition.X;
584 CursorEndY = Buff->CursorPosition.Y;
585 if ((CursorEndX < Region->Left || Region->Right < CursorEndX
586 || CursorEndY < Region->Top || Region->Bottom < CursorEndY)
587 && (CursorEndX != CursorStartX || CursorEndY != CursorStartY))
588 {
589 InvalidateCell(GuiData, CursorEndX, CursorEndY);
590 }
591
592 // HACK!!
593 // Set up the update timer (very short interval) - this is a "hack" for getting the OS to
594 // repaint the window without having it just freeze up and stay on the screen permanently.
595 Buff->CursorBlinkOn = TRUE;
596 SetTimer(GuiData->hWindow, CONGUI_UPDATE_TIMER, CONGUI_UPDATE_TIME, NULL);
597 }
598
599 /* static */ VOID NTAPI
600 GuiRingBell(IN OUT PFRONTEND This)
601 {
602 PGUI_CONSOLE_DATA GuiData = This->Data;
603
604 /* Emit an error beep sound */
605 SendNotifyMessage(GuiData->hWindow, PM_CONSOLE_BEEP, 0, 0);
606 }
607
608 static BOOL NTAPI
609 GuiSetCursorInfo(IN OUT PFRONTEND This,
610 PCONSOLE_SCREEN_BUFFER Buff)
611 {
612 PGUI_CONSOLE_DATA GuiData = This->Data;
613
614 if (GuiData->ActiveBuffer == Buff)
615 {
616 InvalidateCell(GuiData, Buff->CursorPosition.X, Buff->CursorPosition.Y);
617 }
618
619 return TRUE;
620 }
621
622 static BOOL NTAPI
623 GuiSetScreenInfo(IN OUT PFRONTEND This,
624 PCONSOLE_SCREEN_BUFFER Buff,
625 SHORT OldCursorX,
626 SHORT OldCursorY)
627 {
628 PGUI_CONSOLE_DATA GuiData = This->Data;
629
630 if (GuiData->ActiveBuffer == Buff)
631 {
632 /* Redraw char at old position (remove cursor) */
633 InvalidateCell(GuiData, OldCursorX, OldCursorY);
634 /* Redraw char at new position (show cursor) */
635 InvalidateCell(GuiData, Buff->CursorPosition.X, Buff->CursorPosition.Y);
636 }
637
638 return TRUE;
639 }
640
641 static VOID NTAPI
642 GuiResizeTerminal(IN OUT PFRONTEND This)
643 {
644 PGUI_CONSOLE_DATA GuiData = This->Data;
645
646 /* Resize the window to the user's values */
647 PostMessageW(GuiData->hWindow, PM_RESIZE_TERMINAL, 0, 0);
648 }
649
650 static VOID NTAPI
651 GuiSetActiveScreenBuffer(IN OUT PFRONTEND This)
652 {
653 PGUI_CONSOLE_DATA GuiData = This->Data;
654 PCONSOLE_SCREEN_BUFFER ActiveBuffer;
655 HPALETTE hPalette;
656
657 EnterCriticalSection(&GuiData->Lock);
658 GuiData->WindowSizeLock = TRUE;
659
660 InterlockedExchangePointer(&GuiData->ActiveBuffer,
661 ConDrvGetActiveScreenBuffer(GuiData->Console));
662
663 GuiData->WindowSizeLock = FALSE;
664 LeaveCriticalSection(&GuiData->Lock);
665
666 ActiveBuffer = GuiData->ActiveBuffer;
667
668 /* Change the current palette */
669 if (ActiveBuffer->PaletteHandle == NULL)
670 {
671 hPalette = GuiData->hSysPalette;
672 }
673 else
674 {
675 hPalette = ActiveBuffer->PaletteHandle;
676 }
677
678 DPRINT1("GuiSetActiveScreenBuffer using palette 0x%p\n", hPalette);
679
680 /* Set the new palette for the framebuffer */
681 SelectPalette(GuiData->hMemDC, hPalette, FALSE);
682
683 /* Specify the use of the system palette for the framebuffer */
684 SetSystemPaletteUse(GuiData->hMemDC, ActiveBuffer->PaletteUsage);
685
686 /* Realize the (logical) palette */
687 RealizePalette(GuiData->hMemDC);
688
689 GuiResizeTerminal(This);
690 // ConioDrawConsole(Console);
691 }
692
693 static VOID NTAPI
694 GuiReleaseScreenBuffer(IN OUT PFRONTEND This,
695 IN PCONSOLE_SCREEN_BUFFER ScreenBuffer)
696 {
697 PGUI_CONSOLE_DATA GuiData = This->Data;
698
699 /*
700 * If we were notified to release a screen buffer that is not actually
701 * ours, then just ignore the notification...
702 */
703 if (ScreenBuffer != GuiData->ActiveBuffer) return;
704
705 /*
706 * ... else, we must release our active buffer. Two cases are present:
707 * - If ScreenBuffer (== GuiData->ActiveBuffer) IS NOT the console
708 * active screen buffer, then we can safely switch to it.
709 * - If ScreenBuffer IS the console active screen buffer, we must release
710 * it ONLY.
711 */
712
713 /* Release the old active palette and set the default one */
714 if (GetCurrentObject(GuiData->hMemDC, OBJ_PAL) == ScreenBuffer->PaletteHandle)
715 {
716 /* Set the new palette */
717 SelectPalette(GuiData->hMemDC, GuiData->hSysPalette, FALSE);
718 }
719
720 /* Set the adequate active screen buffer */
721 if (ScreenBuffer != GuiData->Console->ActiveBuffer)
722 {
723 GuiSetActiveScreenBuffer(This);
724 }
725 else
726 {
727 EnterCriticalSection(&GuiData->Lock);
728 GuiData->WindowSizeLock = TRUE;
729
730 InterlockedExchangePointer(&GuiData->ActiveBuffer, NULL);
731
732 GuiData->WindowSizeLock = FALSE;
733 LeaveCriticalSection(&GuiData->Lock);
734 }
735 }
736
737 static BOOL NTAPI
738 GuiSetMouseCursor(IN OUT PFRONTEND This,
739 HCURSOR CursorHandle);
740
741 static VOID NTAPI
742 GuiRefreshInternalInfo(IN OUT PFRONTEND This)
743 {
744 PGUI_CONSOLE_DATA GuiData = This->Data;
745
746 /* Update the console leader information held by the window */
747 SetConWndConsoleLeaderCID(GuiData);
748
749 /*
750 * HACK:
751 * We reset the cursor here so that, when a console app quits, we reset
752 * the cursor to the default one. It's quite a hack since it doesn't proceed
753 * per - console process... This must be fixed.
754 *
755 * See GuiInitConsole(...) for more information.
756 */
757
758 /* Mouse is shown by default with its default cursor shape */
759 GuiData->MouseCursorRefCount = 0; // Reinitialize the reference counter
760 GuiSetMouseCursor(This, NULL);
761 }
762
763 static VOID NTAPI
764 GuiChangeTitle(IN OUT PFRONTEND This)
765 {
766 PGUI_CONSOLE_DATA GuiData = This->Data;
767 // PostMessageW(GuiData->hWindow, PM_CONSOLE_SET_TITLE, 0, 0);
768 SetWindowText(GuiData->hWindow, GuiData->Console->Title.Buffer);
769 }
770
771 static BOOL NTAPI
772 GuiChangeIcon(IN OUT PFRONTEND This,
773 HICON IconHandle)
774 {
775 PGUI_CONSOLE_DATA GuiData = This->Data;
776 HICON hIcon, hIconSm;
777
778 if (IconHandle == NULL)
779 {
780 hIcon = ghDefaultIcon;
781 hIconSm = ghDefaultIconSm;
782 }
783 else
784 {
785 hIcon = CopyIcon(IconHandle);
786 hIconSm = CopyIcon(IconHandle);
787 }
788
789 if (hIcon == NULL)
790 {
791 return FALSE;
792 }
793
794 if (hIcon != GuiData->hIcon)
795 {
796 if (GuiData->hIcon != NULL && GuiData->hIcon != ghDefaultIcon)
797 {
798 DestroyIcon(GuiData->hIcon);
799 }
800 if (GuiData->hIconSm != NULL && GuiData->hIconSm != ghDefaultIconSm)
801 {
802 DestroyIcon(GuiData->hIconSm);
803 }
804
805 GuiData->hIcon = hIcon;
806 GuiData->hIconSm = hIconSm;
807
808 DPRINT("Set icons in GuiChangeIcon\n");
809 PostMessageW(GuiData->hWindow, WM_SETICON, ICON_BIG , (LPARAM)GuiData->hIcon );
810 PostMessageW(GuiData->hWindow, WM_SETICON, ICON_SMALL, (LPARAM)GuiData->hIconSm);
811 }
812
813 return TRUE;
814 }
815
816 static HWND NTAPI
817 GuiGetConsoleWindowHandle(IN OUT PFRONTEND This)
818 {
819 PGUI_CONSOLE_DATA GuiData = This->Data;
820 return GuiData->hWindow;
821 }
822
823 static VOID NTAPI
824 GuiGetLargestConsoleWindowSize(IN OUT PFRONTEND This,
825 PCOORD pSize)
826 {
827 PGUI_CONSOLE_DATA GuiData = This->Data;
828 PCONSOLE_SCREEN_BUFFER ActiveBuffer;
829 RECT WorkArea;
830 LONG width, height;
831 UINT WidthUnit, HeightUnit;
832
833 if (!pSize) return;
834
835 if (!SystemParametersInfoW(SPI_GETWORKAREA, 0, &WorkArea, 0))
836 {
837 DPRINT1("SystemParametersInfoW failed - What to do ??\n");
838 return;
839 }
840
841 ActiveBuffer = GuiData->ActiveBuffer;
842 if (ActiveBuffer)
843 {
844 GetScreenBufferSizeUnits(ActiveBuffer, GuiData, &WidthUnit, &HeightUnit);
845 }
846 else
847 {
848 /* Default: text mode */
849 WidthUnit = GuiData->CharWidth ;
850 HeightUnit = GuiData->CharHeight;
851 }
852
853 width = WorkArea.right;
854 height = WorkArea.bottom;
855
856 width -= (2 * (GetSystemMetrics(SM_CXFRAME) + GetSystemMetrics(SM_CXEDGE)));
857 height -= (2 * (GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYEDGE)) + GetSystemMetrics(SM_CYCAPTION));
858
859 if (width < 0) width = 0;
860 if (height < 0) height = 0;
861
862 pSize->X = (SHORT)(width / (int)WidthUnit ) /* HACK */ + 2;
863 pSize->Y = (SHORT)(height / (int)HeightUnit) /* HACK */ + 1;
864 }
865
866 static BOOL NTAPI
867 GuiGetSelectionInfo(IN OUT PFRONTEND This,
868 PCONSOLE_SELECTION_INFO pSelectionInfo)
869 {
870 PGUI_CONSOLE_DATA GuiData = This->Data;
871
872 if (pSelectionInfo == NULL) return FALSE;
873
874 ZeroMemory(pSelectionInfo, sizeof(CONSOLE_SELECTION_INFO));
875 if (GuiData->Selection.dwFlags != CONSOLE_NO_SELECTION)
876 RtlCopyMemory(pSelectionInfo, &GuiData->Selection, sizeof(CONSOLE_SELECTION_INFO));
877
878 return TRUE;
879 }
880
881 static BOOL NTAPI
882 GuiSetPalette(IN OUT PFRONTEND This,
883 HPALETTE PaletteHandle,
884 UINT PaletteUsage)
885 {
886 PGUI_CONSOLE_DATA GuiData = This->Data;
887 HPALETTE OldPalette;
888
889 // if (GetType(GuiData->ActiveBuffer) != GRAPHICS_BUFFER) return FALSE;
890 if (PaletteHandle == NULL) return FALSE;
891
892 /* Set the new palette for the framebuffer */
893 OldPalette = SelectPalette(GuiData->hMemDC, PaletteHandle, FALSE);
894 if (OldPalette == NULL) return FALSE;
895
896 /* Specify the use of the system palette for the framebuffer */
897 SetSystemPaletteUse(GuiData->hMemDC, PaletteUsage);
898
899 /* Realize the (logical) palette */
900 RealizePalette(GuiData->hMemDC);
901
902 /* Save the original system palette handle */
903 if (GuiData->hSysPalette == NULL) GuiData->hSysPalette = OldPalette;
904
905 return TRUE;
906 }
907
908 static ULONG NTAPI
909 GuiGetDisplayMode(IN OUT PFRONTEND This)
910 {
911 PGUI_CONSOLE_DATA GuiData = This->Data;
912 ULONG DisplayMode = 0;
913
914 if (GuiData->GuiInfo.FullScreen)
915 DisplayMode |= CONSOLE_FULLSCREEN_HARDWARE; // CONSOLE_FULLSCREEN
916 else
917 DisplayMode |= CONSOLE_WINDOWED;
918
919 return DisplayMode;
920 }
921
922 static BOOL NTAPI
923 GuiSetDisplayMode(IN OUT PFRONTEND This,
924 ULONG NewMode)
925 {
926 PGUI_CONSOLE_DATA GuiData = This->Data;
927 BOOL FullScreen;
928
929 if (NewMode & ~(CONSOLE_FULLSCREEN_MODE | CONSOLE_WINDOWED_MODE))
930 return FALSE;
931
932 FullScreen = ((NewMode & CONSOLE_FULLSCREEN_MODE) != 0);
933
934 if (FullScreen != GuiData->GuiInfo.FullScreen)
935 {
936 SwitchFullScreen(GuiData, FullScreen);
937 }
938
939 return TRUE;
940 }
941
942 static INT NTAPI
943 GuiShowMouseCursor(IN OUT PFRONTEND This,
944 BOOL Show)
945 {
946 PGUI_CONSOLE_DATA GuiData = This->Data;
947
948 /* Set the reference count */
949 if (Show) ++GuiData->MouseCursorRefCount;
950 else --GuiData->MouseCursorRefCount;
951
952 /* Effectively show (or hide) the cursor (use special values for (w|l)Param) */
953 PostMessageW(GuiData->hWindow, WM_SETCURSOR, -1, -1);
954
955 return GuiData->MouseCursorRefCount;
956 }
957
958 static BOOL NTAPI
959 GuiSetMouseCursor(IN OUT PFRONTEND This,
960 HCURSOR CursorHandle)
961 {
962 PGUI_CONSOLE_DATA GuiData = This->Data;
963
964 /*
965 * Set the cursor's handle. If the given handle is NULL,
966 * then restore the default cursor.
967 */
968 GuiData->hCursor = (CursorHandle ? CursorHandle : ghDefaultCursor);
969
970 /* Effectively modify the shape of the cursor (use special values for (w|l)Param) */
971 PostMessageW(GuiData->hWindow, WM_SETCURSOR, -1, -1);
972
973 return TRUE;
974 }
975
976 static HMENU NTAPI
977 GuiMenuControl(IN OUT PFRONTEND This,
978 UINT CmdIdLow,
979 UINT CmdIdHigh)
980 {
981 PGUI_CONSOLE_DATA GuiData = This->Data;
982
983 GuiData->CmdIdLow = CmdIdLow ;
984 GuiData->CmdIdHigh = CmdIdHigh;
985
986 return GetSystemMenu(GuiData->hWindow, FALSE);
987 }
988
989 static BOOL NTAPI
990 GuiSetMenuClose(IN OUT PFRONTEND This,
991 BOOL Enable)
992 {
993 /*
994 * NOTE: See http://www.mail-archive.com/harbour@harbour-project.org/msg27509.html
995 * or http://harbour-devel.1590103.n2.nabble.com/Question-about-hb-gt-win-CtrlHandler-usage-td4670862i20.html
996 * for more information.
997 */
998
999 PGUI_CONSOLE_DATA GuiData = This->Data;
1000 HMENU hSysMenu = GetSystemMenu(GuiData->hWindow, FALSE);
1001
1002 if (hSysMenu == NULL) return FALSE;
1003
1004 GuiData->IsCloseButtonEnabled = Enable;
1005 EnableMenuItem(hSysMenu, SC_CLOSE, MF_BYCOMMAND | (Enable ? MF_ENABLED : MF_GRAYED));
1006
1007 return TRUE;
1008 }
1009
1010 static FRONTEND_VTBL GuiVtbl =
1011 {
1012 GuiInitFrontEnd,
1013 GuiDeinitFrontEnd,
1014 GuiDrawRegion,
1015 GuiWriteStream,
1016 GuiRingBell,
1017 GuiSetCursorInfo,
1018 GuiSetScreenInfo,
1019 GuiResizeTerminal,
1020 GuiSetActiveScreenBuffer,
1021 GuiReleaseScreenBuffer,
1022 GuiRefreshInternalInfo,
1023 GuiChangeTitle,
1024 GuiChangeIcon,
1025 GuiGetConsoleWindowHandle,
1026 GuiGetLargestConsoleWindowSize,
1027 GuiGetSelectionInfo,
1028 GuiSetPalette,
1029 GuiGetDisplayMode,
1030 GuiSetDisplayMode,
1031 GuiShowMouseCursor,
1032 GuiSetMouseCursor,
1033 GuiMenuControl,
1034 GuiSetMenuClose,
1035 };
1036
1037
1038 NTSTATUS NTAPI
1039 GuiLoadFrontEnd(IN OUT PFRONTEND FrontEnd,
1040 IN OUT PCONSOLE_INFO ConsoleInfo,
1041 IN OUT PVOID ExtraConsoleInfo,
1042 IN ULONG ProcessId)
1043 {
1044 PCONSOLE_INIT_INFO ConsoleInitInfo = ExtraConsoleInfo;
1045 PGUI_INIT_INFO GuiInitInfo;
1046
1047 if (FrontEnd == NULL || ConsoleInfo == NULL || ConsoleInitInfo == NULL)
1048 return STATUS_INVALID_PARAMETER;
1049
1050 /* Initialize GUI terminal emulator common functionalities */
1051 if (!GuiInit()) return STATUS_UNSUCCESSFUL;
1052
1053 /*
1054 * Initialize a private initialization info structure for later use.
1055 * It must be freed by a call to GuiUnloadFrontEnd or GuiInitFrontEnd.
1056 */
1057 GuiInitInfo = ConsoleAllocHeap(HEAP_ZERO_MEMORY, sizeof(GUI_INIT_INFO));
1058 if (GuiInitInfo == NULL) return STATUS_NO_MEMORY;
1059
1060 // HACK: We suppose that the pointers will be valid in GuiInitFrontEnd...
1061 // If not, then copy exactly what we need in GuiInitInfo.
1062 GuiInitInfo->ConsoleInfo = ConsoleInfo;
1063 GuiInitInfo->ConsoleStartInfo = ConsoleInitInfo->ConsoleStartInfo;
1064 GuiInitInfo->ProcessId = ProcessId;
1065
1066 /* Finally, initialize the frontend structure */
1067 FrontEnd->Vtbl = &GuiVtbl;
1068 FrontEnd->Data = NULL;
1069 FrontEnd->OldData = GuiInitInfo;
1070
1071 return STATUS_SUCCESS;
1072 }
1073
1074 NTSTATUS NTAPI
1075 GuiUnloadFrontEnd(IN OUT PFRONTEND FrontEnd)
1076 {
1077 if (FrontEnd == NULL) return STATUS_INVALID_PARAMETER;
1078
1079 if (FrontEnd->Data) GuiDeinitFrontEnd(FrontEnd);
1080 if (FrontEnd->OldData) ConsoleFreeHeap(FrontEnd->OldData);
1081
1082 return STATUS_SUCCESS;
1083 }
1084
1085 /* EOF */