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 GetScreenBufferSizeUnits(IN PCONSOLE_SCREEN_BUFFER Buffer
,
61 IN PGUI_CONSOLE_DATA GuiData
,
65 if (Buffer
== NULL
|| GuiData
== NULL
||
66 WidthUnit
== NULL
|| HeightUnit
== NULL
)
71 if (GetType(Buffer
) == TEXTMODE_BUFFER
)
73 *WidthUnit
= GuiData
->CharWidth
;
74 *HeightUnit
= GuiData
->CharHeight
;
76 else /* if (GetType(Buffer) == GRAPHICS_BUFFER) */
84 GuiConsoleMoveWindow(PGUI_CONSOLE_DATA GuiData
)
86 /* Move the window if needed (not positioned by the system) */
87 if (!GuiData
->GuiInfo
.AutoPosition
)
89 SetWindowPos(GuiData
->hWindow
,
91 GuiData
->GuiInfo
.WindowOrigin
.x
,
92 GuiData
->GuiInfo
.WindowOrigin
.y
,
94 SWP_NOZORDER
| SWP_NOSIZE
| SWP_NOACTIVATE
);
99 SmallRectToRect(PGUI_CONSOLE_DATA GuiData
, PRECT Rect
, PSMALL_RECT SmallRect
)
101 PCONSOLE_SCREEN_BUFFER Buffer
= GuiData
->ActiveBuffer
;
102 UINT WidthUnit
, HeightUnit
;
104 GetScreenBufferSizeUnits(Buffer
, GuiData
, &WidthUnit
, &HeightUnit
);
106 Rect
->left
= (SmallRect
->Left
- Buffer
->ViewOrigin
.X
) * WidthUnit
;
107 Rect
->top
= (SmallRect
->Top
- Buffer
->ViewOrigin
.Y
) * HeightUnit
;
108 Rect
->right
= (SmallRect
->Right
+ 1 - Buffer
->ViewOrigin
.X
) * WidthUnit
;
109 Rect
->bottom
= (SmallRect
->Bottom
+ 1 - Buffer
->ViewOrigin
.Y
) * HeightUnit
;
113 DrawRegion(PGUI_CONSOLE_DATA GuiData
,
118 SmallRectToRect(GuiData
, &RegionRect
, Region
);
119 /* Do not erase the background: it speeds up redrawing and reduce flickering */
120 InvalidateRect(GuiData
->hWindow
, &RegionRect
, FALSE
);
121 /**UpdateWindow(GuiData->hWindow);**/
125 InvalidateCell(PGUI_CONSOLE_DATA GuiData
,
128 SMALL_RECT CellRect
= { x
, y
, x
, y
};
129 DrawRegion(GuiData
, &CellRect
);
133 /******************************************************************************
134 * GUI Terminal Initialization *
135 ******************************************************************************/
138 SwitchFullScreen(PGUI_CONSOLE_DATA GuiData
, BOOL FullScreen
);
140 CreateSysMenu(HWND hWnd
);
143 GuiConsoleInputThread(PVOID Param
)
146 PCSR_THREAD pcsrt
= NULL
;
147 PGUI_INIT_INFO GuiInitInfo
= (PGUI_INIT_INFO
)Param
;
148 DESKTOP_CONSOLE_THREAD DesktopConsoleThreadInfo
;
149 ULONG_PTR InputThreadId
= HandleToUlong(NtCurrentTeb()->ClientId
.UniqueThread
);
150 HANDLE hThread
= NULL
;
152 LONG WindowCount
= 0;
156 * This thread dispatches all the console notifications to the
157 * notification window. It is common for all the console windows
158 * in a given desktop in a window station.
161 /* Assign this console input thread to this desktop */
162 DesktopConsoleThreadInfo
.DesktopHandle
= GuiInitInfo
->Desktop
; // Duplicated desktop handle
163 DesktopConsoleThreadInfo
.ThreadId
= InputThreadId
;
164 Status
= NtUserConsoleControl(ConsoleCtrlDesktopConsoleThread
,
165 &DesktopConsoleThreadInfo
,
166 sizeof(DesktopConsoleThreadInfo
));
167 if (!NT_SUCCESS(Status
)) goto Quit
;
169 /* Connect this CSR thread to the USER subsystem */
171 PCSR_THREAD CurrThread
= CsrGetClientThread();
172 PCSR_PROCESS CsrProcess
;
174 DPRINT1("CsrConnectToUser being called; 0x%p [0x%x, 0x%x]...\n",
175 CurrThread
, (CurrThread
? CurrThread
->ClientId
.UniqueProcess
: (ULONG_PTR
)-1), (CurrThread
? CurrThread
->ClientId
.UniqueThread
: (ULONG_PTR
)-1));
177 pcsrt
= CsrConnectToUser();
178 if (pcsrt
== NULL
) goto Quit
;
179 hThread
= pcsrt
->ThreadHandle
;
181 CurrThread
= CsrGetClientThread();
182 CsrProcess
= CurrThread
->Process
;
184 DPRINT1("CsrConnectToUser was successfully called; P:[0x%x, 0x%x] T:[0x%x, 0x%x] Process: 0x%p; Thread: 0x%p -- hThread = 0x%p, pcsrt = 0x%p; pcsrt->Process = 0x%p; pcsrt->ThreadHandle = 0x%p from [0x%x, 0x%x]\n",
185 CsrProcess
->ClientId
.UniqueProcess
, CsrProcess
->ClientId
.UniqueThread
,
186 CurrThread
->ClientId
.UniqueProcess
, CurrThread
->ClientId
.UniqueThread
,
187 CsrProcess
, CurrThread
,
188 hThread
, pcsrt
, pcsrt
->Process
, pcsrt
->ThreadHandle
,
189 pcsrt
->ClientId
.UniqueProcess
, pcsrt
->ClientId
.UniqueThread
);
192 /* Assign the desktop to this thread */
193 if (!SetThreadDesktop(DesktopConsoleThreadInfo
.DesktopHandle
)) goto Quit
;
195 /* The thread has been initialized, set the event */
196 NtSetEvent(GuiInitInfo
->GuiThreadStartupEvent
, NULL
);
197 Status
= STATUS_SUCCESS
;
199 while (GetMessageW(&msg
, NULL
, 0, 0))
203 case PM_CREATE_CONSOLE
:
205 PGUI_CONSOLE_DATA GuiData
= (PGUI_CONSOLE_DATA
)msg
.lParam
;
206 PCONSRV_CONSOLE Console
= GuiData
->Console
;
210 DPRINT("PM_CREATE_CONSOLE -- creating window\n");
212 NewWindow
= CreateWindowExW(WS_EX_CLIENTEDGE
,
214 Console
->Title
.Buffer
,
215 WS_OVERLAPPEDWINDOW
| WS_HSCROLL
| WS_VSCROLL
,
220 GuiData
->IsWindowVisible
? HWND_DESKTOP
: HWND_MESSAGE
,
224 if (NewWindow
== NULL
)
226 DPRINT1("Failed to create a new console window\n");
230 ASSERT(NewWindow
== GuiData
->hWindow
);
232 InterlockedIncrement(&WindowCount
);
235 // FIXME: TODO: Move everything there into conwnd.c!OnNcCreate()
238 /* Retrieve our real position */
239 // See conwnd.c!OnMove()
240 GetWindowRect(GuiData
->hWindow
, &rcWnd
);
241 GuiData
->GuiInfo
.WindowOrigin
.x
= rcWnd
.left
;
242 GuiData
->GuiInfo
.WindowOrigin
.y
= rcWnd
.top
;
244 if (GuiData
->IsWindowVisible
)
246 /* Move and resize the window to the user's values */
247 /* CAN WE DEADLOCK ?? */
248 GuiConsoleMoveWindow(GuiData
); // FIXME: This MUST be done via the CreateWindowExW call.
249 SendMessageW(GuiData
->hWindow
, PM_RESIZE_TERMINAL
, 0, 0);
252 // FIXME: HACK: Potential HACK for CORE-8129; see revision 63595.
253 CreateSysMenu(GuiData
->hWindow
);
255 if (GuiData
->IsWindowVisible
)
257 /* Switch to full-screen mode if necessary */
258 // FIXME: Move elsewhere, it cause misdrawings of the window.
259 if (GuiData
->GuiInfo
.FullScreen
) SwitchFullScreen(GuiData
, TRUE
);
261 DPRINT("PM_CREATE_CONSOLE -- showing window\n");
262 // ShowWindow(NewWindow, (int)GuiData->GuiInfo.ShowWindow);
263 ShowWindowAsync(NewWindow
, (int)GuiData
->GuiInfo
.ShowWindow
);
264 DPRINT("Window showed\n");
268 DPRINT("PM_CREATE_CONSOLE -- hidden window\n");
269 ShowWindowAsync(NewWindow
, SW_HIDE
);
275 case PM_DESTROY_CONSOLE
:
277 PGUI_CONSOLE_DATA GuiData
= (PGUI_CONSOLE_DATA
)msg
.lParam
;
280 /* Exit the full screen mode if it was already set */
281 // LeaveFullScreen(GuiData);
284 * Window creation is done using a PostMessage(), so it's possible
285 * that the window that we want to destroy doesn't exist yet.
286 * So first empty the message queue.
289 while (PeekMessageW(&TempMsg, NULL, 0, 0, PM_REMOVE))
291 TranslateMessage(&TempMsg);
292 DispatchMessageW(&TempMsg);
294 while (PeekMessageW(&TempMsg
, NULL
, 0, 0, PM_REMOVE
)) ;
296 if (GuiData
->hWindow
== NULL
) continue;
298 DestroyWindow(GuiData
->hWindow
);
300 NtSetEvent(GuiData
->hGuiTermEvent
, NULL
);
302 if (InterlockedDecrement(&WindowCount
) == 0)
304 DPRINT("CONSRV: Going to quit the Input Thread 0x%p\n", InputThreadId
);
312 TranslateMessage(&msg
);
313 DispatchMessageW(&msg
);
317 DPRINT("CONSRV: Quit the Input Thread 0x%p, Status = 0x%08lx\n", InputThreadId
, Status
);
319 /* Remove this console input thread from this desktop */
320 // DesktopConsoleThreadInfo.DesktopHandle;
321 DesktopConsoleThreadInfo
.ThreadId
= 0;
322 NtUserConsoleControl(ConsoleCtrlDesktopConsoleThread
,
323 &DesktopConsoleThreadInfo
,
324 sizeof(DesktopConsoleThreadInfo
));
326 /* Close the duplicated desktop handle */
327 CloseDesktop(DesktopConsoleThreadInfo
.DesktopHandle
); // NtUserCloseDesktop
329 /* Cleanup CSR thread */
332 PCSR_THREAD CurrThread
= CsrGetClientThread();
334 DPRINT1("CsrDereferenceThread being called; [0x%x, 0x%x] -- hThread = 0x%p, pcsrt->Process = 0x%p; pcsrt->ThreadHandle = 0x%p from [0x%x, 0x%x]\n",
335 CurrThread
->ClientId
.UniqueProcess
, CurrThread
->ClientId
.UniqueThread
,
336 hThread
, pcsrt
->Process
, pcsrt
->ThreadHandle
,
337 pcsrt
->ClientId
.UniqueProcess
, pcsrt
->ClientId
.UniqueThread
);
339 if (hThread
!= pcsrt
->ThreadHandle
)
340 DPRINT1("WARNING!! hThread != pcsrt->ThreadHandle, you may expect crashes soon!!\n");
342 CsrDereferenceThread(pcsrt
);
345 /* Exit the thread */
346 RtlExitUserThread(Status
);
350 // FIXME: Maybe return a NTSTATUS
352 GuiInit(IN PCONSOLE_INIT_INFO ConsoleInitInfo
,
353 IN HANDLE ConsoleLeaderProcessHandle
,
354 IN OUT PGUI_INIT_INFO GuiInitInfo
)
357 UNICODE_STRING DesktopPath
;
358 DESKTOP_CONSOLE_THREAD DesktopConsoleThreadInfo
;
367 * Initialize and register the console window class, if needed.
369 if (!ConsInitialized
)
371 if (!RegisterConWndClass(ConSrvDllInstance
)) return FALSE
;
372 ConsInitialized
= TRUE
;
376 * Set-up the console input thread. We have
377 * one console input thread per desktop.
380 if (!CsrImpersonateClient(NULL
))
381 // return STATUS_BAD_IMPERSONATION_LEVEL;
384 if (ConsoleInitInfo
->DesktopLength
)
386 DesktopPath
.MaximumLength
= ConsoleInitInfo
->DesktopLength
;
387 DesktopPath
.Length
= DesktopPath
.MaximumLength
- sizeof(UNICODE_NULL
);
388 DesktopPath
.Buffer
= ConsoleInitInfo
->Desktop
;
392 RtlInitUnicodeString(&DesktopPath
, L
"Default");
395 hDesk
= NtUserResolveDesktop(ConsoleLeaderProcessHandle
,
399 DPRINT("NtUserResolveDesktop(DesktopPath = '%wZ') returned hDesk = 0x%p; hWinSta = 0x%p\n",
400 &DesktopPath
, hDesk
, hWinSta
);
404 if (hDesk
== NULL
) return FALSE
;
407 * We need to see whether we need to create a
408 * new console input thread for this desktop.
410 DesktopConsoleThreadInfo
.DesktopHandle
= hDesk
;
411 DesktopConsoleThreadInfo
.ThreadId
= (ULONG_PTR
)INVALID_HANDLE_VALUE
; // Special value to say we just want to retrieve the thread ID.
412 NtUserConsoleControl(ConsoleCtrlDesktopConsoleThread
,
413 &DesktopConsoleThreadInfo
,
414 sizeof(DesktopConsoleThreadInfo
));
415 DPRINT("NtUserConsoleControl returned ThreadId = 0x%p\n", DesktopConsoleThreadInfo
.ThreadId
);
418 * Save the opened window station and desktop handles in the initialization
419 * structure. They will be used later on, and released, by the GUI frontend.
421 GuiInitInfo
->WinSta
= hWinSta
;
422 GuiInitInfo
->Desktop
= hDesk
;
424 /* Here GuiInitInfo contains original handles */
426 /* If we already have a console input thread on this desktop... */
427 if (DesktopConsoleThreadInfo
.ThreadId
!= 0)
429 /* ... just use it... */
430 DPRINT("Using input thread InputThreadId = 0x%p\n", DesktopConsoleThreadInfo
.ThreadId
);
431 GuiInitInfo
->InputThreadId
= DesktopConsoleThreadInfo
.ThreadId
;
435 /* ... otherwise create a new one. */
437 /* Initialize a startup event for the thread to signal it */
438 Status
= NtCreateEvent(&GuiInitInfo
->GuiThreadStartupEvent
, EVENT_ALL_ACCESS
,
439 NULL
, SynchronizationEvent
, FALSE
);
440 if (!NT_SUCCESS(Status
))
447 * Duplicate the desktop handle for the console input thread internal needs.
448 * If it happens to need also a window station handle in the future, then
449 * it is there that you also need to duplicate the window station handle!
451 * Note also that we are going to temporarily overwrite the stored handles
452 * in GuiInitInfo because it happens that we use also this structure to give
453 * the duplicated handles to the input thread that is going to initialize.
454 * After the input thread finishes its initialization, we restore the handles
455 * in GuiInitInfo to their old values.
457 Status
= NtDuplicateObject(NtCurrentProcess(),
460 (PHANDLE
)&GuiInitInfo
->Desktop
,
461 0, 0, DUPLICATE_SAME_ACCESS
);
462 if (!NT_SUCCESS(Status
))
468 /* Here GuiInitInfo contains duplicated handles */
470 Status
= RtlCreateUserThread(NtCurrentProcess(),
472 TRUE
, // Start the thread in suspended state
476 (PVOID
)GuiConsoleInputThread
,
480 if (NT_SUCCESS(Status
))
482 /* Add it as a static server thread and resume it */
483 CsrAddStaticServerThread(hInputThread
, &ClientId
, 0);
484 Status
= NtResumeThread(hInputThread
, NULL
);
486 DPRINT("Thread creation hInputThread = 0x%p, InputThreadId = 0x%p, Status = 0x%08lx\n",
487 hInputThread
, ClientId
.UniqueThread
, Status
);
489 if (!NT_SUCCESS(Status
) || hInputThread
== NULL
)
491 /* Close the thread's handle */
492 if (hInputThread
) NtClose(hInputThread
);
494 /* We need to close here the duplicated desktop handle */
495 CloseDesktop(GuiInitInfo
->Desktop
); // NtUserCloseDesktop
497 /* Close the startup event and bail out */
498 NtClose(GuiInitInfo
->GuiThreadStartupEvent
);
500 DPRINT1("CONSRV: Failed to create graphics console thread.\n");
505 /* No need to close hInputThread, this is done by CSR automatically */
507 /* Wait for the thread to finish its initialization, and close the startup event */
508 NtWaitForSingleObject(GuiInitInfo
->GuiThreadStartupEvent
, FALSE
, NULL
);
509 NtClose(GuiInitInfo
->GuiThreadStartupEvent
);
512 * Save the input thread ID for later use, and restore the original handles.
513 * The copies are held by the console input thread.
515 GuiInitInfo
->InputThreadId
= (ULONG_PTR
)ClientId
.UniqueThread
;
516 GuiInitInfo
->WinSta
= hWinSta
;
517 GuiInitInfo
->Desktop
= hDesk
;
519 /* Here GuiInitInfo contains again original handles */
525 * Close the original handles. Do not use the copies in GuiInitInfo
526 * because we may have failed in the middle of the duplicate operation
527 * and the handles stored in GuiInitInfo may have changed.
529 CloseDesktop(hDesk
); // NtUserCloseDesktop
530 CloseWindowStation(hWinSta
); // NtUserCloseWindowStation
537 /******************************************************************************
538 * GUI Console Driver *
539 ******************************************************************************/
542 GuiDeinitFrontEnd(IN OUT PFRONTEND This
);
544 static NTSTATUS NTAPI
545 GuiInitFrontEnd(IN OUT PFRONTEND This
,
546 IN PCONSRV_CONSOLE Console
)
548 PGUI_INIT_INFO GuiInitInfo
;
549 PGUI_CONSOLE_DATA GuiData
;
551 if (This
== NULL
|| Console
== NULL
|| This
->Context2
== NULL
)
552 return STATUS_INVALID_PARAMETER
;
554 ASSERT(This
->Console
== Console
);
556 GuiInitInfo
= This
->Context2
;
558 /* Terminal data allocation */
559 GuiData
= ConsoleAllocHeap(HEAP_ZERO_MEMORY
, sizeof(*GuiData
));
562 DPRINT1("CONSRV: Failed to create GUI_CONSOLE_DATA\n");
563 return STATUS_UNSUCCESSFUL
;
565 /// /* HACK */ Console->FrontEndIFace.Context = (PVOID)GuiData; /* HACK */
566 GuiData
->Console
= Console
;
567 GuiData
->ActiveBuffer
= Console
->ActiveBuffer
;
568 GuiData
->hWindow
= NULL
;
569 GuiData
->IsWindowVisible
= GuiInitInfo
->IsWindowVisible
;
571 /* The console can be resized */
572 Console
->FixedSize
= FALSE
;
574 InitializeCriticalSection(&GuiData
->Lock
);
579 RtlCopyMemory(&GuiData
->GuiInfo
, &GuiInitInfo
->TermInfo
, sizeof(GuiInitInfo
->TermInfo
));
581 /* Initialize the icon handles */
582 if (GuiInitInfo
->hIcon
!= NULL
)
583 GuiData
->hIcon
= GuiInitInfo
->hIcon
;
585 GuiData
->hIcon
= ghDefaultIcon
;
587 if (GuiInitInfo
->hIconSm
!= NULL
)
588 GuiData
->hIconSm
= GuiInitInfo
->hIconSm
;
590 GuiData
->hIconSm
= ghDefaultIconSm
;
592 ASSERT(GuiData
->hIcon
&& GuiData
->hIconSm
);
594 /* Mouse is shown by default with its default cursor shape */
595 GuiData
->hCursor
= ghDefaultCursor
;
596 GuiData
->MouseCursorRefCount
= 0;
598 /* A priori don't ignore mouse signals */
599 GuiData
->IgnoreNextMouseSignal
= FALSE
;
600 /* Initialize HACK FOR CORE-8394. See conwnd.c!OnMouse for more details. */
601 GuiData
->HackCORE8394IgnoreNextMove
= FALSE
;
603 /* Close button and the corresponding system menu item are enabled by default */
604 GuiData
->IsCloseButtonEnabled
= TRUE
;
606 /* There is no user-reserved menu id range by default */
607 GuiData
->CmdIdLow
= GuiData
->CmdIdHigh
= 0;
609 /* Initialize the selection */
610 RtlZeroMemory(&GuiData
->Selection
, sizeof(GuiData
->Selection
));
611 GuiData
->Selection
.dwFlags
= CONSOLE_NO_SELECTION
;
612 RtlZeroMemory(&GuiData
->dwSelectionCursor
, sizeof(GuiData
->dwSelectionCursor
));
613 GuiData
->LineSelection
= FALSE
; // Default to block selection
614 // TODO: Retrieve the selection mode via the registry.
616 GuiData
->InputThreadId
= GuiInitInfo
->InputThreadId
;
617 GuiData
->WinSta
= GuiInitInfo
->WinSta
;
618 GuiData
->Desktop
= GuiInitInfo
->Desktop
;
620 /* Finally, finish to initialize the frontend structure */
621 This
->Context
= GuiData
;
622 ConsoleFreeHeap(This
->Context2
);
623 This
->Context2
= NULL
;
626 * We need to wait until the GUI has been fully initialized
627 * to retrieve custom settings i.e. WindowSize etc...
628 * Ideally we could use SendNotifyMessage for this but its not
631 NtCreateEvent(&GuiData
->hGuiInitEvent
, EVENT_ALL_ACCESS
,
632 NULL
, SynchronizationEvent
, FALSE
);
633 NtCreateEvent(&GuiData
->hGuiTermEvent
, EVENT_ALL_ACCESS
,
634 NULL
, SynchronizationEvent
, FALSE
);
636 DPRINT("GUI - Checkpoint\n");
638 /* Create the terminal window */
639 PostThreadMessageW(GuiData
->InputThreadId
, PM_CREATE_CONSOLE
, 0, (LPARAM
)GuiData
);
641 /* Wait until initialization has finished */
642 NtWaitForSingleObject(GuiData
->hGuiInitEvent
, FALSE
, NULL
);
643 DPRINT("OK we created the console window\n");
644 NtClose(GuiData
->hGuiInitEvent
);
645 GuiData
->hGuiInitEvent
= NULL
;
647 /* Check whether we really succeeded in initializing the terminal window */
648 if (GuiData
->hWindow
== NULL
)
650 DPRINT("GuiInitConsole - We failed at creating a new terminal window\n");
651 GuiDeinitFrontEnd(This
);
652 return STATUS_UNSUCCESSFUL
;
655 return STATUS_SUCCESS
;
659 GuiDeinitFrontEnd(IN OUT PFRONTEND This
)
661 PGUI_CONSOLE_DATA GuiData
= This
->Context
;
663 DPRINT("Send PM_DESTROY_CONSOLE message and wait on hGuiTermEvent...\n");
664 PostThreadMessageW(GuiData
->InputThreadId
, PM_DESTROY_CONSOLE
, 0, (LPARAM
)GuiData
);
665 NtWaitForSingleObject(GuiData
->hGuiTermEvent
, FALSE
, NULL
);
666 DPRINT("hGuiTermEvent set\n");
667 NtClose(GuiData
->hGuiTermEvent
);
668 GuiData
->hGuiTermEvent
= NULL
;
670 CloseDesktop(GuiData
->Desktop
); // NtUserCloseDesktop
671 CloseWindowStation(GuiData
->WinSta
); // NtUserCloseWindowStation
673 DPRINT("Destroying icons !! - GuiData->hIcon = 0x%p ; ghDefaultIcon = 0x%p ; GuiData->hIconSm = 0x%p ; ghDefaultIconSm = 0x%p\n",
674 GuiData
->hIcon
, ghDefaultIcon
, GuiData
->hIconSm
, ghDefaultIconSm
);
675 if (GuiData
->hIcon
!= NULL
&& GuiData
->hIcon
!= ghDefaultIcon
)
677 DPRINT("Destroy hIcon\n");
678 DestroyIcon(GuiData
->hIcon
);
680 if (GuiData
->hIconSm
!= NULL
&& GuiData
->hIconSm
!= ghDefaultIconSm
)
682 DPRINT("Destroy hIconSm\n");
683 DestroyIcon(GuiData
->hIconSm
);
686 This
->Context
= NULL
;
687 DeleteCriticalSection(&GuiData
->Lock
);
688 ConsoleFreeHeap(GuiData
);
690 DPRINT("Quit GuiDeinitFrontEnd\n");
694 GuiDrawRegion(IN OUT PFRONTEND This
,
697 PGUI_CONSOLE_DATA GuiData
= This
->Context
;
699 /* Do nothing if the window is hidden */
700 if (!GuiData
->IsWindowVisible
) return;
702 DrawRegion(GuiData
, Region
);
706 GuiWriteStream(IN OUT PFRONTEND This
,
714 PGUI_CONSOLE_DATA GuiData
= This
->Context
;
715 PCONSOLE_SCREEN_BUFFER Buff
;
716 SHORT CursorEndX
, CursorEndY
;
719 if (NULL
== GuiData
|| NULL
== GuiData
->hWindow
) return;
721 /* Do nothing if the window is hidden */
722 if (!GuiData
->IsWindowVisible
) return;
724 Buff
= GuiData
->ActiveBuffer
;
725 if (GetType(Buff
) != TEXTMODE_BUFFER
) return;
727 if (0 != ScrolledLines
)
731 ScrollRect
.right
= Buff
->ViewSize
.X
* GuiData
->CharWidth
;
732 ScrollRect
.bottom
= Region
->Top
* GuiData
->CharHeight
;
734 ScrollWindowEx(GuiData
->hWindow
,
736 -(int)(ScrolledLines
* GuiData
->CharHeight
),
744 DrawRegion(GuiData
, Region
);
746 if (CursorStartX
< Region
->Left
|| Region
->Right
< CursorStartX
747 || CursorStartY
< Region
->Top
|| Region
->Bottom
< CursorStartY
)
749 InvalidateCell(GuiData
, CursorStartX
, CursorStartY
);
752 CursorEndX
= Buff
->CursorPosition
.X
;
753 CursorEndY
= Buff
->CursorPosition
.Y
;
754 if ((CursorEndX
< Region
->Left
|| Region
->Right
< CursorEndX
755 || CursorEndY
< Region
->Top
|| Region
->Bottom
< CursorEndY
)
756 && (CursorEndX
!= CursorStartX
|| CursorEndY
!= CursorStartY
))
758 InvalidateCell(GuiData
, CursorEndX
, CursorEndY
);
762 // Set up the update timer (very short interval) - this is a "hack" for getting the OS to
763 // repaint the window without having it just freeze up and stay on the screen permanently.
764 Buff
->CursorBlinkOn
= TRUE
;
765 SetTimer(GuiData
->hWindow
, CONGUI_UPDATE_TIMER
, CONGUI_UPDATE_TIME
, NULL
);
768 /* static */ VOID NTAPI
769 GuiRingBell(IN OUT PFRONTEND This
)
771 PGUI_CONSOLE_DATA GuiData
= This
->Context
;
773 /* Emit an error beep sound */
774 SendNotifyMessage(GuiData
->hWindow
, PM_CONSOLE_BEEP
, 0, 0);
778 GuiSetCursorInfo(IN OUT PFRONTEND This
,
779 PCONSOLE_SCREEN_BUFFER Buff
)
781 PGUI_CONSOLE_DATA GuiData
= This
->Context
;
783 /* Do nothing if the window is hidden */
784 if (!GuiData
->IsWindowVisible
) return TRUE
;
786 if (GuiData
->ActiveBuffer
== Buff
)
788 InvalidateCell(GuiData
, Buff
->CursorPosition
.X
, Buff
->CursorPosition
.Y
);
795 GuiSetScreenInfo(IN OUT PFRONTEND This
,
796 PCONSOLE_SCREEN_BUFFER Buff
,
800 PGUI_CONSOLE_DATA GuiData
= This
->Context
;
802 /* Do nothing if the window is hidden */
803 if (!GuiData
->IsWindowVisible
) return TRUE
;
805 if (GuiData
->ActiveBuffer
== Buff
)
807 /* Redraw char at old position (remove cursor) */
808 InvalidateCell(GuiData
, OldCursorX
, OldCursorY
);
809 /* Redraw char at new position (show cursor) */
810 InvalidateCell(GuiData
, Buff
->CursorPosition
.X
, Buff
->CursorPosition
.Y
);
817 GuiResizeTerminal(IN OUT PFRONTEND This
)
819 PGUI_CONSOLE_DATA GuiData
= This
->Context
;
821 /* Resize the window to the user's values */
822 PostMessageW(GuiData
->hWindow
, PM_RESIZE_TERMINAL
, 0, 0);
826 GuiSetActiveScreenBuffer(IN OUT PFRONTEND This
)
828 PGUI_CONSOLE_DATA GuiData
= This
->Context
;
829 PCONSOLE_SCREEN_BUFFER ActiveBuffer
;
832 EnterCriticalSection(&GuiData
->Lock
);
833 GuiData
->WindowSizeLock
= TRUE
;
835 InterlockedExchangePointer((PVOID
*)&GuiData
->ActiveBuffer
,
836 ConDrvGetActiveScreenBuffer(GuiData
->Console
));
838 GuiData
->WindowSizeLock
= FALSE
;
839 LeaveCriticalSection(&GuiData
->Lock
);
841 ActiveBuffer
= GuiData
->ActiveBuffer
;
843 /* Change the current palette */
844 if (ActiveBuffer
->PaletteHandle
== NULL
)
846 hPalette
= GuiData
->hSysPalette
;
850 hPalette
= ActiveBuffer
->PaletteHandle
;
853 DPRINT("GuiSetActiveScreenBuffer using palette 0x%p\n", hPalette
);
855 /* Set the new palette for the framebuffer */
856 SelectPalette(GuiData
->hMemDC
, hPalette
, FALSE
);
858 /* Specify the use of the system palette for the framebuffer */
859 SetSystemPaletteUse(GuiData
->hMemDC
, ActiveBuffer
->PaletteUsage
);
861 /* Realize the (logical) palette */
862 RealizePalette(GuiData
->hMemDC
);
864 GuiResizeTerminal(This
);
865 // ConioDrawConsole(Console);
869 GuiReleaseScreenBuffer(IN OUT PFRONTEND This
,
870 IN PCONSOLE_SCREEN_BUFFER ScreenBuffer
)
872 PGUI_CONSOLE_DATA GuiData
= This
->Context
;
875 * If we were notified to release a screen buffer that is not actually
876 * ours, then just ignore the notification...
878 if (ScreenBuffer
!= GuiData
->ActiveBuffer
) return;
881 * ... else, we must release our active buffer. Two cases are present:
882 * - If ScreenBuffer (== GuiData->ActiveBuffer) IS NOT the console
883 * active screen buffer, then we can safely switch to it.
884 * - If ScreenBuffer IS the console active screen buffer, we must release
888 /* Release the old active palette and set the default one */
889 if (GetCurrentObject(GuiData
->hMemDC
, OBJ_PAL
) == ScreenBuffer
->PaletteHandle
)
891 /* Set the new palette */
892 SelectPalette(GuiData
->hMemDC
, GuiData
->hSysPalette
, FALSE
);
895 /* Set the adequate active screen buffer */
896 if (ScreenBuffer
!= GuiData
->Console
->ActiveBuffer
)
898 GuiSetActiveScreenBuffer(This
);
902 EnterCriticalSection(&GuiData
->Lock
);
903 GuiData
->WindowSizeLock
= TRUE
;
905 InterlockedExchangePointer((PVOID
*)&GuiData
->ActiveBuffer
, NULL
);
907 GuiData
->WindowSizeLock
= FALSE
;
908 LeaveCriticalSection(&GuiData
->Lock
);
913 GuiSetMouseCursor(IN OUT PFRONTEND This
,
914 HCURSOR CursorHandle
);
917 GuiRefreshInternalInfo(IN OUT PFRONTEND This
)
919 PGUI_CONSOLE_DATA GuiData
= This
->Context
;
921 /* Update the console leader information held by the window */
922 SetConWndConsoleLeaderCID(GuiData
);
926 * We reset the cursor here so that, when a console app quits, we reset
927 * the cursor to the default one. It's quite a hack since it doesn't proceed
928 * per - console process... This must be fixed.
930 * See GuiInitConsole(...) for more information.
933 /* Mouse is shown by default with its default cursor shape */
934 GuiData
->MouseCursorRefCount
= 0; // Reinitialize the reference counter
935 GuiSetMouseCursor(This
, NULL
);
939 GuiChangeTitle(IN OUT PFRONTEND This
)
941 PGUI_CONSOLE_DATA GuiData
= This
->Context
;
942 // PostMessageW(GuiData->hWindow, PM_CONSOLE_SET_TITLE, 0, 0);
943 SetWindowTextW(GuiData
->hWindow
, GuiData
->Console
->Title
.Buffer
);
947 GuiChangeIcon(IN OUT PFRONTEND This
,
950 PGUI_CONSOLE_DATA GuiData
= This
->Context
;
951 HICON hIcon
, hIconSm
;
953 if (IconHandle
== NULL
)
955 hIcon
= ghDefaultIcon
;
956 hIconSm
= ghDefaultIconSm
;
960 hIcon
= CopyIcon(IconHandle
);
961 hIconSm
= CopyIcon(IconHandle
);
969 if (hIcon
!= GuiData
->hIcon
)
971 if (GuiData
->hIcon
!= NULL
&& GuiData
->hIcon
!= ghDefaultIcon
)
973 DestroyIcon(GuiData
->hIcon
);
975 if (GuiData
->hIconSm
!= NULL
&& GuiData
->hIconSm
!= ghDefaultIconSm
)
977 DestroyIcon(GuiData
->hIconSm
);
980 GuiData
->hIcon
= hIcon
;
981 GuiData
->hIconSm
= hIconSm
;
983 DPRINT("Set icons in GuiChangeIcon\n");
984 PostMessageW(GuiData
->hWindow
, WM_SETICON
, ICON_BIG
, (LPARAM
)GuiData
->hIcon
);
985 PostMessageW(GuiData
->hWindow
, WM_SETICON
, ICON_SMALL
, (LPARAM
)GuiData
->hIconSm
);
992 GuiGetConsoleWindowHandle(IN OUT PFRONTEND This
)
994 PGUI_CONSOLE_DATA GuiData
= This
->Context
;
995 return GuiData
->hWindow
;
999 GuiGetLargestConsoleWindowSize(IN OUT PFRONTEND This
,
1002 PGUI_CONSOLE_DATA GuiData
= This
->Context
;
1003 PCONSOLE_SCREEN_BUFFER ActiveBuffer
;
1006 UINT WidthUnit
, HeightUnit
;
1010 if (!SystemParametersInfoW(SPI_GETWORKAREA
, 0, &WorkArea
, 0))
1012 DPRINT1("SystemParametersInfoW failed - What to do ??\n");
1016 ActiveBuffer
= GuiData
->ActiveBuffer
;
1019 GetScreenBufferSizeUnits(ActiveBuffer
, GuiData
, &WidthUnit
, &HeightUnit
);
1023 /* Default: text mode */
1024 WidthUnit
= GuiData
->CharWidth
;
1025 HeightUnit
= GuiData
->CharHeight
;
1028 width
= WorkArea
.right
;
1029 height
= WorkArea
.bottom
;
1031 width
-= (2 * (GetSystemMetrics(SM_CXFRAME
) + GetSystemMetrics(SM_CXEDGE
)));
1032 height
-= (2 * (GetSystemMetrics(SM_CYFRAME
) + GetSystemMetrics(SM_CYEDGE
)) + GetSystemMetrics(SM_CYCAPTION
));
1034 if (width
< 0) width
= 0;
1035 if (height
< 0) height
= 0;
1037 pSize
->X
= (SHORT
)(width
/ (int)WidthUnit
) /* HACK */ + 2;
1038 pSize
->Y
= (SHORT
)(height
/ (int)HeightUnit
) /* HACK */ + 1;
1042 GuiGetSelectionInfo(IN OUT PFRONTEND This
,
1043 PCONSOLE_SELECTION_INFO pSelectionInfo
)
1045 PGUI_CONSOLE_DATA GuiData
= This
->Context
;
1047 if (pSelectionInfo
== NULL
) return FALSE
;
1049 ZeroMemory(pSelectionInfo
, sizeof(*pSelectionInfo
));
1050 if (GuiData
->Selection
.dwFlags
!= CONSOLE_NO_SELECTION
)
1051 RtlCopyMemory(pSelectionInfo
, &GuiData
->Selection
, sizeof(*pSelectionInfo
));
1057 GuiSetPalette(IN OUT PFRONTEND This
,
1058 HPALETTE PaletteHandle
,
1061 PGUI_CONSOLE_DATA GuiData
= This
->Context
;
1062 HPALETTE OldPalette
;
1064 // if (GetType(GuiData->ActiveBuffer) != GRAPHICS_BUFFER) return FALSE;
1065 if (PaletteHandle
== NULL
) return FALSE
;
1067 /* Set the new palette for the framebuffer */
1068 OldPalette
= SelectPalette(GuiData
->hMemDC
, PaletteHandle
, FALSE
);
1069 if (OldPalette
== NULL
) return FALSE
;
1071 /* Specify the use of the system palette for the framebuffer */
1072 SetSystemPaletteUse(GuiData
->hMemDC
, PaletteUsage
);
1074 /* Realize the (logical) palette */
1075 RealizePalette(GuiData
->hMemDC
);
1077 /* Save the original system palette handle */
1078 if (GuiData
->hSysPalette
== NULL
) GuiData
->hSysPalette
= OldPalette
;
1084 GuiGetDisplayMode(IN OUT PFRONTEND This
)
1086 PGUI_CONSOLE_DATA GuiData
= This
->Context
;
1087 ULONG DisplayMode
= 0;
1089 if (GuiData
->GuiInfo
.FullScreen
)
1090 DisplayMode
|= CONSOLE_FULLSCREEN_HARDWARE
; // CONSOLE_FULLSCREEN
1092 DisplayMode
|= CONSOLE_WINDOWED
;
1098 GuiSetDisplayMode(IN OUT PFRONTEND This
,
1101 PGUI_CONSOLE_DATA GuiData
= This
->Context
;
1104 if (NewMode
& ~(CONSOLE_FULLSCREEN_MODE
| CONSOLE_WINDOWED_MODE
))
1107 /* Do nothing if the window is hidden */
1108 if (!GuiData
->IsWindowVisible
) return TRUE
;
1110 FullScreen
= ((NewMode
& CONSOLE_FULLSCREEN_MODE
) != 0);
1112 if (FullScreen
!= GuiData
->GuiInfo
.FullScreen
)
1114 SwitchFullScreen(GuiData
, FullScreen
);
1121 GuiShowMouseCursor(IN OUT PFRONTEND This
,
1124 PGUI_CONSOLE_DATA GuiData
= This
->Context
;
1126 if (GuiData
->IsWindowVisible
)
1128 /* Set the reference count */
1129 if (Show
) ++GuiData
->MouseCursorRefCount
;
1130 else --GuiData
->MouseCursorRefCount
;
1132 /* Effectively show (or hide) the cursor (use special values for (w|l)Param) */
1133 PostMessageW(GuiData
->hWindow
, WM_SETCURSOR
, -1, -1);
1136 return GuiData
->MouseCursorRefCount
;
1140 GuiSetMouseCursor(IN OUT PFRONTEND This
,
1141 HCURSOR CursorHandle
)
1143 PGUI_CONSOLE_DATA GuiData
= This
->Context
;
1145 /* Do nothing if the window is hidden */
1146 if (!GuiData
->IsWindowVisible
) return TRUE
;
1149 * Set the cursor's handle. If the given handle is NULL,
1150 * then restore the default cursor.
1152 GuiData
->hCursor
= (CursorHandle
? CursorHandle
: ghDefaultCursor
);
1154 /* Effectively modify the shape of the cursor (use special values for (w|l)Param) */
1155 PostMessageW(GuiData
->hWindow
, WM_SETCURSOR
, -1, -1);
1161 GuiMenuControl(IN OUT PFRONTEND This
,
1165 PGUI_CONSOLE_DATA GuiData
= This
->Context
;
1167 GuiData
->CmdIdLow
= CmdIdLow
;
1168 GuiData
->CmdIdHigh
= CmdIdHigh
;
1170 return GetSystemMenu(GuiData
->hWindow
, FALSE
);
1174 GuiSetMenuClose(IN OUT PFRONTEND This
,
1178 * NOTE: See http://www.mail-archive.com/harbour@harbour-project.org/msg27509.html
1179 * or http://harbour-devel.1590103.n2.nabble.com/Question-about-hb-gt-win-CtrlHandler-usage-td4670862i20.html
1180 * for more information.
1183 PGUI_CONSOLE_DATA GuiData
= This
->Context
;
1184 HMENU hSysMenu
= GetSystemMenu(GuiData
->hWindow
, FALSE
);
1186 if (hSysMenu
== NULL
) return FALSE
;
1188 GuiData
->IsCloseButtonEnabled
= Enable
;
1189 EnableMenuItem(hSysMenu
, SC_CLOSE
, MF_BYCOMMAND
| (Enable
? MF_ENABLED
: MF_GRAYED
));
1194 static FRONTEND_VTBL GuiVtbl
=
1204 GuiSetActiveScreenBuffer
,
1205 GuiReleaseScreenBuffer
,
1206 GuiRefreshInternalInfo
,
1209 GuiGetConsoleWindowHandle
,
1210 GuiGetLargestConsoleWindowSize
,
1211 GuiGetSelectionInfo
,
1223 GuiLoadFrontEnd(IN OUT PFRONTEND FrontEnd
,
1224 IN OUT PCONSOLE_STATE_INFO ConsoleInfo
,
1225 IN OUT PCONSOLE_INIT_INFO ConsoleInitInfo
,
1226 IN HANDLE ConsoleLeaderProcessHandle
)
1228 PCONSOLE_START_INFO ConsoleStartInfo
;
1229 PGUI_INIT_INFO GuiInitInfo
;
1231 if (FrontEnd
== NULL
|| ConsoleInfo
== NULL
|| ConsoleInitInfo
== NULL
)
1232 return STATUS_INVALID_PARAMETER
;
1234 ConsoleStartInfo
= ConsoleInitInfo
->ConsoleStartInfo
;
1237 * Initialize a private initialization info structure for later use.
1238 * It must be freed by a call to GuiUnloadFrontEnd or GuiInitFrontEnd.
1240 GuiInitInfo
= ConsoleAllocHeap(HEAP_ZERO_MEMORY
, sizeof(*GuiInitInfo
));
1241 if (GuiInitInfo
== NULL
) return STATUS_NO_MEMORY
;
1243 /* Initialize GUI terminal emulator common functionalities */
1244 if (!GuiInit(ConsoleInitInfo
, ConsoleLeaderProcessHandle
, GuiInitInfo
))
1246 ConsoleFreeHeap(GuiInitInfo
);
1247 return STATUS_UNSUCCESSFUL
;
1251 * Load terminal settings
1254 /* Impersonate the caller in order to retrieve settings in its context */
1255 // if (!CsrImpersonateClient(NULL))
1256 // return STATUS_UNSUCCESSFUL;
1257 CsrImpersonateClient(NULL
);
1259 /* 1. Load the default settings */
1260 GuiConsoleGetDefaultSettings(&GuiInitInfo
->TermInfo
);
1263 GuiInitInfo
->TermInfo
.ShowWindow
= SW_SHOWNORMAL
;
1265 if (ConsoleInitInfo
->IsWindowVisible
)
1267 /* 2. Load the remaining console settings via the registry */
1268 if ((ConsoleStartInfo
->dwStartupFlags
& STARTF_TITLEISLINKNAME
) == 0)
1271 /* Load the terminal infos from the registry */
1272 GuiConsoleReadUserSettings(&GuiInitInfo
->TermInfo
);
1276 * Now, update them with the properties the user might gave to us
1277 * via the STARTUPINFO structure before calling CreateProcess
1278 * (and which was transmitted via the ConsoleStartInfo structure).
1279 * We therefore overwrite the values read in the registry.
1281 if (ConsoleStartInfo
->dwStartupFlags
& STARTF_USESHOWWINDOW
)
1283 GuiInitInfo
->TermInfo
.ShowWindow
= ConsoleStartInfo
->wShowWindow
;
1285 if (ConsoleStartInfo
->dwStartupFlags
& STARTF_USEPOSITION
)
1287 ConsoleInfo
->AutoPosition
= FALSE
;
1288 ConsoleInfo
->WindowPosition
.x
= ConsoleStartInfo
->dwWindowOrigin
.X
;
1289 ConsoleInfo
->WindowPosition
.y
= ConsoleStartInfo
->dwWindowOrigin
.Y
;
1291 if (ConsoleStartInfo
->dwStartupFlags
& STARTF_RUNFULLSCREEN
)
1293 ConsoleInfo
->FullScreen
= TRUE
;
1299 /* Revert impersonation */
1304 wcsncpy(GuiInitInfo
->TermInfo
.FaceName
, ConsoleInfo
->FaceName
, LF_FACESIZE
);
1305 GuiInitInfo
->TermInfo
.FaceName
[LF_FACESIZE
- 1] = UNICODE_NULL
;
1306 GuiInitInfo
->TermInfo
.FontFamily
= ConsoleInfo
->FontFamily
;
1307 GuiInitInfo
->TermInfo
.FontSize
= ConsoleInfo
->FontSize
;
1308 GuiInitInfo
->TermInfo
.FontWeight
= ConsoleInfo
->FontWeight
;
1311 GuiInitInfo
->TermInfo
.FullScreen
= ConsoleInfo
->FullScreen
;
1312 // GuiInitInfo->TermInfo.ShowWindow;
1313 GuiInitInfo
->TermInfo
.AutoPosition
= ConsoleInfo
->AutoPosition
;
1314 GuiInitInfo
->TermInfo
.WindowOrigin
= ConsoleInfo
->WindowPosition
;
1316 /* Initialize the icon handles */
1317 // if (ConsoleStartInfo->hIcon != NULL)
1318 GuiInitInfo
->hIcon
= ConsoleStartInfo
->hIcon
;
1320 // GuiInitInfo->hIcon = ghDefaultIcon;
1322 // if (ConsoleStartInfo->hIconSm != NULL)
1323 GuiInitInfo
->hIconSm
= ConsoleStartInfo
->hIconSm
;
1325 // GuiInitInfo->hIconSm = ghDefaultIconSm;
1327 // ASSERT(GuiInitInfo->hIcon && GuiInitInfo->hIconSm);
1329 GuiInitInfo
->IsWindowVisible
= ConsoleInitInfo
->IsWindowVisible
;
1331 /* Finally, initialize the frontend structure */
1332 FrontEnd
->Vtbl
= &GuiVtbl
;
1333 FrontEnd
->Context
= NULL
;
1334 FrontEnd
->Context2
= GuiInitInfo
;
1336 return STATUS_SUCCESS
;
1340 GuiUnloadFrontEnd(IN OUT PFRONTEND FrontEnd
)
1342 if (FrontEnd
== NULL
) return STATUS_INVALID_PARAMETER
;
1344 if (FrontEnd
->Context
) GuiDeinitFrontEnd(FrontEnd
);
1345 if (FrontEnd
->Context2
) ConsoleFreeHeap(FrontEnd
->Context2
);
1347 return STATUS_SUCCESS
;