2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Console Server DLL
4 * FILE: win32ss/user/consrv/frontends/tui/tuiterm.c
5 * PURPOSE: TUI Terminal Front-End - Virtual Consoles...
6 * PROGRAMMERS: David Welch
9 * Hermes Belusca-Maito (hermes.belusca@sfr.fr)
13 #include "include/conio.h"
14 #include "include/console.h"
15 #include "include/settings.h"
17 #include <drivers/blue/ntddblue.h>
23 /* GLOBALS ********************************************************************/
25 #define GetNextConsole(Console) \
26 CONTAINING_RECORD(Console->Entry.Flink, TUI_CONSOLE_DATA, Entry)
28 #define GetPrevConsole(Console) \
29 CONTAINING_RECORD(Console->Entry.Blink, TUI_CONSOLE_DATA, Entry)
32 /* TUI Console Window Class name */
33 #define TUI_CONSOLE_WINDOW_CLASS L"TuiConsoleWindowClass"
35 typedef struct _TUI_CONSOLE_DATA
37 CRITICAL_SECTION Lock
;
38 LIST_ENTRY Entry
; /* Entry in the list of virtual consoles */
39 // HANDLE hTuiInitEvent;
41 HWND hWindow
; /* Handle to the console's window (used for the window's procedure */
43 PCONSOLE Console
; /* Pointer to the owned console */
44 // TUI_CONSOLE_INFO TuiInfo; /* TUI terminal settings */
45 } TUI_CONSOLE_DATA
, *PTUI_CONSOLE_DATA
;
47 /* List of the maintained virtual consoles and its lock */
48 static LIST_ENTRY VirtConsList
;
49 static PTUI_CONSOLE_DATA ActiveConsole
; /* The active console on screen */
50 static CRITICAL_SECTION ActiveVirtConsLock
;
52 static COORD PhysicalConsoleSize
;
53 static HANDLE ConsoleDeviceHandle
;
55 static BOOL ConsInitialized
= FALSE
;
57 /******************************************************************************\
58 |** BlueScreen Driver management **|
60 /* Code taken and adapted from base/system/services/driver.c */
62 ScmLoadDriver(LPCWSTR lpServiceName
)
64 NTSTATUS Status
= STATUS_SUCCESS
;
65 BOOLEAN WasPrivilegeEnabled
= FALSE
;
67 UNICODE_STRING DriverPath
;
69 /* Build the driver path */
70 /* 52 = wcslen(L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\") */
71 pszDriverPath
= ConsoleAllocHeap(HEAP_ZERO_MEMORY
,
72 (52 + wcslen(lpServiceName
) + 1) * sizeof(WCHAR
));
73 if (pszDriverPath
== NULL
)
74 return ERROR_NOT_ENOUGH_MEMORY
;
77 L
"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\");
81 RtlInitUnicodeString(&DriverPath
,
84 DPRINT(" Path: %wZ\n", &DriverPath
);
86 /* Acquire driver-loading privilege */
87 Status
= RtlAdjustPrivilege(SE_LOAD_DRIVER_PRIVILEGE
,
90 &WasPrivilegeEnabled
);
91 if (!NT_SUCCESS(Status
))
93 /* We encountered a failure, exit properly */
94 DPRINT1("CONSRV: Cannot acquire driver-loading privilege, Status = 0x%08lx\n", Status
);
98 Status
= NtLoadDriver(&DriverPath
);
100 /* Release driver-loading privilege */
101 RtlAdjustPrivilege(SE_LOAD_DRIVER_PRIVILEGE
,
104 &WasPrivilegeEnabled
);
107 ConsoleFreeHeap(pszDriverPath
);
108 return RtlNtStatusToDosError(Status
);
111 #ifdef BLUESCREEN_DRIVER_UNLOADING
113 ScmUnloadDriver(LPCWSTR lpServiceName
)
115 NTSTATUS Status
= STATUS_SUCCESS
;
116 BOOLEAN WasPrivilegeEnabled
= FALSE
;
118 UNICODE_STRING DriverPath
;
120 /* Build the driver path */
121 /* 52 = wcslen(L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\") */
122 pszDriverPath
= ConsoleAllocHeap(HEAP_ZERO_MEMORY
,
123 (52 + wcslen(lpServiceName
) + 1) * sizeof(WCHAR
));
124 if (pszDriverPath
== NULL
)
125 return ERROR_NOT_ENOUGH_MEMORY
;
127 wcscpy(pszDriverPath
,
128 L
"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\");
129 wcscat(pszDriverPath
,
132 RtlInitUnicodeString(&DriverPath
,
135 DPRINT(" Path: %wZ\n", &DriverPath
);
137 /* Acquire driver-unloading privilege */
138 Status
= RtlAdjustPrivilege(SE_LOAD_DRIVER_PRIVILEGE
,
141 &WasPrivilegeEnabled
);
142 if (!NT_SUCCESS(Status
))
144 /* We encountered a failure, exit properly */
145 DPRINT1("CONSRV: Cannot acquire driver-unloading privilege, Status = 0x%08lx\n", Status
);
149 Status
= NtUnloadDriver(&DriverPath
);
151 /* Release driver-unloading privilege */
152 RtlAdjustPrivilege(SE_LOAD_DRIVER_PRIVILEGE
,
155 &WasPrivilegeEnabled
);
158 ConsoleFreeHeap(pszDriverPath
);
159 return RtlNtStatusToDosError(Status
);
163 \******************************************************************************/
166 TuiSwapConsole(INT Next
)
168 static PTUI_CONSOLE_DATA SwapConsole
= NULL
; /* Console we are thinking about swapping with */
177 * Alt-Tab, swap consoles.
178 * move SwapConsole to next console, and print its title.
180 EnterCriticalSection(&ActiveVirtConsLock
);
181 if (!SwapConsole
) SwapConsole
= ActiveConsole
;
183 SwapConsole
= (0 < Next
? GetNextConsole(SwapConsole
) : GetPrevConsole(SwapConsole
));
184 Title
.MaximumLength
= RtlUnicodeStringToAnsiSize(&SwapConsole
->Console
->Title
);
186 Buffer
= ConsoleAllocHeap(0, sizeof(COORD
) + Title
.MaximumLength
);
187 pos
= (PCOORD
)Buffer
;
188 Title
.Buffer
= (PVOID
)((ULONG_PTR
)Buffer
+ sizeof(COORD
));
190 RtlUnicodeStringToAnsiString(&Title
, &SwapConsole
->Console
->Title
, FALSE
);
191 pos
->X
= (PhysicalConsoleSize
.X
- Title
.Length
) / 2;
192 pos
->Y
= PhysicalConsoleSize
.Y
/ 2;
193 /* Redraw the console to clear off old title */
194 ConioDrawConsole(ActiveConsole
->Console
);
195 if (!DeviceIoControl(ConsoleDeviceHandle
, IOCTL_CONSOLE_WRITE_OUTPUT_CHARACTER
,
196 NULL
, 0, Buffer
, sizeof(COORD
) + Title
.Length
,
197 &BytesReturned
, NULL
))
199 DPRINT1( "Error writing to console\n" );
201 ConsoleFreeHeap(Buffer
);
202 LeaveCriticalSection(&ActiveVirtConsLock
);
206 else if (NULL
!= SwapConsole
)
208 EnterCriticalSection(&ActiveVirtConsLock
);
209 if (SwapConsole
!= ActiveConsole
)
211 /* First remove swapconsole from the list */
212 SwapConsole
->Entry
.Blink
->Flink
= SwapConsole
->Entry
.Flink
;
213 SwapConsole
->Entry
.Flink
->Blink
= SwapConsole
->Entry
.Blink
;
214 /* Now insert before activeconsole */
215 SwapConsole
->Entry
.Flink
= &ActiveConsole
->Entry
;
216 SwapConsole
->Entry
.Blink
= ActiveConsole
->Entry
.Blink
;
217 ActiveConsole
->Entry
.Blink
->Flink
= &SwapConsole
->Entry
;
218 ActiveConsole
->Entry
.Blink
= &SwapConsole
->Entry
;
220 ActiveConsole
= SwapConsole
;
222 ConioDrawConsole(ActiveConsole
->Console
);
223 LeaveCriticalSection(&ActiveVirtConsLock
);
233 TuiCopyRect(char *Dest
, PTEXTMODE_SCREEN_BUFFER Buff
, SMALL_RECT
* Region
)
235 UINT SrcDelta
, DestDelta
;
239 Src
= ConioCoordToPointer(Buff
, Region
->Left
, Region
->Top
);
240 SrcDelta
= Buff
->ScreenBufferSize
.X
* 2;
241 SrcEnd
= Buff
->Buffer
+ Buff
->ScreenBufferSize
.Y
* Buff
->ScreenBufferSize
.X
* 2;
242 DestDelta
= ConioRectWidth(Region
) * 2;
243 for (i
= Region
->Top
; i
<= Region
->Bottom
; i
++)
245 memcpy(Dest
, Src
, DestDelta
);
249 Src
-= Buff
->ScreenBufferSize
.Y
* Buff
->ScreenBufferSize
.X
* 2;
255 static LRESULT CALLBACK
256 TuiConsoleWndProc(HWND hWnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
259 PTUI_CONSOLE_DATA TuiData = NULL;
260 PCONSOLE Console = NULL;
262 TuiData = TuiGetGuiData(hWnd);
263 if (TuiData == NULL) return 0;
275 if (ConSrvValidateConsoleUnsafe(ActiveConsole
->Console
, CONSOLE_RUNNING
, TRUE
))
279 Message
.message
= msg
;
280 Message
.wParam
= wParam
;
281 Message
.lParam
= lParam
;
283 ConioProcessKey(ActiveConsole
->Console
, &Message
);
284 LeaveCriticalSection(&ActiveConsole
->Console
->Lock
);
291 if (ConSrvValidateConsoleUnsafe(ActiveConsole
->Console
, CONSOLE_RUNNING
, TRUE
))
293 if (LOWORD(wParam
) != WA_INACTIVE
)
296 ConioDrawConsole(ActiveConsole
->Console
);
298 LeaveCriticalSection(&ActiveConsole
->Console
->Lock
);
307 return DefWindowProcW(hWnd
, msg
, wParam
, lParam
);
311 TuiConsoleThread(PVOID Data
)
313 PTUI_CONSOLE_DATA TuiData
= (PTUI_CONSOLE_DATA
)Data
;
314 PCONSOLE Console
= TuiData
->Console
;
318 NewWindow
= CreateWindowW(TUI_CONSOLE_WINDOW_CLASS
,
319 Console
->Title
.Buffer
,
321 -32000, -32000, 0, 0,
325 if (NULL
== NewWindow
)
327 DPRINT1("CONSRV: Unable to create console window\n");
330 TuiData
->hWindow
= NewWindow
;
332 SetForegroundWindow(TuiData
->hWindow
);
333 NtUserConsoleControl(ConsoleAcquireDisplayOwnership
, NULL
, 0);
335 while (GetMessageW(&msg
, NULL
, 0, 0))
337 TranslateMessage(&msg
);
338 DispatchMessageW(&msg
);
348 CONSOLE_SCREEN_BUFFER_INFO ScrInfo
;
351 ATOM ConsoleClassAtom
;
352 USHORT TextAttribute
= FOREGROUND_BLUE
| FOREGROUND_GREEN
| FOREGROUND_RED
;
354 /* Exit if we were already initialized */
355 if (ConsInitialized
) return TRUE
;
358 * Initialize the TUI front-end:
359 * - load the console driver,
360 * - set default screen attributes,
361 * - grab the console size.
363 ScmLoadDriver(L
"Blue");
365 ConsoleDeviceHandle
= CreateFileW(L
"\\\\.\\BlueScreen",
370 if (INVALID_HANDLE_VALUE
== ConsoleDeviceHandle
)
372 DPRINT1("Failed to open BlueScreen.\n");
376 if (!DeviceIoControl(ConsoleDeviceHandle
, IOCTL_CONSOLE_LOADFONT
,
377 &OemCP
, sizeof(OemCP
), NULL
, 0,
378 &BytesReturned
, NULL
))
380 DPRINT1("Failed to load the font for codepage %d\n", OemCP
);
381 /* Let's suppose the font is good enough to continue */
384 if (!DeviceIoControl(ConsoleDeviceHandle
, IOCTL_CONSOLE_SET_TEXT_ATTRIBUTE
,
385 &TextAttribute
, sizeof(TextAttribute
), NULL
, 0,
386 &BytesReturned
, NULL
))
388 DPRINT1("Failed to set text attribute\n");
391 ActiveConsole
= NULL
;
392 InitializeListHead(&VirtConsList
);
393 InitializeCriticalSection(&ActiveVirtConsLock
);
395 if (!DeviceIoControl(ConsoleDeviceHandle
, IOCTL_CONSOLE_GET_SCREEN_BUFFER_INFO
,
396 NULL
, 0, &ScrInfo
, sizeof(ScrInfo
), &BytesReturned
, NULL
))
398 DPRINT1("Failed to get console info\n");
402 PhysicalConsoleSize
= ScrInfo
.dwSize
;
404 /* Register the TUI notification window class */
405 RtlZeroMemory(&wc
, sizeof(WNDCLASSEXW
));
406 wc
.cbSize
= sizeof(WNDCLASSEXW
);
407 wc
.lpszClassName
= TUI_CONSOLE_WINDOW_CLASS
;
408 wc
.lpfnWndProc
= TuiConsoleWndProc
;
410 wc
.hInstance
= ConSrvDllInstance
;
412 ConsoleClassAtom
= RegisterClassExW(&wc
);
413 if (ConsoleClassAtom
== 0)
415 DPRINT1("Failed to register TUI console wndproc\n");
426 DeleteCriticalSection(&ActiveVirtConsLock
);
427 CloseHandle(ConsoleDeviceHandle
);
430 ConsInitialized
= Ret
;
436 /******************************************************************************
437 * TUI Console Driver *
438 ******************************************************************************/
441 TuiCleanupConsole(PCONSOLE Console
)
443 PTUI_CONSOLE_DATA TuiData
= Console
->TermIFace
.Data
;
445 /* Close the notification window */
446 DestroyWindow(TuiData
->hWindow
);
449 * Set the active console to the next one
450 * and remove the console from the list.
452 EnterCriticalSection(&ActiveVirtConsLock
);
453 ActiveConsole
= GetNextConsole(TuiData
);
454 RemoveEntryList(&TuiData
->Entry
);
456 // /* Switch to next console */
457 // if (ActiveConsole == TuiData)
458 // if (ActiveConsole->Console == Console)
460 // ActiveConsole = (TuiData->Entry.Flink != TuiData->Entry ? GetNextConsole(TuiData) : NULL);
463 // if (GetNextConsole(TuiData) != TuiData)
465 // TuiData->Entry.Blink->Flink = TuiData->Entry.Flink;
466 // TuiData->Entry.Flink->Blink = TuiData->Entry.Blink;
469 LeaveCriticalSection(&ActiveVirtConsLock
);
471 /* Switch to the next console */
472 if (NULL
!= ActiveConsole
) ConioDrawConsole(ActiveConsole
->Console
);
474 Console
->TermIFace
.Data
= NULL
;
475 DeleteCriticalSection(&TuiData
->Lock
);
476 ConsoleFreeHeap(TuiData
);
480 TuiDrawRegion(PCONSOLE Console
, SMALL_RECT
* Region
)
483 PCONSOLE_SCREEN_BUFFER Buff
= Console
->ActiveBuffer
;
484 PCONSOLE_DRAW ConsoleDraw
;
485 UINT ConsoleDrawSize
;
487 if (ActiveConsole
->Console
!= Console
|| GetType(Buff
) != TEXTMODE_BUFFER
) return;
489 ConsoleDrawSize
= sizeof(CONSOLE_DRAW
) +
490 (ConioRectWidth(Region
) * ConioRectHeight(Region
)) * 2;
491 ConsoleDraw
= ConsoleAllocHeap(0, ConsoleDrawSize
);
492 if (NULL
== ConsoleDraw
)
494 DPRINT1("ConsoleAllocHeap failed\n");
497 ConsoleDraw
->X
= Region
->Left
;
498 ConsoleDraw
->Y
= Region
->Top
;
499 ConsoleDraw
->SizeX
= ConioRectWidth(Region
);
500 ConsoleDraw
->SizeY
= ConioRectHeight(Region
);
501 ConsoleDraw
->CursorX
= Buff
->CursorPosition
.X
;
502 ConsoleDraw
->CursorY
= Buff
->CursorPosition
.Y
;
504 TuiCopyRect((char*)(ConsoleDraw
+ 1), (PTEXTMODE_SCREEN_BUFFER
)Buff
, Region
);
506 if (!DeviceIoControl(ConsoleDeviceHandle
, IOCTL_CONSOLE_DRAW
,
507 NULL
, 0, ConsoleDraw
, ConsoleDrawSize
, &BytesReturned
, NULL
))
509 DPRINT1("Failed to draw console\n");
510 ConsoleFreeHeap(ConsoleDraw
);
514 ConsoleFreeHeap(ConsoleDraw
);
518 TuiWriteStream(PCONSOLE Console
, SMALL_RECT
* Region
, SHORT CursorStartX
, SHORT CursorStartY
,
519 UINT ScrolledLines
, CHAR
*Buffer
, UINT Length
)
522 PCONSOLE_SCREEN_BUFFER Buff
= Console
->ActiveBuffer
;
524 if (ActiveConsole
->Console
->ActiveBuffer
!= Buff
) return;
526 if (!WriteFile(ConsoleDeviceHandle
, Buffer
, Length
, &BytesWritten
, NULL
))
528 DPRINT1("Error writing to BlueScreen\n");
533 TuiSetCursorInfo(PCONSOLE Console
, PCONSOLE_SCREEN_BUFFER Buff
)
535 CONSOLE_CURSOR_INFO Info
;
538 if (ActiveConsole
->Console
->ActiveBuffer
!= Buff
) return TRUE
;
540 Info
.dwSize
= ConioEffectiveCursorSize(Console
, 100);
541 Info
.bVisible
= Buff
->CursorInfo
.bVisible
;
543 if (!DeviceIoControl(ConsoleDeviceHandle
, IOCTL_CONSOLE_SET_CURSOR_INFO
,
544 &Info
, sizeof(Info
), NULL
, 0, &BytesReturned
, NULL
))
546 DPRINT1( "Failed to set cursor info\n" );
554 TuiSetScreenInfo(PCONSOLE Console
, PCONSOLE_SCREEN_BUFFER Buff
, SHORT OldCursorX
, SHORT OldCursorY
)
556 CONSOLE_SCREEN_BUFFER_INFO Info
;
559 if (ActiveConsole
->Console
->ActiveBuffer
!= Buff
) return TRUE
;
560 if (GetType(Buff
) != TEXTMODE_BUFFER
) return FALSE
;
562 Info
.dwCursorPosition
= Buff
->CursorPosition
;
563 Info
.wAttributes
= ((PTEXTMODE_SCREEN_BUFFER
)Buff
)->ScreenDefaultAttrib
;
565 if (!DeviceIoControl(ConsoleDeviceHandle
, IOCTL_CONSOLE_SET_SCREEN_BUFFER_INFO
,
566 &Info
, sizeof(CONSOLE_SCREEN_BUFFER_INFO
), NULL
, 0,
567 &BytesReturned
, NULL
))
569 DPRINT1( "Failed to set cursor position\n" );
577 TuiResizeTerminal(PCONSOLE Console
)
582 TuiProcessKeyCallback(PCONSOLE Console
, MSG
* msg
, BYTE KeyStateMenu
, DWORD ShiftState
, UINT VirtualKeyCode
, BOOL Down
)
584 if (0 != (ShiftState
& (RIGHT_ALT_PRESSED
| LEFT_ALT_PRESSED
)) &&
585 VK_TAB
== VirtualKeyCode
)
589 TuiSwapConsole(ShiftState
& SHIFT_PRESSED
? -1 : 1);
594 else if (VK_MENU
== VirtualKeyCode
&& !Down
)
596 return TuiSwapConsole(0);
603 TuiRefreshInternalInfo(PCONSOLE Console
)
608 TuiChangeTitle(PCONSOLE Console
)
613 TuiChangeIcon(PCONSOLE Console
, HICON hWindowIcon
)
619 TuiGetConsoleWindowHandle(PCONSOLE Console
)
621 PTUI_CONSOLE_DATA TuiData
= Console
->TermIFace
.Data
;
622 return TuiData
->hWindow
;
626 TuiGetLargestConsoleWindowSize(PCONSOLE Console
, PCOORD pSize
)
629 *pSize
= PhysicalConsoleSize
;
633 TuiGetDisplayMode(PCONSOLE Console
)
635 return CONSOLE_FULLSCREEN_HARDWARE
; // CONSOLE_FULLSCREEN;
639 TuiSetDisplayMode(PCONSOLE Console
, ULONG NewMode
)
641 // if (NewMode & ~(CONSOLE_FULLSCREEN_MODE | CONSOLE_WINDOWED_MODE))
647 TuiShowMouseCursor(PCONSOLE Console
, BOOL Show
)
653 TuiSetMouseCursor(PCONSOLE Console
, HCURSOR hCursor
)
659 TuiMenuControl(PCONSOLE Console
, UINT cmdIdLow
, UINT cmdIdHigh
)
665 TuiSetMenuClose(PCONSOLE Console
, BOOL Enable
)
670 static FRONTEND_VTBL TuiVtbl
=
678 TuiProcessKeyCallback
,
679 TuiRefreshInternalInfo
,
682 TuiGetConsoleWindowHandle
,
683 TuiGetLargestConsoleWindowSize
,
693 TuiInitConsole(PCONSOLE Console
,
694 /*IN*/ PCONSOLE_START_INFO ConsoleStartInfo
,
695 PCONSOLE_INFO ConsoleInfo
,
698 PTUI_CONSOLE_DATA TuiData
;
701 if (Console
== NULL
|| ConsoleInfo
== NULL
)
702 return STATUS_INVALID_PARAMETER
;
704 if (GetType(Console
->ActiveBuffer
) != TEXTMODE_BUFFER
)
705 return STATUS_INVALID_PARAMETER
;
707 /* Initialize the TUI terminal emulator */
708 if (!TuiInit(Console
->CodePage
)) return STATUS_UNSUCCESSFUL
;
710 /* Initialize the console */
711 Console
->TermIFace
.Vtbl
= &TuiVtbl
;
713 TuiData
= ConsoleAllocHeap(HEAP_ZERO_MEMORY
, sizeof(TUI_CONSOLE_DATA
));
716 DPRINT1("CONSRV: Failed to create TUI_CONSOLE_DATA\n");
717 return STATUS_UNSUCCESSFUL
;
719 Console
->TermIFace
.Data
= (PVOID
)TuiData
;
720 TuiData
->Console
= Console
;
721 TuiData
->hWindow
= NULL
;
723 InitializeCriticalSection(&TuiData
->Lock
);
726 * HACK: Resize the console since we don't support for now changing
727 * the console size when we display it with the hardware.
729 Console
->ConsoleSize
= PhysicalConsoleSize
;
730 ConioResizeBuffer(Console
, (PTEXTMODE_SCREEN_BUFFER
)(Console
->ActiveBuffer
), PhysicalConsoleSize
);
732 /* The console cannot be resized anymore */
733 Console
->FixedSize
= TRUE
; // MUST be placed AFTER the call to ConioResizeBuffer !!
734 // ConioResizeTerminal(Console);
737 * Contrary to what we do in the GUI front-end, here we create
738 * an input thread for each console. It will dispatch all the
739 * input messages to the proper console (on the GUI it is done
740 * via the default GUI dispatch thread).
742 ThreadHandle
= CreateThread(NULL
,
748 if (NULL
== ThreadHandle
)
750 DPRINT1("CONSRV: Unable to create console thread\n");
751 TuiCleanupConsole(Console
);
752 return STATUS_UNSUCCESSFUL
;
754 CloseHandle(ThreadHandle
);
757 * Insert the newly created console in the list of virtual consoles
758 * and activate it (give it the focus).
760 EnterCriticalSection(&ActiveVirtConsLock
);
761 InsertTailList(&VirtConsList
, &TuiData
->Entry
);
762 ActiveConsole
= TuiData
;
763 LeaveCriticalSection(&ActiveVirtConsLock
);
765 return STATUS_SUCCESS
;