/* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS Console Server DLL * FILE: win32ss/user/consrv/tuiconsole.c * PURPOSE: TUI terminal emulator * PROGRAMMERS: */ #ifdef TUI_CONSOLE #include "consrv.h" #include "settings.h" #include "tuiconsole.h" #include #define NDEBUG #include /* GLOBALS ********************************************************************/ /* TUI Console Window Class name */ #define TUI_CONSOLE_WINDOW_CLASS L"TuiConsoleWindowClass" typedef struct _TUI_CONSOLE_DATA { CRITICAL_SECTION Lock; // HANDLE hTuiInitEvent; HWND hWindow; PCONSOLE Console; // TUI_CONSOLE_INFO TuiInfo; } TUI_CONSOLE_DATA, *PTUI_CONSOLE_DATA; CRITICAL_SECTION ActiveConsoleLock; static COORD PhysicalConsoleSize; static HANDLE ConsoleDeviceHandle; static PCONSOLE ActiveConsole; static BOOL ConsInitialized = FALSE; /******************************************************************************\ |** BlueScreen Driver management **| \**/ /* Code taken and adapted from base/system/services/driver.c */ static DWORD ScmLoadDriver(LPCWSTR lpServiceName) { NTSTATUS Status = STATUS_SUCCESS; BOOLEAN WasPrivilegeEnabled = FALSE; PWSTR pszDriverPath; UNICODE_STRING DriverPath; /* Build the driver path */ /* 52 = wcslen(L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\") */ pszDriverPath = RtlAllocateHeap(ConSrvHeap, HEAP_ZERO_MEMORY, (52 + wcslen(lpServiceName) + 1) * sizeof(WCHAR)); if (pszDriverPath == NULL) return ERROR_NOT_ENOUGH_MEMORY; wcscpy(pszDriverPath, L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\"); wcscat(pszDriverPath, lpServiceName); RtlInitUnicodeString(&DriverPath, pszDriverPath); DPRINT(" Path: %wZ\n", &DriverPath); /* Acquire driver-loading privilege */ Status = RtlAdjustPrivilege(SE_LOAD_DRIVER_PRIVILEGE, TRUE, FALSE, &WasPrivilegeEnabled); if (!NT_SUCCESS(Status)) { /* We encountered a failure, exit properly */ DPRINT1("CONSRV: Cannot acquire driver-loading privilege, Status = 0x%08lx\n", Status); goto done; } Status = NtLoadDriver(&DriverPath); /* Release driver-loading privilege */ RtlAdjustPrivilege(SE_LOAD_DRIVER_PRIVILEGE, WasPrivilegeEnabled, FALSE, &WasPrivilegeEnabled); done: RtlFreeHeap(ConSrvHeap, 0, pszDriverPath); return RtlNtStatusToDosError(Status); } #ifdef BLUESCREEN_DRIVER_UNLOADING static DWORD ScmUnloadDriver(LPCWSTR lpServiceName) { NTSTATUS Status = STATUS_SUCCESS; BOOLEAN WasPrivilegeEnabled = FALSE; PWSTR pszDriverPath; UNICODE_STRING DriverPath; /* Build the driver path */ /* 52 = wcslen(L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\") */ pszDriverPath = RtlAllocateHeap(ConSrvHeap, HEAP_ZERO_MEMORY, (52 + wcslen(lpServiceName) + 1) * sizeof(WCHAR)); if (pszDriverPath == NULL) return ERROR_NOT_ENOUGH_MEMORY; wcscpy(pszDriverPath, L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\"); wcscat(pszDriverPath, lpServiceName); RtlInitUnicodeString(&DriverPath, pszDriverPath); DPRINT(" Path: %wZ\n", &DriverPath); /* Acquire driver-unloading privilege */ Status = RtlAdjustPrivilege(SE_LOAD_DRIVER_PRIVILEGE, TRUE, FALSE, &WasPrivilegeEnabled); if (!NT_SUCCESS(Status)) { /* We encountered a failure, exit properly */ DPRINT1("CONSRV: Cannot acquire driver-unloading privilege, Status = 0x%08lx\n", Status); goto done; } Status = NtUnloadDriver(&DriverPath); /* Release driver-unloading privilege */ RtlAdjustPrivilege(SE_LOAD_DRIVER_PRIVILEGE, WasPrivilegeEnabled, FALSE, &WasPrivilegeEnabled); done: RtlFreeHeap(ConSrvHeap, 0, pszDriverPath); return RtlNtStatusToDosError(Status); } #endif /**\ \******************************************************************************/ static LRESULT CALLBACK TuiConsoleWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { if (msg == WM_ACTIVATE) { if (LOWORD(wParam) != WA_INACTIVE) { SetFocus(hWnd); ConioDrawConsole(ActiveConsole); } } return DefWindowProcW(hWnd, msg, wParam, lParam); } static BOOL FASTCALL TuiSwapConsole(INT Next) { static PCONSOLE SwapConsole = NULL; /* console we are thinking about swapping with */ DWORD BytesReturned; ANSI_STRING Title; PVOID Buffer; PCOORD pos; if (0 != Next) { /* alt-tab, swap consoles */ /* move SwapConsole to next console, and print its title */ EnterCriticalSection(&ActiveConsoleLock); if (!SwapConsole) { SwapConsole = ActiveConsole; } SwapConsole = (0 < Next ? SwapConsole->Next : SwapConsole->Prev); Title.MaximumLength = RtlUnicodeStringToAnsiSize(&SwapConsole->Title); Title.Length = 0; Buffer = RtlAllocateHeap(ConSrvHeap, 0, sizeof(COORD) + Title.MaximumLength); pos = (PCOORD )Buffer; Title.Buffer = (PVOID)((ULONG_PTR)Buffer + sizeof( COORD )); RtlUnicodeStringToAnsiString(&Title, &SwapConsole->Title, FALSE); pos->Y = PhysicalConsoleSize.Y / 2; pos->X = (PhysicalConsoleSize.X - Title.Length) / 2; /* redraw the console to clear off old title */ ConioDrawConsole(ActiveConsole); if (!DeviceIoControl(ConsoleDeviceHandle, IOCTL_CONSOLE_WRITE_OUTPUT_CHARACTER, NULL, 0, Buffer, sizeof(COORD) + Title.Length, &BytesReturned, NULL)) { DPRINT1( "Error writing to console\n" ); } RtlFreeHeap(ConSrvHeap, 0, Buffer); LeaveCriticalSection(&ActiveConsoleLock); return TRUE; } else if (NULL != SwapConsole) { EnterCriticalSection(&ActiveConsoleLock); if (SwapConsole != ActiveConsole) { /* first remove swapconsole from the list */ SwapConsole->Prev->Next = SwapConsole->Next; SwapConsole->Next->Prev = SwapConsole->Prev; /* now insert before activeconsole */ SwapConsole->Next = ActiveConsole; SwapConsole->Prev = ActiveConsole->Prev; ActiveConsole->Prev->Next = SwapConsole; ActiveConsole->Prev = SwapConsole; } ActiveConsole = SwapConsole; SwapConsole = NULL; ConioDrawConsole(ActiveConsole); LeaveCriticalSection(&ActiveConsoleLock); return TRUE; } else { return FALSE; } } static BOOL WINAPI TuiProcessKeyCallback(PCONSOLE Console, MSG* msg, BYTE KeyStateMenu, DWORD ShiftState, UINT VirtualKeyCode, BOOL Down) { if (0 != (ShiftState & (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED)) && VK_TAB == VirtualKeyCode) { if (Down) { TuiSwapConsole(ShiftState & SHIFT_PRESSED ? -1 : 1); } return TRUE; } else if (VK_MENU == VirtualKeyCode && !Down) { return TuiSwapConsole(0); } return FALSE; } static BOOL FASTCALL TuiInit(DWORD OemCP) { CONSOLE_SCREEN_BUFFER_INFO ScrInfo; DWORD BytesReturned; WNDCLASSEXW wc; ATOM ConsoleClassAtom; USHORT TextAttribute = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED; ScmLoadDriver(L"Blue"); ConsoleDeviceHandle = CreateFileW(L"\\\\.\\BlueScreen", FILE_ALL_ACCESS, 0, NULL, OPEN_EXISTING, 0, NULL); if (INVALID_HANDLE_VALUE == ConsoleDeviceHandle) { DPRINT1("Failed to open BlueScreen.\n"); return FALSE; } if (!DeviceIoControl(ConsoleDeviceHandle, IOCTL_CONSOLE_LOADFONT, &OemCP, sizeof(OemCP), NULL, 0, &BytesReturned, NULL)) { DPRINT1("Failed to load the font for codepage %d\n", OemCP); /* Let's suppose the font is good enough to continue */ } if (!DeviceIoControl(ConsoleDeviceHandle, IOCTL_CONSOLE_SET_TEXT_ATTRIBUTE, &TextAttribute, sizeof(TextAttribute), NULL, 0, &BytesReturned, NULL)) { DPRINT1("Failed to set text attribute\n"); } ActiveConsole = NULL; InitializeCriticalSection(&ActiveConsoleLock); if (!DeviceIoControl(ConsoleDeviceHandle, IOCTL_CONSOLE_GET_SCREEN_BUFFER_INFO, NULL, 0, &ScrInfo, sizeof(ScrInfo), &BytesReturned, NULL)) { DPRINT1("Failed to get console info\n"); return FALSE; } PhysicalConsoleSize = ScrInfo.dwSize; RtlZeroMemory(&wc, sizeof(WNDCLASSEXW)); wc.cbSize = sizeof(WNDCLASSEXW); wc.lpszClassName = TUI_CONSOLE_WINDOW_CLASS; wc.lpfnWndProc = TuiConsoleWndProc; wc.cbWndExtra = GWLP_CONSOLEWND_ALLOC; wc.hInstance = ConSrvDllInstance; ConsoleClassAtom = RegisterClassExW(&wc); if (ConsoleClassAtom == 0) { DPRINT1("Failed to register TUI console wndproc\n"); return FALSE; } else { NtUserConsoleControl(TuiConsoleWndClassAtom, &ConsoleClassAtom, sizeof(ATOM)); } return TRUE; } static VOID FASTCALL TuiCopyRect(char *Dest, PCONSOLE_SCREEN_BUFFER Buff, SMALL_RECT* Region) { UINT SrcDelta, DestDelta; LONG i; PBYTE Src, SrcEnd; Src = ConioCoordToPointer(Buff, Region->Left, Region->Top); SrcDelta = Buff->ScreenBufferSize.X * 2; SrcEnd = Buff->Buffer + Buff->ScreenBufferSize.Y * Buff->ScreenBufferSize.X * 2; DestDelta = ConioRectWidth(Region) * 2; for (i = Region->Top; i <= Region->Bottom; i++) { memcpy(Dest, Src, DestDelta); Src += SrcDelta; if (SrcEnd <= Src) { Src -= Buff->ScreenBufferSize.Y * Buff->ScreenBufferSize.X * 2; } Dest += DestDelta; } } static VOID WINAPI TuiDrawRegion(PCONSOLE Console, SMALL_RECT* Region) { DWORD BytesReturned; PCONSOLE_SCREEN_BUFFER Buff = Console->ActiveBuffer; PCONSOLE_DRAW ConsoleDraw; UINT ConsoleDrawSize; if (ActiveConsole != Console) { return; } ConsoleDrawSize = sizeof(CONSOLE_DRAW) + (ConioRectWidth(Region) * ConioRectHeight(Region)) * 2; ConsoleDraw = RtlAllocateHeap(ConSrvHeap, 0, ConsoleDrawSize); if (NULL == ConsoleDraw) { DPRINT1("RtlAllocateHeap failed\n"); return; } ConsoleDraw->X = Region->Left; ConsoleDraw->Y = Region->Top; ConsoleDraw->SizeX = ConioRectWidth(Region); ConsoleDraw->SizeY = ConioRectHeight(Region); ConsoleDraw->CursorX = Buff->CursorPosition.X; ConsoleDraw->CursorY = Buff->CursorPosition.Y; TuiCopyRect((char *) (ConsoleDraw + 1), Buff, Region); if (!DeviceIoControl(ConsoleDeviceHandle, IOCTL_CONSOLE_DRAW, NULL, 0, ConsoleDraw, ConsoleDrawSize, &BytesReturned, NULL)) { DPRINT1("Failed to draw console\n"); RtlFreeHeap(ConSrvHeap, 0, ConsoleDraw); return; } RtlFreeHeap(ConSrvHeap, 0, ConsoleDraw); } static VOID WINAPI TuiWriteStream(PCONSOLE Console, SMALL_RECT* Region, LONG CursorStartX, LONG CursorStartY, UINT ScrolledLines, CHAR *Buffer, UINT Length) { DWORD BytesWritten; PCONSOLE_SCREEN_BUFFER Buff = Console->ActiveBuffer; if (ActiveConsole->ActiveBuffer != Buff) { return; } if (!WriteFile(ConsoleDeviceHandle, Buffer, Length, &BytesWritten, NULL)) { DPRINT1("Error writing to BlueScreen\n"); } } static BOOL WINAPI TuiSetCursorInfo(PCONSOLE Console, PCONSOLE_SCREEN_BUFFER Buff) { CONSOLE_CURSOR_INFO Info; DWORD BytesReturned; if (ActiveConsole->ActiveBuffer != Buff) { return TRUE; } Info.dwSize = ConioEffectiveCursorSize(Console, 100); Info.bVisible = Buff->CursorInfo.bVisible; if (!DeviceIoControl(ConsoleDeviceHandle, IOCTL_CONSOLE_SET_CURSOR_INFO, &Info, sizeof(Info), NULL, 0, &BytesReturned, NULL)) { DPRINT1( "Failed to set cursor info\n" ); return FALSE; } return TRUE; } static BOOL WINAPI TuiSetScreenInfo(PCONSOLE Console, PCONSOLE_SCREEN_BUFFER Buff, UINT OldCursorX, UINT OldCursorY) { CONSOLE_SCREEN_BUFFER_INFO Info; DWORD BytesReturned; if (ActiveConsole->ActiveBuffer != Buff) { return TRUE; } Info.dwCursorPosition = Buff->CursorPosition; Info.wAttributes = Buff->ScreenDefaultAttrib; if (!DeviceIoControl(ConsoleDeviceHandle, IOCTL_CONSOLE_SET_SCREEN_BUFFER_INFO, &Info, sizeof(CONSOLE_SCREEN_BUFFER_INFO), NULL, 0, &BytesReturned, NULL)) { DPRINT1( "Failed to set cursor position\n" ); return FALSE; } return TRUE; } static BOOL WINAPI TuiUpdateScreenInfo(PCONSOLE Console, PCONSOLE_SCREEN_BUFFER Buff) { return TRUE; } static VOID WINAPI TuiChangeTitle(PCONSOLE Console) { } static VOID WINAPI TuiCleanupConsole(PCONSOLE Console) { DestroyWindow(Console->hWindow); EnterCriticalSection(&ActiveConsoleLock); /* Switch to next console */ if (ActiveConsole == Console) { ActiveConsole = Console->Next != Console ? Console->Next : NULL; } if (Console->Next != Console) { Console->Prev->Next = Console->Next; Console->Next->Prev = Console->Prev; } LeaveCriticalSection(&ActiveConsoleLock); if (NULL != ActiveConsole) { ConioDrawConsole(ActiveConsole); } } static BOOL WINAPI TuiChangeIcon(PCONSOLE Console, HICON hWindowIcon) { return TRUE; } static HWND WINAPI TuiGetConsoleWindowHandle(PCONSOLE Console) { return Console->hWindow; } static NTSTATUS WINAPI TuiResizeBuffer(PCONSOLE Console, PCONSOLE_SCREEN_BUFFER ScreenBuffer, COORD Size) { UNIMPLEMENTED; return STATUS_NOT_IMPLEMENTED; } static DWORD WINAPI TuiConsoleThread(PVOID Data) { PCONSOLE Console = (PCONSOLE) Data; HWND NewWindow; MSG msg; NewWindow = CreateWindowW(TUI_CONSOLE_WINDOW_CLASS, Console->Title.Buffer, 0, -32000, -32000, 0, 0, NULL, NULL, ConSrvDllInstance, (PVOID)Console); if (NULL == NewWindow) { DPRINT1("CONSRV: Unable to create console window\n"); return 1; } Console->hWindow = NewWindow; SetForegroundWindow(Console->hWindow); while (TRUE) { GetMessageW(&msg, 0, 0, 0); DispatchMessage(&msg); TranslateMessage(&msg); if (msg.message == WM_CHAR || msg.message == WM_SYSCHAR || msg.message == WM_KEYDOWN || msg.message == WM_SYSKEYDOWN || msg.message == WM_KEYUP || msg.message == WM_SYSKEYUP) { ConioProcessKey(Console, &msg); } } return 0; } static TERMINAL_VTBL TuiVtbl = { TuiCleanupConsole, TuiWriteStream, TuiDrawRegion, TuiSetCursorInfo, TuiSetScreenInfo, TuiUpdateScreenInfo, TuiChangeTitle, TuiChangeIcon, TuiGetConsoleWindowHandle, TuiResizeBuffer, TuiProcessKeyCallback }; NTSTATUS FASTCALL TuiInitConsole(PCONSOLE Console, PCONSOLE_INFO ConsoleInfo) { HANDLE ThreadHandle; if (!ConsInitialized) { ConsInitialized = TRUE; if (!TuiInit(Console->CodePage)) { ConsInitialized = FALSE; return STATUS_UNSUCCESSFUL; } } Console->TermIFace.Vtbl = &TuiVtbl; Console->hWindow = NULL; Console->Size = PhysicalConsoleSize; Console->ActiveBuffer->ScreenBufferSize = PhysicalConsoleSize; ThreadHandle = CreateThread(NULL, 0, TuiConsoleThread, (PVOID)Console, 0, NULL); if (NULL == ThreadHandle) { DPRINT1("CONSRV: Unable to create console thread\n"); return STATUS_UNSUCCESSFUL; } CloseHandle(ThreadHandle); EnterCriticalSection(&ActiveConsoleLock); if (NULL != ActiveConsole) { Console->Prev = ActiveConsole; Console->Next = ActiveConsole->Next; ActiveConsole->Next->Prev = Console; ActiveConsole->Next = Console; } else { Console->Prev = Console; Console->Next = Console; } ActiveConsole = Console; LeaveCriticalSection(&ActiveConsoleLock); return STATUS_SUCCESS; } PCONSOLE FASTCALL TuiGetFocusConsole(VOID) { return ActiveConsole; } #endif /* EOF */