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
= RtlAllocateHeap(ConSrvHeap
,
73 (52 + wcslen(lpServiceName
) + 1) * sizeof(WCHAR
));
74 if (pszDriverPath
== NULL
)
75 return ERROR_NOT_ENOUGH_MEMORY
;
78 L
"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\");
82 RtlInitUnicodeString(&DriverPath
,
85 DPRINT(" Path: %wZ\n", &DriverPath
);
87 /* Acquire driver-loading privilege */
88 Status
= RtlAdjustPrivilege(SE_LOAD_DRIVER_PRIVILEGE
,
91 &WasPrivilegeEnabled
);
92 if (!NT_SUCCESS(Status
))
94 /* We encountered a failure, exit properly */
95 DPRINT1("CONSRV: Cannot acquire driver-loading privilege, Status = 0x%08lx\n", Status
);
99 Status
= NtLoadDriver(&DriverPath
);
101 /* Release driver-loading privilege */
102 RtlAdjustPrivilege(SE_LOAD_DRIVER_PRIVILEGE
,
105 &WasPrivilegeEnabled
);
108 RtlFreeHeap(ConSrvHeap
, 0, pszDriverPath
);
109 return RtlNtStatusToDosError(Status
);
112 #ifdef BLUESCREEN_DRIVER_UNLOADING
114 ScmUnloadDriver(LPCWSTR lpServiceName
)
116 NTSTATUS Status
= STATUS_SUCCESS
;
117 BOOLEAN WasPrivilegeEnabled
= FALSE
;
119 UNICODE_STRING DriverPath
;
121 /* Build the driver path */
122 /* 52 = wcslen(L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\") */
123 pszDriverPath
= RtlAllocateHeap(ConSrvHeap
,
125 (52 + wcslen(lpServiceName
) + 1) * sizeof(WCHAR
));
126 if (pszDriverPath
== NULL
)
127 return ERROR_NOT_ENOUGH_MEMORY
;
129 wcscpy(pszDriverPath
,
130 L
"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\");
131 wcscat(pszDriverPath
,
134 RtlInitUnicodeString(&DriverPath
,
137 DPRINT(" Path: %wZ\n", &DriverPath
);
139 /* Acquire driver-unloading privilege */
140 Status
= RtlAdjustPrivilege(SE_LOAD_DRIVER_PRIVILEGE
,
143 &WasPrivilegeEnabled
);
144 if (!NT_SUCCESS(Status
))
146 /* We encountered a failure, exit properly */
147 DPRINT1("CONSRV: Cannot acquire driver-unloading privilege, Status = 0x%08lx\n", Status
);
151 Status
= NtUnloadDriver(&DriverPath
);
153 /* Release driver-unloading privilege */
154 RtlAdjustPrivilege(SE_LOAD_DRIVER_PRIVILEGE
,
157 &WasPrivilegeEnabled
);
160 RtlFreeHeap(ConSrvHeap
, 0, pszDriverPath
);
161 return RtlNtStatusToDosError(Status
);
165 \******************************************************************************/
168 TuiSwapConsole(INT Next
)
170 static PTUI_CONSOLE_DATA SwapConsole
= NULL
; /* Console we are thinking about swapping with */
179 * Alt-Tab, swap consoles.
180 * move SwapConsole to next console, and print its title.
182 EnterCriticalSection(&ActiveVirtConsLock
);
183 if (!SwapConsole
) SwapConsole
= ActiveConsole
;
185 SwapConsole
= (0 < Next
? GetNextConsole(SwapConsole
) : GetPrevConsole(SwapConsole
));
186 Title
.MaximumLength
= RtlUnicodeStringToAnsiSize(&SwapConsole
->Console
->Title
);
188 Buffer
= RtlAllocateHeap(ConSrvHeap
, 0,
189 sizeof(COORD
) + Title
.MaximumLength
);
190 pos
= (PCOORD
)Buffer
;
191 Title
.Buffer
= (PVOID
)((ULONG_PTR
)Buffer
+ sizeof(COORD
));
193 RtlUnicodeStringToAnsiString(&Title
, &SwapConsole
->Console
->Title
, FALSE
);
194 pos
->X
= (PhysicalConsoleSize
.X
- Title
.Length
) / 2;
195 pos
->Y
= PhysicalConsoleSize
.Y
/ 2;
196 /* Redraw the console to clear off old title */
197 ConioDrawConsole(ActiveConsole
->Console
);
198 if (!DeviceIoControl(ConsoleDeviceHandle
, IOCTL_CONSOLE_WRITE_OUTPUT_CHARACTER
,
199 NULL
, 0, Buffer
, sizeof(COORD
) + Title
.Length
,
200 &BytesReturned
, NULL
))
202 DPRINT1( "Error writing to console\n" );
204 RtlFreeHeap(ConSrvHeap
, 0, Buffer
);
205 LeaveCriticalSection(&ActiveVirtConsLock
);
209 else if (NULL
!= SwapConsole
)
211 EnterCriticalSection(&ActiveVirtConsLock
);
212 if (SwapConsole
!= ActiveConsole
)
214 /* First remove swapconsole from the list */
215 SwapConsole
->Entry
.Blink
->Flink
= SwapConsole
->Entry
.Flink
;
216 SwapConsole
->Entry
.Flink
->Blink
= SwapConsole
->Entry
.Blink
;
217 /* Now insert before activeconsole */
218 SwapConsole
->Entry
.Flink
= &ActiveConsole
->Entry
;
219 SwapConsole
->Entry
.Blink
= ActiveConsole
->Entry
.Blink
;
220 ActiveConsole
->Entry
.Blink
->Flink
= &SwapConsole
->Entry
;
221 ActiveConsole
->Entry
.Blink
= &SwapConsole
->Entry
;
223 ActiveConsole
= SwapConsole
;
225 ConioDrawConsole(ActiveConsole
->Console
);
226 LeaveCriticalSection(&ActiveVirtConsLock
);
236 TuiCopyRect(char *Dest
, PCONSOLE_SCREEN_BUFFER Buff
, SMALL_RECT
* Region
)
238 UINT SrcDelta
, DestDelta
;
242 Src
= ConioCoordToPointer(Buff
, Region
->Left
, Region
->Top
);
243 SrcDelta
= Buff
->ScreenBufferSize
.X
* 2;
244 SrcEnd
= Buff
->Buffer
+ Buff
->ScreenBufferSize
.Y
* Buff
->ScreenBufferSize
.X
* 2;
245 DestDelta
= ConioRectWidth(Region
) * 2;
246 for (i
= Region
->Top
; i
<= Region
->Bottom
; i
++)
248 memcpy(Dest
, Src
, DestDelta
);
252 Src
-= Buff
->ScreenBufferSize
.Y
* Buff
->ScreenBufferSize
.X
* 2;
258 static LRESULT CALLBACK
259 TuiConsoleWndProc(HWND hWnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
262 PTUI_CONSOLE_DATA TuiData = NULL;
263 PCONSOLE Console = NULL;
265 TuiData = TuiGetGuiData(hWnd);
266 if (TuiData == NULL) return 0;
278 if (ConSrvValidateConsoleUnsafe(ActiveConsole
->Console
, CONSOLE_RUNNING
, TRUE
))
282 Message
.message
= msg
;
283 Message
.wParam
= wParam
;
284 Message
.lParam
= lParam
;
286 ConioProcessKey(ActiveConsole
->Console
, &Message
);
287 LeaveCriticalSection(&ActiveConsole
->Console
->Lock
);
294 if (ConSrvValidateConsoleUnsafe(ActiveConsole
->Console
, CONSOLE_RUNNING
, TRUE
))
296 if (LOWORD(wParam
) != WA_INACTIVE
)
299 ConioDrawConsole(ActiveConsole
->Console
);
301 LeaveCriticalSection(&ActiveConsole
->Console
->Lock
);
310 return DefWindowProcW(hWnd
, msg
, wParam
, lParam
);
314 TuiConsoleThread(PVOID Data
)
316 PTUI_CONSOLE_DATA TuiData
= (PTUI_CONSOLE_DATA
)Data
;
317 PCONSOLE Console
= TuiData
->Console
;
321 NewWindow
= CreateWindowW(TUI_CONSOLE_WINDOW_CLASS
,
322 Console
->Title
.Buffer
,
324 -32000, -32000, 0, 0,
328 if (NULL
== NewWindow
)
330 DPRINT1("CONSRV: Unable to create console window\n");
333 TuiData
->hWindow
= NewWindow
;
335 SetForegroundWindow(TuiData
->hWindow
);
336 NtUserConsoleControl(ConsoleAcquireDisplayOwnership
, NULL
, 0);
338 while (GetMessageW(&msg
, NULL
, 0, 0))
340 TranslateMessage(&msg
);
341 DispatchMessageW(&msg
);
351 CONSOLE_SCREEN_BUFFER_INFO ScrInfo
;
354 ATOM ConsoleClassAtom
;
355 USHORT TextAttribute
= FOREGROUND_BLUE
| FOREGROUND_GREEN
| FOREGROUND_RED
;
357 /* Exit if we were already initialized */
358 if (ConsInitialized
) return TRUE
;
361 * Initialize the TUI front-end:
362 * - load the console driver,
363 * - set default screen attributes,
364 * - grab the console size.
366 ScmLoadDriver(L
"Blue");
368 ConsoleDeviceHandle
= CreateFileW(L
"\\\\.\\BlueScreen",
373 if (INVALID_HANDLE_VALUE
== ConsoleDeviceHandle
)
375 DPRINT1("Failed to open BlueScreen.\n");
379 if (!DeviceIoControl(ConsoleDeviceHandle
, IOCTL_CONSOLE_LOADFONT
,
380 &OemCP
, sizeof(OemCP
), NULL
, 0,
381 &BytesReturned
, NULL
))
383 DPRINT1("Failed to load the font for codepage %d\n", OemCP
);
384 /* Let's suppose the font is good enough to continue */
387 if (!DeviceIoControl(ConsoleDeviceHandle
, IOCTL_CONSOLE_SET_TEXT_ATTRIBUTE
,
388 &TextAttribute
, sizeof(TextAttribute
), NULL
, 0,
389 &BytesReturned
, NULL
))
391 DPRINT1("Failed to set text attribute\n");
394 ActiveConsole
= NULL
;
395 InitializeListHead(&VirtConsList
);
396 InitializeCriticalSection(&ActiveVirtConsLock
);
398 if (!DeviceIoControl(ConsoleDeviceHandle
, IOCTL_CONSOLE_GET_SCREEN_BUFFER_INFO
,
399 NULL
, 0, &ScrInfo
, sizeof(ScrInfo
), &BytesReturned
, NULL
))
401 DPRINT1("Failed to get console info\n");
405 PhysicalConsoleSize
= ScrInfo
.dwSize
;
407 /* Register the TUI notification window class */
408 RtlZeroMemory(&wc
, sizeof(WNDCLASSEXW
));
409 wc
.cbSize
= sizeof(WNDCLASSEXW
);
410 wc
.lpszClassName
= TUI_CONSOLE_WINDOW_CLASS
;
411 wc
.lpfnWndProc
= TuiConsoleWndProc
;
413 wc
.hInstance
= ConSrvDllInstance
;
415 ConsoleClassAtom
= RegisterClassExW(&wc
);
416 if (ConsoleClassAtom
== 0)
418 DPRINT1("Failed to register TUI console wndproc\n");
429 DeleteCriticalSection(&ActiveVirtConsLock
);
430 CloseHandle(ConsoleDeviceHandle
);
433 ConsInitialized
= Ret
;
439 /******************************************************************************
440 * TUI Console Driver *
441 ******************************************************************************/
444 TuiCleanupConsole(PCONSOLE Console
)
446 PTUI_CONSOLE_DATA TuiData
= Console
->TermIFace
.Data
;
448 /* Close the notification window */
449 DestroyWindow(TuiData
->hWindow
);
452 * Set the active console to the next one
453 * and remove the console from the list.
455 EnterCriticalSection(&ActiveVirtConsLock
);
456 ActiveConsole
= GetNextConsole(TuiData
);
457 RemoveEntryList(&TuiData
->Entry
);
459 // /* Switch to next console */
460 // if (ActiveConsole == TuiData)
461 // if (ActiveConsole->Console == Console)
463 // ActiveConsole = (TuiData->Entry.Flink != TuiData->Entry ? GetNextConsole(TuiData) : NULL);
466 // if (GetNextConsole(TuiData) != TuiData)
468 // TuiData->Entry.Blink->Flink = TuiData->Entry.Flink;
469 // TuiData->Entry.Flink->Blink = TuiData->Entry.Blink;
472 LeaveCriticalSection(&ActiveVirtConsLock
);
474 /* Switch to the next console */
475 if (NULL
!= ActiveConsole
) ConioDrawConsole(ActiveConsole
->Console
);
477 Console
->TermIFace
.Data
= NULL
;
478 DeleteCriticalSection(&TuiData
->Lock
);
479 RtlFreeHeap(ConSrvHeap
, 0, TuiData
);
483 TuiWriteStream(PCONSOLE Console
, SMALL_RECT
* Region
, SHORT CursorStartX
, SHORT CursorStartY
,
484 UINT ScrolledLines
, CHAR
*Buffer
, UINT Length
)
487 PCONSOLE_SCREEN_BUFFER Buff
= Console
->ActiveBuffer
;
489 if (ActiveConsole
->Console
->ActiveBuffer
!= Buff
) return;
491 if (!WriteFile(ConsoleDeviceHandle
, Buffer
, Length
, &BytesWritten
, NULL
))
493 DPRINT1("Error writing to BlueScreen\n");
498 TuiDrawRegion(PCONSOLE Console
, SMALL_RECT
* Region
)
501 PCONSOLE_SCREEN_BUFFER Buff
= Console
->ActiveBuffer
;
502 PCONSOLE_DRAW ConsoleDraw
;
503 UINT ConsoleDrawSize
;
505 if (ActiveConsole
->Console
!= Console
) return;
507 ConsoleDrawSize
= sizeof(CONSOLE_DRAW
) +
508 (ConioRectWidth(Region
) * ConioRectHeight(Region
)) * 2;
509 ConsoleDraw
= RtlAllocateHeap(ConSrvHeap
, 0, ConsoleDrawSize
);
510 if (NULL
== ConsoleDraw
)
512 DPRINT1("RtlAllocateHeap failed\n");
515 ConsoleDraw
->X
= Region
->Left
;
516 ConsoleDraw
->Y
= Region
->Top
;
517 ConsoleDraw
->SizeX
= ConioRectWidth(Region
);
518 ConsoleDraw
->SizeY
= ConioRectHeight(Region
);
519 ConsoleDraw
->CursorX
= Buff
->CursorPosition
.X
;
520 ConsoleDraw
->CursorY
= Buff
->CursorPosition
.Y
;
522 TuiCopyRect((char *) (ConsoleDraw
+ 1), Buff
, Region
);
524 if (!DeviceIoControl(ConsoleDeviceHandle
, IOCTL_CONSOLE_DRAW
,
525 NULL
, 0, ConsoleDraw
, ConsoleDrawSize
, &BytesReturned
, NULL
))
527 DPRINT1("Failed to draw console\n");
528 RtlFreeHeap(ConSrvHeap
, 0, ConsoleDraw
);
532 RtlFreeHeap(ConSrvHeap
, 0, ConsoleDraw
);
536 TuiSetCursorInfo(PCONSOLE Console
, PCONSOLE_SCREEN_BUFFER Buff
)
538 CONSOLE_CURSOR_INFO Info
;
541 if (ActiveConsole
->Console
->ActiveBuffer
!= Buff
) return TRUE
;
543 Info
.dwSize
= ConioEffectiveCursorSize(Console
, 100);
544 Info
.bVisible
= Buff
->CursorInfo
.bVisible
;
546 if (!DeviceIoControl(ConsoleDeviceHandle
, IOCTL_CONSOLE_SET_CURSOR_INFO
,
547 &Info
, sizeof(Info
), NULL
, 0, &BytesReturned
, NULL
))
549 DPRINT1( "Failed to set cursor info\n" );
557 TuiSetScreenInfo(PCONSOLE Console
, PCONSOLE_SCREEN_BUFFER Buff
, SHORT OldCursorX
, SHORT OldCursorY
)
559 CONSOLE_SCREEN_BUFFER_INFO Info
;
562 if (ActiveConsole
->Console
->ActiveBuffer
!= Buff
) return TRUE
;
564 Info
.dwCursorPosition
= Buff
->CursorPosition
;
565 Info
.wAttributes
= Buff
->ScreenDefaultAttrib
;
567 if (!DeviceIoControl(ConsoleDeviceHandle
, IOCTL_CONSOLE_SET_SCREEN_BUFFER_INFO
,
568 &Info
, sizeof(CONSOLE_SCREEN_BUFFER_INFO
), NULL
, 0,
569 &BytesReturned
, NULL
))
571 DPRINT1( "Failed to set cursor position\n" );
579 TuiUpdateScreenInfo(PCONSOLE Console
, PCONSOLE_SCREEN_BUFFER Buff
)
585 TuiIsBufferResizeSupported(PCONSOLE Console
)
587 return (Console
&& Console
->State
== CONSOLE_INITIALIZING
? TRUE
: FALSE
);
591 TuiResizeTerminal(PCONSOLE Console
)
596 TuiProcessKeyCallback(PCONSOLE Console
, MSG
* msg
, BYTE KeyStateMenu
, DWORD ShiftState
, UINT VirtualKeyCode
, BOOL Down
)
598 if (0 != (ShiftState
& (RIGHT_ALT_PRESSED
| LEFT_ALT_PRESSED
)) &&
599 VK_TAB
== VirtualKeyCode
)
603 TuiSwapConsole(ShiftState
& SHIFT_PRESSED
? -1 : 1);
608 else if (VK_MENU
== VirtualKeyCode
&& !Down
)
610 return TuiSwapConsole(0);
617 TuiRefreshInternalInfo(PCONSOLE Console
)
622 TuiChangeTitle(PCONSOLE Console
)
627 TuiChangeIcon(PCONSOLE Console
, HICON hWindowIcon
)
633 TuiGetConsoleWindowHandle(PCONSOLE Console
)
635 PTUI_CONSOLE_DATA TuiData
= Console
->TermIFace
.Data
;
636 return TuiData
->hWindow
;
640 TuiGetLargestConsoleWindowSize(PCONSOLE Console
, PCOORD pSize
)
643 *pSize
= PhysicalConsoleSize
;
646 static FRONTEND_VTBL TuiVtbl
=
654 TuiIsBufferResizeSupported
,
656 TuiProcessKeyCallback
,
657 TuiRefreshInternalInfo
,
660 TuiGetConsoleWindowHandle
,
661 TuiGetLargestConsoleWindowSize
665 TuiInitConsole(PCONSOLE Console
,
666 /*IN*/ PCONSOLE_START_INFO ConsoleStartInfo
,
667 PCONSOLE_INFO ConsoleInfo
,
670 PTUI_CONSOLE_DATA TuiData
;
673 if (Console
== NULL
|| ConsoleInfo
== NULL
)
674 return STATUS_INVALID_PARAMETER
;
676 /* Initialize the TUI terminal emulator */
677 if (!TuiInit(Console
->CodePage
)) return STATUS_UNSUCCESSFUL
;
679 /* Initialize the console */
680 Console
->TermIFace
.Vtbl
= &TuiVtbl
;
682 TuiData
= RtlAllocateHeap(ConSrvHeap
, HEAP_ZERO_MEMORY
,
683 sizeof(TUI_CONSOLE_DATA
));
686 DPRINT1("CONSRV: Failed to create TUI_CONSOLE_DATA\n");
687 return STATUS_UNSUCCESSFUL
;
689 Console
->TermIFace
.Data
= (PVOID
)TuiData
;
690 TuiData
->Console
= Console
;
691 TuiData
->hWindow
= NULL
;
693 InitializeCriticalSection(&TuiData
->Lock
);
696 * HACK: Resize the console since we don't support for now changing
697 * the console size when we display it with the hardware.
699 Console
->ConsoleSize
= PhysicalConsoleSize
;
700 ConioResizeBuffer(Console
, Console
->ActiveBuffer
, PhysicalConsoleSize
);
701 Console
->ActiveBuffer
->DisplayMode
|= CONSOLE_FULLSCREEN_MODE
;
702 // ConioResizeTerminal(Console);
705 * Contrary to what we do in the GUI front-end, here we create
706 * an input thread for each console. It will dispatch all the
707 * input messages to the proper console (on the GUI it is done
708 * via the default GUI dispatch thread).
710 ThreadHandle
= CreateThread(NULL
,
716 if (NULL
== ThreadHandle
)
718 DPRINT1("CONSRV: Unable to create console thread\n");
719 TuiCleanupConsole(Console
);
720 return STATUS_UNSUCCESSFUL
;
722 CloseHandle(ThreadHandle
);
725 * Insert the newly created console in the list of virtual consoles
726 * and activate it (give it the focus).
728 EnterCriticalSection(&ActiveVirtConsLock
);
729 InsertTailList(&VirtConsList
, &TuiData
->Entry
);
730 ActiveConsole
= TuiData
;
731 LeaveCriticalSection(&ActiveVirtConsLock
);
733 return STATUS_SUCCESS
;