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
9 * Hermes Belusca-Maito (hermes.belusca@sfr.fr)
12 /* INCLUDES *******************************************************************/
22 // HACK!! Remove it when the hack in GuiWriteStream is fixed
23 #define CONGUI_UPDATE_TIME 0
24 #define CONGUI_UPDATE_TIMER 1
26 #define PM_CREATE_CONSOLE (WM_APP + 1)
27 #define PM_DESTROY_CONSOLE (WM_APP + 2)
30 /* GLOBALS ********************************************************************/
32 typedef struct _GUI_INIT_INFO
34 HANDLE GuiThreadStartupEvent
;
35 ULONG_PTR InputThreadId
;
40 BOOLEAN IsWindowVisible
;
41 GUI_CONSOLE_INFO TermInfo
;
42 } GUI_INIT_INFO
, *PGUI_INIT_INFO
;
44 static BOOL ConsInitialized
= FALSE
;
46 extern HICON ghDefaultIcon
;
47 extern HICON ghDefaultIconSm
;
48 extern HCURSOR ghDefaultCursor
;
51 SetConWndConsoleLeaderCID(IN PGUI_CONSOLE_DATA GuiData
);
53 RegisterConWndClass(IN HINSTANCE hInstance
);
55 UnRegisterConWndClass(HINSTANCE hInstance
);
57 /* FUNCTIONS ******************************************************************/
60 GuiConsoleMoveWindow(PGUI_CONSOLE_DATA GuiData
)
62 /* Move the window if needed (not positioned by the system) */
63 if (!GuiData
->GuiInfo
.AutoPosition
)
65 SetWindowPos(GuiData
->hWindow
,
67 GuiData
->GuiInfo
.WindowOrigin
.x
,
68 GuiData
->GuiInfo
.WindowOrigin
.y
,
70 SWP_NOZORDER
| SWP_NOSIZE
| SWP_NOACTIVATE
);
75 DrawRegion(PGUI_CONSOLE_DATA GuiData
,
80 SmallRectToRect(GuiData
, &RegionRect
, Region
);
81 /* Do not erase the background: it speeds up redrawing and reduce flickering */
82 InvalidateRect(GuiData
->hWindow
, &RegionRect
, FALSE
);
83 /**UpdateWindow(GuiData->hWindow);**/
87 InvalidateCell(PGUI_CONSOLE_DATA GuiData
,
90 SMALL_RECT CellRect
= { x
, y
, x
, y
};
91 DrawRegion(GuiData
, &CellRect
);
95 /******************************************************************************
96 * GUI Terminal Initialization *
97 ******************************************************************************/
100 CreateSysMenu(HWND hWnd
);
103 GuiConsoleInputThread(PVOID Param
)
106 PCSR_THREAD pcsrt
= NULL
;
107 PGUI_INIT_INFO GuiInitInfo
= (PGUI_INIT_INFO
)Param
;
108 DESKTOP_CONSOLE_THREAD DesktopConsoleThreadInfo
;
109 ULONG_PTR InputThreadId
= HandleToUlong(NtCurrentTeb()->ClientId
.UniqueThread
);
110 HANDLE hThread
= NULL
;
112 LONG WindowCount
= 0;
116 * This thread dispatches all the console notifications to the
117 * notification window. It is common for all the console windows
118 * in a given desktop in a window station.
121 /* Assign this console input thread to this desktop */
122 DesktopConsoleThreadInfo
.DesktopHandle
= GuiInitInfo
->Desktop
; // Duplicated desktop handle
123 DesktopConsoleThreadInfo
.ThreadId
= InputThreadId
;
124 Status
= NtUserConsoleControl(ConsoleCtrlDesktopConsoleThread
,
125 &DesktopConsoleThreadInfo
,
126 sizeof(DesktopConsoleThreadInfo
));
127 if (!NT_SUCCESS(Status
)) goto Quit
;
129 /* Connect this CSR thread to the USER subsystem */
130 pcsrt
= CsrConnectToUser();
131 if (pcsrt
== NULL
) goto Quit
;
132 hThread
= pcsrt
->ThreadHandle
;
134 /* Assign the desktop to this thread */
135 if (!SetThreadDesktop(DesktopConsoleThreadInfo
.DesktopHandle
)) goto Quit
;
137 /* The thread has been initialized, set the event */
138 NtSetEvent(GuiInitInfo
->GuiThreadStartupEvent
, NULL
);
139 Status
= STATUS_SUCCESS
;
141 while (GetMessageW(&msg
, NULL
, 0, 0))
145 case PM_CREATE_CONSOLE
:
147 PGUI_CONSOLE_DATA GuiData
= (PGUI_CONSOLE_DATA
)msg
.lParam
;
148 PCONSRV_CONSOLE Console
= GuiData
->Console
;
152 DPRINT("PM_CREATE_CONSOLE -- creating window\n");
154 NewWindow
= CreateWindowExW(WS_EX_CLIENTEDGE
,
156 Console
->Title
.Buffer
,
162 GuiData
->IsWindowVisible
? HWND_DESKTOP
: HWND_MESSAGE
,
166 if (NewWindow
== NULL
)
168 DPRINT1("Failed to create a new console window\n");
172 ASSERT(NewWindow
== GuiData
->hWindow
);
174 InterlockedIncrement(&WindowCount
);
177 // FIXME: TODO: Move everything there into conwnd.c!OnNcCreate()
180 /* Retrieve our real position */
181 // See conwnd.c!OnMove()
182 GetWindowRect(GuiData
->hWindow
, &rcWnd
);
183 GuiData
->GuiInfo
.WindowOrigin
.x
= rcWnd
.left
;
184 GuiData
->GuiInfo
.WindowOrigin
.y
= rcWnd
.top
;
186 if (GuiData
->IsWindowVisible
)
188 /* Move and resize the window to the user's values */
189 /* CAN WE DEADLOCK ?? */
190 GuiConsoleMoveWindow(GuiData
); // FIXME: This MUST be done via the CreateWindowExW call.
191 SendMessageW(GuiData
->hWindow
, PM_RESIZE_TERMINAL
, 0, 0);
194 // FIXME: HACK: Potential HACK for CORE-8129; see revision 63595.
195 CreateSysMenu(GuiData
->hWindow
);
197 if (GuiData
->IsWindowVisible
)
199 /* Switch to full-screen mode if necessary */
200 // FIXME: Move elsewhere, it cause misdrawings of the window.
201 if (GuiData
->GuiInfo
.FullScreen
) SwitchFullScreen(GuiData
, TRUE
);
203 DPRINT("PM_CREATE_CONSOLE -- showing window\n");
204 ShowWindowAsync(NewWindow
, (int)GuiData
->GuiInfo
.ShowWindow
);
208 DPRINT("PM_CREATE_CONSOLE -- hidden window\n");
209 ShowWindowAsync(NewWindow
, SW_HIDE
);
215 case PM_DESTROY_CONSOLE
:
217 PGUI_CONSOLE_DATA GuiData
= (PGUI_CONSOLE_DATA
)msg
.lParam
;
220 /* Exit the full screen mode if it was already set */
221 // LeaveFullScreen(GuiData);
224 * Window creation is done using a PostMessage(), so it's possible
225 * that the window that we want to destroy doesn't exist yet.
226 * So first empty the message queue.
228 while (PeekMessageW(&TempMsg
, NULL
, 0, 0, PM_REMOVE
))
230 DispatchMessageW(&TempMsg
);
233 if (GuiData
->hWindow
== NULL
) continue;
235 DestroyWindow(GuiData
->hWindow
);
237 NtSetEvent(GuiData
->hGuiTermEvent
, NULL
);
239 if (InterlockedDecrement(&WindowCount
) == 0)
241 DPRINT("CONSRV: Going to quit the Input Thread 0x%p\n", InputThreadId
);
249 TranslateMessage(&msg
);
250 DispatchMessageW(&msg
);
254 DPRINT("CONSRV: Quit the Input Thread 0x%p, Status = 0x%08lx\n", InputThreadId
, Status
);
256 /* Remove this console input thread from this desktop */
257 // DesktopConsoleThreadInfo.DesktopHandle;
258 DesktopConsoleThreadInfo
.ThreadId
= 0;
259 NtUserConsoleControl(ConsoleCtrlDesktopConsoleThread
,
260 &DesktopConsoleThreadInfo
,
261 sizeof(DesktopConsoleThreadInfo
));
263 /* Close the duplicated desktop handle */
264 CloseDesktop(DesktopConsoleThreadInfo
.DesktopHandle
); // NtUserCloseDesktop
266 /* Cleanup CSR thread */
269 if (hThread
!= pcsrt
->ThreadHandle
)
270 DPRINT1("WARNING!! hThread (0x%p) != pcsrt->ThreadHandle (0x%p), you may expect crashes soon!!\n", hThread
, pcsrt
->ThreadHandle
);
272 CsrDereferenceThread(pcsrt
);
275 /* Exit the thread */
276 RtlExitUserThread(Status
);
280 // FIXME: Maybe return a NTSTATUS
282 GuiInit(IN PCONSOLE_INIT_INFO ConsoleInitInfo
,
283 IN HANDLE ConsoleLeaderProcessHandle
,
284 IN OUT PGUI_INIT_INFO GuiInitInfo
)
287 UNICODE_STRING DesktopPath
;
288 DESKTOP_CONSOLE_THREAD DesktopConsoleThreadInfo
;
297 * Initialize and register the console window class, if needed.
299 if (!ConsInitialized
)
301 if (!RegisterConWndClass(ConSrvDllInstance
)) return FALSE
;
302 ConsInitialized
= TRUE
;
306 * Set-up the console input thread. We have
307 * one console input thread per desktop.
310 if (!CsrImpersonateClient(NULL
))
311 // return STATUS_BAD_IMPERSONATION_LEVEL;
314 if (ConsoleInitInfo
->DesktopLength
)
316 DesktopPath
.MaximumLength
= ConsoleInitInfo
->DesktopLength
;
317 DesktopPath
.Length
= DesktopPath
.MaximumLength
- sizeof(UNICODE_NULL
);
318 DesktopPath
.Buffer
= ConsoleInitInfo
->Desktop
;
322 RtlInitUnicodeString(&DesktopPath
, L
"Default");
325 hDesk
= NtUserResolveDesktop(ConsoleLeaderProcessHandle
,
329 DPRINT("NtUserResolveDesktop(DesktopPath = '%wZ') returned hDesk = 0x%p; hWinSta = 0x%p\n",
330 &DesktopPath
, hDesk
, hWinSta
);
334 if (hDesk
== NULL
) return FALSE
;
337 * We need to see whether we need to create a
338 * new console input thread for this desktop.
340 DesktopConsoleThreadInfo
.DesktopHandle
= hDesk
;
341 DesktopConsoleThreadInfo
.ThreadId
= (ULONG_PTR
)INVALID_HANDLE_VALUE
; // Special value to say we just want to retrieve the thread ID.
342 NtUserConsoleControl(ConsoleCtrlDesktopConsoleThread
,
343 &DesktopConsoleThreadInfo
,
344 sizeof(DesktopConsoleThreadInfo
));
345 DPRINT("NtUserConsoleControl returned ThreadId = 0x%p\n", DesktopConsoleThreadInfo
.ThreadId
);
348 * Save the opened window station and desktop handles in the initialization
349 * structure. They will be used later on, and released, by the GUI frontend.
351 GuiInitInfo
->WinSta
= hWinSta
;
352 GuiInitInfo
->Desktop
= hDesk
;
354 /* Here GuiInitInfo contains original handles */
356 /* If we already have a console input thread on this desktop... */
357 if (DesktopConsoleThreadInfo
.ThreadId
!= 0)
359 /* ... just use it... */
360 DPRINT("Using input thread InputThreadId = 0x%p\n", DesktopConsoleThreadInfo
.ThreadId
);
361 GuiInitInfo
->InputThreadId
= DesktopConsoleThreadInfo
.ThreadId
;
365 /* ... otherwise create a new one. */
367 /* Initialize a startup event for the thread to signal it */
368 Status
= NtCreateEvent(&GuiInitInfo
->GuiThreadStartupEvent
, EVENT_ALL_ACCESS
,
369 NULL
, SynchronizationEvent
, FALSE
);
370 if (!NT_SUCCESS(Status
))
377 * Duplicate the desktop handle for the console input thread internal needs.
378 * If it happens to need also a window station handle in the future, then
379 * it is there that you also need to duplicate the window station handle!
381 * Note also that we are going to temporarily overwrite the stored handles
382 * in GuiInitInfo because it happens that we use also this structure to give
383 * the duplicated handles to the input thread that is going to initialize.
384 * After the input thread finishes its initialization, we restore the handles
385 * in GuiInitInfo to their old values.
387 Status
= NtDuplicateObject(NtCurrentProcess(),
390 (PHANDLE
)&GuiInitInfo
->Desktop
,
391 0, 0, DUPLICATE_SAME_ACCESS
);
392 if (!NT_SUCCESS(Status
))
398 /* Here GuiInitInfo contains duplicated handles */
400 Status
= RtlCreateUserThread(NtCurrentProcess(),
402 TRUE
, // Start the thread in suspended state
406 (PVOID
)GuiConsoleInputThread
,
410 if (NT_SUCCESS(Status
))
412 /* Add it as a static server thread and resume it */
413 CsrAddStaticServerThread(hInputThread
, &ClientId
, 0);
414 Status
= NtResumeThread(hInputThread
, NULL
);
416 DPRINT("Thread creation hInputThread = 0x%p, InputThreadId = 0x%p, Status = 0x%08lx\n",
417 hInputThread
, ClientId
.UniqueThread
, Status
);
419 if (!NT_SUCCESS(Status
) || hInputThread
== NULL
)
421 /* Close the thread's handle */
422 if (hInputThread
) NtClose(hInputThread
);
424 /* We need to close here the duplicated desktop handle */
425 CloseDesktop(GuiInitInfo
->Desktop
); // NtUserCloseDesktop
427 /* Close the startup event and bail out */
428 NtClose(GuiInitInfo
->GuiThreadStartupEvent
);
430 DPRINT1("CONSRV: Failed to create graphics console thread.\n");
435 /* No need to close hInputThread, this is done by CSR automatically */
437 /* Wait for the thread to finish its initialization, and close the startup event */
438 NtWaitForSingleObject(GuiInitInfo
->GuiThreadStartupEvent
, FALSE
, NULL
);
439 NtClose(GuiInitInfo
->GuiThreadStartupEvent
);
442 * Save the input thread ID for later use, and restore the original handles.
443 * The copies are held by the console input thread.
445 GuiInitInfo
->InputThreadId
= (ULONG_PTR
)ClientId
.UniqueThread
;
446 GuiInitInfo
->WinSta
= hWinSta
;
447 GuiInitInfo
->Desktop
= hDesk
;
449 /* Here GuiInitInfo contains again original handles */
455 * Close the original handles. Do not use the copies in GuiInitInfo
456 * because we may have failed in the middle of the duplicate operation
457 * and the handles stored in GuiInitInfo may have changed.
459 CloseDesktop(hDesk
); // NtUserCloseDesktop
460 CloseWindowStation(hWinSta
); // NtUserCloseWindowStation
467 /******************************************************************************
468 * GUI Console Driver *
469 ******************************************************************************/
472 GuiDeinitFrontEnd(IN OUT PFRONTEND This
);
474 static NTSTATUS NTAPI
475 GuiInitFrontEnd(IN OUT PFRONTEND This
,
476 IN PCONSRV_CONSOLE Console
)
478 PGUI_INIT_INFO GuiInitInfo
;
479 PGUI_CONSOLE_DATA GuiData
;
481 if (This
== NULL
|| Console
== NULL
|| This
->Context2
== NULL
)
482 return STATUS_INVALID_PARAMETER
;
484 ASSERT(This
->Console
== Console
);
486 GuiInitInfo
= This
->Context2
;
488 /* Terminal data allocation */
489 GuiData
= ConsoleAllocHeap(HEAP_ZERO_MEMORY
, sizeof(*GuiData
));
492 DPRINT1("CONSRV: Failed to create GUI_CONSOLE_DATA\n");
493 return STATUS_UNSUCCESSFUL
;
495 /// /* HACK */ Console->FrontEndIFace.Context = (PVOID)GuiData; /* HACK */
496 GuiData
->Console
= Console
;
497 GuiData
->ActiveBuffer
= Console
->ActiveBuffer
;
498 GuiData
->hWindow
= NULL
;
499 GuiData
->IsWindowVisible
= GuiInitInfo
->IsWindowVisible
;
501 /* The console can be resized */
502 Console
->FixedSize
= FALSE
;
504 InitializeCriticalSection(&GuiData
->Lock
);
509 RtlCopyMemory(&GuiData
->GuiInfo
, &GuiInitInfo
->TermInfo
, sizeof(GuiInitInfo
->TermInfo
));
511 /* Initialize the icon handles */
512 if (GuiInitInfo
->hIcon
!= NULL
)
513 GuiData
->hIcon
= GuiInitInfo
->hIcon
;
515 GuiData
->hIcon
= ghDefaultIcon
;
517 if (GuiInitInfo
->hIconSm
!= NULL
)
518 GuiData
->hIconSm
= GuiInitInfo
->hIconSm
;
520 GuiData
->hIconSm
= ghDefaultIconSm
;
522 ASSERT(GuiData
->hIcon
&& GuiData
->hIconSm
);
524 /* Mouse is shown by default with its default cursor shape */
525 GuiData
->hCursor
= ghDefaultCursor
;
526 GuiData
->MouseCursorRefCount
= 0;
528 /* A priori don't ignore mouse signals */
529 GuiData
->IgnoreNextMouseSignal
= FALSE
;
530 /* Initialize HACK FOR CORE-8394. See conwnd.c!OnMouse for more details. */
531 GuiData
->HackCORE8394IgnoreNextMove
= FALSE
;
533 /* Close button and the corresponding system menu item are enabled by default */
534 GuiData
->IsCloseButtonEnabled
= TRUE
;
536 /* There is no user-reserved menu id range by default */
537 GuiData
->CmdIdLow
= GuiData
->CmdIdHigh
= 0;
539 /* Initialize the selection */
540 RtlZeroMemory(&GuiData
->Selection
, sizeof(GuiData
->Selection
));
541 GuiData
->Selection
.dwFlags
= CONSOLE_NO_SELECTION
;
542 RtlZeroMemory(&GuiData
->dwSelectionCursor
, sizeof(GuiData
->dwSelectionCursor
));
543 GuiData
->LineSelection
= FALSE
; // Default to block selection
544 // TODO: Retrieve the selection mode via the registry.
546 GuiData
->InputThreadId
= GuiInitInfo
->InputThreadId
;
547 GuiData
->WinSta
= GuiInitInfo
->WinSta
;
548 GuiData
->Desktop
= GuiInitInfo
->Desktop
;
550 /* Finally, finish to initialize the frontend structure */
551 This
->Context
= GuiData
;
552 ConsoleFreeHeap(This
->Context2
);
553 This
->Context2
= NULL
;
556 * We need to wait until the GUI has been fully initialized
557 * to retrieve custom settings i.e. WindowSize etc...
558 * Ideally we could use SendNotifyMessage for this but its not
561 NtCreateEvent(&GuiData
->hGuiInitEvent
, EVENT_ALL_ACCESS
,
562 NULL
, SynchronizationEvent
, FALSE
);
563 NtCreateEvent(&GuiData
->hGuiTermEvent
, EVENT_ALL_ACCESS
,
564 NULL
, SynchronizationEvent
, FALSE
);
566 DPRINT("GUI - Checkpoint\n");
568 /* Create the terminal window */
569 PostThreadMessageW(GuiData
->InputThreadId
, PM_CREATE_CONSOLE
, 0, (LPARAM
)GuiData
);
571 /* Wait until initialization has finished */
572 NtWaitForSingleObject(GuiData
->hGuiInitEvent
, FALSE
, NULL
);
573 DPRINT("OK we created the console window\n");
574 NtClose(GuiData
->hGuiInitEvent
);
575 GuiData
->hGuiInitEvent
= NULL
;
577 /* Check whether we really succeeded in initializing the terminal window */
578 if (GuiData
->hWindow
== NULL
)
580 DPRINT("GuiInitConsole - We failed at creating a new terminal window\n");
581 GuiDeinitFrontEnd(This
);
582 return STATUS_UNSUCCESSFUL
;
585 return STATUS_SUCCESS
;
589 GuiDeinitFrontEnd(IN OUT PFRONTEND This
)
591 PGUI_CONSOLE_DATA GuiData
= This
->Context
;
593 DPRINT("Send PM_DESTROY_CONSOLE message and wait on hGuiTermEvent...\n");
594 PostThreadMessageW(GuiData
->InputThreadId
, PM_DESTROY_CONSOLE
, 0, (LPARAM
)GuiData
);
595 NtWaitForSingleObject(GuiData
->hGuiTermEvent
, FALSE
, NULL
);
596 DPRINT("hGuiTermEvent set\n");
597 NtClose(GuiData
->hGuiTermEvent
);
598 GuiData
->hGuiTermEvent
= NULL
;
600 CloseDesktop(GuiData
->Desktop
); // NtUserCloseDesktop
601 CloseWindowStation(GuiData
->WinSta
); // NtUserCloseWindowStation
603 DPRINT("Destroying icons !! - GuiData->hIcon = 0x%p ; ghDefaultIcon = 0x%p ; GuiData->hIconSm = 0x%p ; ghDefaultIconSm = 0x%p\n",
604 GuiData
->hIcon
, ghDefaultIcon
, GuiData
->hIconSm
, ghDefaultIconSm
);
605 if (GuiData
->hIcon
!= NULL
&& GuiData
->hIcon
!= ghDefaultIcon
)
607 DPRINT("Destroy hIcon\n");
608 DestroyIcon(GuiData
->hIcon
);
610 if (GuiData
->hIconSm
!= NULL
&& GuiData
->hIconSm
!= ghDefaultIconSm
)
612 DPRINT("Destroy hIconSm\n");
613 DestroyIcon(GuiData
->hIconSm
);
616 This
->Context
= NULL
;
617 DeleteCriticalSection(&GuiData
->Lock
);
618 ConsoleFreeHeap(GuiData
);
620 DPRINT("Quit GuiDeinitFrontEnd\n");
624 GuiDrawRegion(IN OUT PFRONTEND This
,
627 PGUI_CONSOLE_DATA GuiData
= This
->Context
;
629 /* Do nothing if the window is hidden */
630 if (!GuiData
->IsWindowVisible
) return;
632 DrawRegion(GuiData
, Region
);
636 GuiWriteStream(IN OUT PFRONTEND This
,
644 PGUI_CONSOLE_DATA GuiData
= This
->Context
;
645 PCONSOLE_SCREEN_BUFFER Buff
;
646 SHORT CursorEndX
, CursorEndY
;
649 if (NULL
== GuiData
|| NULL
== GuiData
->hWindow
) return;
651 /* Do nothing if the window is hidden */
652 if (!GuiData
->IsWindowVisible
) return;
654 Buff
= GuiData
->ActiveBuffer
;
655 if (GetType(Buff
) != TEXTMODE_BUFFER
) return;
657 if (0 != ScrolledLines
)
661 ScrollRect
.right
= Buff
->ViewSize
.X
* GuiData
->CharWidth
;
662 ScrollRect
.bottom
= Region
->Top
* GuiData
->CharHeight
;
664 ScrollWindowEx(GuiData
->hWindow
,
666 -(int)(ScrolledLines
* GuiData
->CharHeight
),
674 DrawRegion(GuiData
, Region
);
676 if (CursorStartX
< Region
->Left
|| Region
->Right
< CursorStartX
677 || CursorStartY
< Region
->Top
|| Region
->Bottom
< CursorStartY
)
679 InvalidateCell(GuiData
, CursorStartX
, CursorStartY
);
682 CursorEndX
= Buff
->CursorPosition
.X
;
683 CursorEndY
= Buff
->CursorPosition
.Y
;
684 if ((CursorEndX
< Region
->Left
|| Region
->Right
< CursorEndX
685 || CursorEndY
< Region
->Top
|| Region
->Bottom
< CursorEndY
)
686 && (CursorEndX
!= CursorStartX
|| CursorEndY
!= CursorStartY
))
688 InvalidateCell(GuiData
, CursorEndX
, CursorEndY
);
692 // Set up the update timer (very short interval) - this is a "hack" for getting the OS to
693 // repaint the window without having it just freeze up and stay on the screen permanently.
694 Buff
->CursorBlinkOn
= TRUE
;
695 SetTimer(GuiData
->hWindow
, CONGUI_UPDATE_TIMER
, CONGUI_UPDATE_TIME
, NULL
);
698 /* static */ VOID NTAPI
699 GuiRingBell(IN OUT PFRONTEND This
)
701 PGUI_CONSOLE_DATA GuiData
= This
->Context
;
703 /* Emit an error beep sound */
704 SendNotifyMessage(GuiData
->hWindow
, PM_CONSOLE_BEEP
, 0, 0);
708 GuiSetCursorInfo(IN OUT PFRONTEND This
,
709 PCONSOLE_SCREEN_BUFFER Buff
)
711 PGUI_CONSOLE_DATA GuiData
= This
->Context
;
713 /* Do nothing if the window is hidden */
714 if (!GuiData
->IsWindowVisible
) return TRUE
;
716 if (GuiData
->ActiveBuffer
== Buff
)
718 InvalidateCell(GuiData
, Buff
->CursorPosition
.X
, Buff
->CursorPosition
.Y
);
725 GuiSetScreenInfo(IN OUT PFRONTEND This
,
726 PCONSOLE_SCREEN_BUFFER Buff
,
730 PGUI_CONSOLE_DATA GuiData
= This
->Context
;
732 /* Do nothing if the window is hidden */
733 if (!GuiData
->IsWindowVisible
) return TRUE
;
735 if (GuiData
->ActiveBuffer
== Buff
)
737 /* Redraw char at old position (remove cursor) */
738 InvalidateCell(GuiData
, OldCursorX
, OldCursorY
);
739 /* Redraw char at new position (show cursor) */
740 InvalidateCell(GuiData
, Buff
->CursorPosition
.X
, Buff
->CursorPosition
.Y
);
747 GuiResizeTerminal(IN OUT PFRONTEND This
)
749 PGUI_CONSOLE_DATA GuiData
= This
->Context
;
751 /* Resize the window to the user's values */
752 PostMessageW(GuiData
->hWindow
, PM_RESIZE_TERMINAL
, 0, 0);
756 GuiSetActiveScreenBuffer(IN OUT PFRONTEND This
)
758 PGUI_CONSOLE_DATA GuiData
= This
->Context
;
759 PCONSOLE_SCREEN_BUFFER ActiveBuffer
;
762 EnterCriticalSection(&GuiData
->Lock
);
763 GuiData
->WindowSizeLock
= TRUE
;
765 InterlockedExchangePointer((PVOID
*)&GuiData
->ActiveBuffer
,
766 ConDrvGetActiveScreenBuffer(GuiData
->Console
));
768 GuiData
->WindowSizeLock
= FALSE
;
769 LeaveCriticalSection(&GuiData
->Lock
);
771 ActiveBuffer
= GuiData
->ActiveBuffer
;
773 /* Change the current palette */
774 if (ActiveBuffer
->PaletteHandle
== NULL
)
775 hPalette
= GuiData
->hSysPalette
;
777 hPalette
= ActiveBuffer
->PaletteHandle
;
779 DPRINT("GuiSetActiveScreenBuffer using palette 0x%p\n", hPalette
);
781 /* Set the new palette for the framebuffer */
782 SelectPalette(GuiData
->hMemDC
, hPalette
, FALSE
);
784 /* Specify the use of the system palette for the framebuffer */
785 SetSystemPaletteUse(GuiData
->hMemDC
, ActiveBuffer
->PaletteUsage
);
787 /* Realize the (logical) palette */
788 RealizePalette(GuiData
->hMemDC
);
790 GuiResizeTerminal(This
);
791 // ConioDrawConsole(Console);
795 GuiReleaseScreenBuffer(IN OUT PFRONTEND This
,
796 IN PCONSOLE_SCREEN_BUFFER ScreenBuffer
)
798 PGUI_CONSOLE_DATA GuiData
= This
->Context
;
801 * If we were notified to release a screen buffer that is not actually
802 * ours, then just ignore the notification...
804 if (ScreenBuffer
!= GuiData
->ActiveBuffer
) return;
807 * ... else, we must release our active buffer. Two cases are present:
808 * - If ScreenBuffer (== GuiData->ActiveBuffer) IS NOT the console
809 * active screen buffer, then we can safely switch to it.
810 * - If ScreenBuffer IS the console active screen buffer, we must release
814 /* Release the old active palette and set the default one */
815 if (GetCurrentObject(GuiData
->hMemDC
, OBJ_PAL
) == ScreenBuffer
->PaletteHandle
)
817 /* Set the new palette */
818 SelectPalette(GuiData
->hMemDC
, GuiData
->hSysPalette
, FALSE
);
821 /* Set the adequate active screen buffer */
822 if (ScreenBuffer
!= GuiData
->Console
->ActiveBuffer
)
824 GuiSetActiveScreenBuffer(This
);
828 EnterCriticalSection(&GuiData
->Lock
);
829 GuiData
->WindowSizeLock
= TRUE
;
831 InterlockedExchangePointer((PVOID
*)&GuiData
->ActiveBuffer
, NULL
);
833 GuiData
->WindowSizeLock
= FALSE
;
834 LeaveCriticalSection(&GuiData
->Lock
);
839 GuiSetMouseCursor(IN OUT PFRONTEND This
,
840 HCURSOR CursorHandle
);
843 GuiRefreshInternalInfo(IN OUT PFRONTEND This
)
845 PGUI_CONSOLE_DATA GuiData
= This
->Context
;
847 /* Update the console leader information held by the window */
848 SetConWndConsoleLeaderCID(GuiData
);
852 * We reset the cursor here so that, when a console app quits, we reset
853 * the cursor to the default one. It's quite a hack since it doesn't proceed
854 * per - console process... This must be fixed.
856 * See GuiInitConsole(...) for more information.
859 /* Mouse is shown by default with its default cursor shape */
860 GuiData
->MouseCursorRefCount
= 0; // Reinitialize the reference counter
861 GuiSetMouseCursor(This
, NULL
);
865 GuiChangeTitle(IN OUT PFRONTEND This
)
867 PGUI_CONSOLE_DATA GuiData
= This
->Context
;
868 // PostMessageW(GuiData->hWindow, PM_CONSOLE_SET_TITLE, 0, 0);
869 SetWindowTextW(GuiData
->hWindow
, GuiData
->Console
->Title
.Buffer
);
873 GuiChangeIcon(IN OUT PFRONTEND This
,
876 PGUI_CONSOLE_DATA GuiData
= This
->Context
;
877 HICON hIcon
, hIconSm
;
879 if (IconHandle
== NULL
)
881 hIcon
= ghDefaultIcon
;
882 hIconSm
= ghDefaultIconSm
;
886 hIcon
= CopyIcon(IconHandle
);
887 hIconSm
= CopyIcon(IconHandle
);
893 if (hIcon
!= GuiData
->hIcon
)
895 if (GuiData
->hIcon
!= NULL
&& GuiData
->hIcon
!= ghDefaultIcon
)
897 DestroyIcon(GuiData
->hIcon
);
899 if (GuiData
->hIconSm
!= NULL
&& GuiData
->hIconSm
!= ghDefaultIconSm
)
901 DestroyIcon(GuiData
->hIconSm
);
904 GuiData
->hIcon
= hIcon
;
905 GuiData
->hIconSm
= hIconSm
;
907 DPRINT("Set icons in GuiChangeIcon\n");
908 PostMessageW(GuiData
->hWindow
, WM_SETICON
, ICON_BIG
, (LPARAM
)GuiData
->hIcon
);
909 PostMessageW(GuiData
->hWindow
, WM_SETICON
, ICON_SMALL
, (LPARAM
)GuiData
->hIconSm
);
916 GuiGetConsoleWindowHandle(IN OUT PFRONTEND This
)
918 PGUI_CONSOLE_DATA GuiData
= This
->Context
;
919 return GuiData
->hWindow
;
923 GuiGetLargestConsoleWindowSize(IN OUT PFRONTEND This
,
926 PGUI_CONSOLE_DATA GuiData
= This
->Context
;
927 PCONSOLE_SCREEN_BUFFER ActiveBuffer
;
929 MONITORINFO MonitorInfo
;
931 UINT WidthUnit
, HeightUnit
;
936 * Retrieve the monitor that is mostly covered by the current console window;
937 * default to primary monitor otherwise.
939 MonitorInfo
.cbSize
= sizeof(MonitorInfo
);
940 hMonitor
= MonitorFromWindow(GuiData
->hWindow
, MONITOR_DEFAULTTOPRIMARY
);
941 if (hMonitor
&& GetMonitorInfoW(hMonitor
, &MonitorInfo
))
943 /* Retrieve the width and height of the client area of this monitor */
944 Width
= MonitorInfo
.rcWork
.right
- MonitorInfo
.rcWork
.left
;
945 Height
= MonitorInfo
.rcWork
.bottom
- MonitorInfo
.rcWork
.top
;
950 * Retrieve the width and height of the client area for a full-screen
951 * window on the primary display monitor.
953 Width
= GetSystemMetrics(SM_CXFULLSCREEN
);
954 Height
= GetSystemMetrics(SM_CYFULLSCREEN
);
957 // SystemParametersInfoW(SPI_GETWORKAREA, 0, &WorkArea, 0);
958 // Width = WorkArea.right;
959 // Height = WorkArea.bottom;
962 ActiveBuffer
= GuiData
->ActiveBuffer
;
964 // NOTE: This would be surprising if we wouldn't have an associated buffer...
967 GetScreenBufferSizeUnits(ActiveBuffer
, GuiData
, &WidthUnit
, &HeightUnit
);
970 /* Default: graphics mode */
971 WidthUnit
= HeightUnit
= 1;
974 Width
-= (2 * (GetSystemMetrics(SM_CXFRAME
) + GetSystemMetrics(SM_CXEDGE
)));
975 Height
-= (2 * (GetSystemMetrics(SM_CYFRAME
) + GetSystemMetrics(SM_CYEDGE
)) + GetSystemMetrics(SM_CYCAPTION
));
977 if (Width
< 0) Width
= 0;
978 if (Height
< 0) Height
= 0;
980 pSize
->X
= (SHORT
)(Width
/ (int)WidthUnit
) /* HACK */ + 2;
981 pSize
->Y
= (SHORT
)(Height
/ (int)HeightUnit
) /* HACK */ + 1;
985 GuiGetSelectionInfo(IN OUT PFRONTEND This
,
986 PCONSOLE_SELECTION_INFO pSelectionInfo
)
988 PGUI_CONSOLE_DATA GuiData
= This
->Context
;
990 if (pSelectionInfo
== NULL
) return FALSE
;
992 ZeroMemory(pSelectionInfo
, sizeof(*pSelectionInfo
));
993 if (GuiData
->Selection
.dwFlags
!= CONSOLE_NO_SELECTION
)
994 RtlCopyMemory(pSelectionInfo
, &GuiData
->Selection
, sizeof(*pSelectionInfo
));
1000 GuiSetPalette(IN OUT PFRONTEND This
,
1001 HPALETTE PaletteHandle
,
1004 PGUI_CONSOLE_DATA GuiData
= This
->Context
;
1005 HPALETTE OldPalette
;
1007 // if (GetType(GuiData->ActiveBuffer) != GRAPHICS_BUFFER) return FALSE;
1008 if (PaletteHandle
== NULL
) return FALSE
;
1010 /* Set the new palette for the framebuffer */
1011 OldPalette
= SelectPalette(GuiData
->hMemDC
, PaletteHandle
, FALSE
);
1012 if (OldPalette
== NULL
) return FALSE
;
1014 /* Specify the use of the system palette for the framebuffer */
1015 SetSystemPaletteUse(GuiData
->hMemDC
, PaletteUsage
);
1017 /* Realize the (logical) palette */
1018 RealizePalette(GuiData
->hMemDC
);
1020 /* Save the original system palette handle */
1021 if (GuiData
->hSysPalette
== NULL
) GuiData
->hSysPalette
= OldPalette
;
1027 GuiGetDisplayMode(IN OUT PFRONTEND This
)
1029 PGUI_CONSOLE_DATA GuiData
= This
->Context
;
1030 ULONG DisplayMode
= 0;
1032 if (GuiData
->GuiInfo
.FullScreen
)
1033 DisplayMode
|= CONSOLE_FULLSCREEN_HARDWARE
; // CONSOLE_FULLSCREEN
1035 DisplayMode
|= CONSOLE_WINDOWED
;
1041 GuiSetDisplayMode(IN OUT PFRONTEND This
,
1044 PGUI_CONSOLE_DATA GuiData
= This
->Context
;
1047 if (NewMode
& ~(CONSOLE_FULLSCREEN_MODE
| CONSOLE_WINDOWED_MODE
))
1050 /* Do nothing if the window is hidden */
1051 if (!GuiData
->IsWindowVisible
) return TRUE
;
1053 FullScreen
= ((NewMode
& CONSOLE_FULLSCREEN_MODE
) != 0);
1055 if (FullScreen
!= GuiData
->GuiInfo
.FullScreen
)
1057 SwitchFullScreen(GuiData
, FullScreen
);
1064 GuiShowMouseCursor(IN OUT PFRONTEND This
,
1067 PGUI_CONSOLE_DATA GuiData
= This
->Context
;
1069 if (GuiData
->IsWindowVisible
)
1071 /* Set the reference count */
1072 if (Show
) ++GuiData
->MouseCursorRefCount
;
1073 else --GuiData
->MouseCursorRefCount
;
1075 /* Effectively show (or hide) the cursor (use special values for (w|l)Param) */
1076 PostMessageW(GuiData
->hWindow
, WM_SETCURSOR
, -1, -1);
1079 return GuiData
->MouseCursorRefCount
;
1083 GuiSetMouseCursor(IN OUT PFRONTEND This
,
1084 HCURSOR CursorHandle
)
1086 PGUI_CONSOLE_DATA GuiData
= This
->Context
;
1088 /* Do nothing if the window is hidden */
1089 if (!GuiData
->IsWindowVisible
) return TRUE
;
1092 * Set the cursor's handle. If the given handle is NULL,
1093 * then restore the default cursor.
1095 GuiData
->hCursor
= (CursorHandle
? CursorHandle
: ghDefaultCursor
);
1097 /* Effectively modify the shape of the cursor (use special values for (w|l)Param) */
1098 PostMessageW(GuiData
->hWindow
, WM_SETCURSOR
, -1, -1);
1104 GuiMenuControl(IN OUT PFRONTEND This
,
1108 PGUI_CONSOLE_DATA GuiData
= This
->Context
;
1110 GuiData
->CmdIdLow
= CmdIdLow
;
1111 GuiData
->CmdIdHigh
= CmdIdHigh
;
1113 return GetSystemMenu(GuiData
->hWindow
, FALSE
);
1117 GuiSetMenuClose(IN OUT PFRONTEND This
,
1121 * NOTE: See http://www.mail-archive.com/harbour@harbour-project.org/msg27509.html
1122 * or http://harbour-devel.1590103.n2.nabble.com/Question-about-hb-gt-win-CtrlHandler-usage-td4670862i20.html
1123 * for more information.
1126 PGUI_CONSOLE_DATA GuiData
= This
->Context
;
1127 HMENU hSysMenu
= GetSystemMenu(GuiData
->hWindow
, FALSE
);
1129 if (hSysMenu
== NULL
) return FALSE
;
1131 GuiData
->IsCloseButtonEnabled
= Enable
;
1132 EnableMenuItem(hSysMenu
, SC_CLOSE
, MF_BYCOMMAND
| (Enable
? MF_ENABLED
: MF_GRAYED
));
1137 static FRONTEND_VTBL GuiVtbl
=
1147 GuiSetActiveScreenBuffer
,
1148 GuiReleaseScreenBuffer
,
1149 GuiRefreshInternalInfo
,
1152 GuiGetConsoleWindowHandle
,
1153 GuiGetLargestConsoleWindowSize
,
1154 GuiGetSelectionInfo
,
1166 GuiLoadFrontEnd(IN OUT PFRONTEND FrontEnd
,
1167 IN OUT PCONSOLE_STATE_INFO ConsoleInfo
,
1168 IN OUT PCONSOLE_INIT_INFO ConsoleInitInfo
,
1169 IN HANDLE ConsoleLeaderProcessHandle
)
1171 PCONSOLE_START_INFO ConsoleStartInfo
;
1172 PGUI_INIT_INFO GuiInitInfo
;
1173 USEROBJECTFLAGS UserObjectFlags
;
1175 if (FrontEnd
== NULL
|| ConsoleInfo
== NULL
|| ConsoleInitInfo
== NULL
)
1176 return STATUS_INVALID_PARAMETER
;
1178 ConsoleStartInfo
= ConsoleInitInfo
->ConsoleStartInfo
;
1181 * Initialize a private initialization info structure for later use.
1182 * It must be freed by a call to GuiUnloadFrontEnd or GuiInitFrontEnd.
1184 GuiInitInfo
= ConsoleAllocHeap(HEAP_ZERO_MEMORY
, sizeof(*GuiInitInfo
));
1185 if (GuiInitInfo
== NULL
) return STATUS_NO_MEMORY
;
1187 /* Initialize GUI terminal emulator common functionalities */
1188 if (!GuiInit(ConsoleInitInfo
, ConsoleLeaderProcessHandle
, GuiInitInfo
))
1190 ConsoleFreeHeap(GuiInitInfo
);
1191 return STATUS_UNSUCCESSFUL
;
1194 GuiInitInfo
->IsWindowVisible
= ConsoleInitInfo
->IsWindowVisible
;
1195 if (GuiInitInfo
->IsWindowVisible
)
1197 /* Don't show the console if the window station is not interactive */
1198 if (GetUserObjectInformationW(GuiInitInfo
->WinSta
,
1201 sizeof(UserObjectFlags
),
1204 if (!(UserObjectFlags
.dwFlags
& WSF_VISIBLE
))
1205 GuiInitInfo
->IsWindowVisible
= FALSE
;
1210 * Load terminal settings
1213 /* Impersonate the caller in order to retrieve settings in its context */
1214 // if (!CsrImpersonateClient(NULL))
1215 // return STATUS_UNSUCCESSFUL;
1216 CsrImpersonateClient(NULL
);
1218 /* 1. Load the default settings */
1219 GuiConsoleGetDefaultSettings(&GuiInitInfo
->TermInfo
);
1222 GuiInitInfo
->TermInfo
.ShowWindow
= SW_SHOWNORMAL
;
1224 if (GuiInitInfo
->IsWindowVisible
)
1226 /* 2. Load the remaining console settings via the registry */
1227 if ((ConsoleStartInfo
->dwStartupFlags
& STARTF_TITLEISLINKNAME
) == 0)
1230 /* Load the terminal infos from the registry */
1231 GuiConsoleReadUserSettings(&GuiInitInfo
->TermInfo
);
1235 * Now, update them with the properties the user might gave to us
1236 * via the STARTUPINFO structure before calling CreateProcess
1237 * (and which was transmitted via the ConsoleStartInfo structure).
1238 * We therefore overwrite the values read in the registry.
1240 if (ConsoleStartInfo
->dwStartupFlags
& STARTF_USESHOWWINDOW
)
1242 GuiInitInfo
->TermInfo
.ShowWindow
= ConsoleStartInfo
->wShowWindow
;
1244 if (ConsoleStartInfo
->dwStartupFlags
& STARTF_USEPOSITION
)
1246 ConsoleInfo
->AutoPosition
= FALSE
;
1247 ConsoleInfo
->WindowPosition
.x
= ConsoleStartInfo
->dwWindowOrigin
.X
;
1248 ConsoleInfo
->WindowPosition
.y
= ConsoleStartInfo
->dwWindowOrigin
.Y
;
1250 if (ConsoleStartInfo
->dwStartupFlags
& STARTF_RUNFULLSCREEN
)
1252 ConsoleInfo
->FullScreen
= TRUE
;
1258 /* Revert impersonation */
1263 StringCchCopyNW(GuiInitInfo
->TermInfo
.FaceName
, ARRAYSIZE(GuiInitInfo
->TermInfo
.FaceName
),
1264 ConsoleInfo
->FaceName
, ARRAYSIZE(ConsoleInfo
->FaceName
));
1265 GuiInitInfo
->TermInfo
.FontFamily
= ConsoleInfo
->FontFamily
;
1266 GuiInitInfo
->TermInfo
.FontSize
= ConsoleInfo
->FontSize
;
1267 GuiInitInfo
->TermInfo
.FontWeight
= ConsoleInfo
->FontWeight
;
1270 GuiInitInfo
->TermInfo
.FullScreen
= ConsoleInfo
->FullScreen
;
1271 GuiInitInfo
->TermInfo
.AutoPosition
= ConsoleInfo
->AutoPosition
;
1272 GuiInitInfo
->TermInfo
.WindowOrigin
= ConsoleInfo
->WindowPosition
;
1274 /* Initialize the icon handles */
1275 // if (ConsoleStartInfo->hIcon != NULL)
1276 GuiInitInfo
->hIcon
= ConsoleStartInfo
->hIcon
;
1278 // GuiInitInfo->hIcon = ghDefaultIcon;
1280 // if (ConsoleStartInfo->hIconSm != NULL)
1281 GuiInitInfo
->hIconSm
= ConsoleStartInfo
->hIconSm
;
1283 // GuiInitInfo->hIconSm = ghDefaultIconSm;
1285 // ASSERT(GuiInitInfo->hIcon && GuiInitInfo->hIconSm);
1287 /* Finally, initialize the frontend structure */
1288 FrontEnd
->Vtbl
= &GuiVtbl
;
1289 FrontEnd
->Context
= NULL
;
1290 FrontEnd
->Context2
= GuiInitInfo
;
1292 return STATUS_SUCCESS
;
1296 GuiUnloadFrontEnd(IN OUT PFRONTEND FrontEnd
)
1298 if (FrontEnd
== NULL
) return STATUS_INVALID_PARAMETER
;
1300 if (FrontEnd
->Context
) GuiDeinitFrontEnd(FrontEnd
);
1301 if (FrontEnd
->Context2
) ConsoleFreeHeap(FrontEnd
->Context2
);
1303 return STATUS_SUCCESS
;