mirror of
https://github.com/reactos/reactos.git
synced 2024-11-02 12:53:33 +00:00
542 lines
15 KiB
C
542 lines
15 KiB
C
/*
|
|
* COPYRIGHT: See COPYING in the top level directory
|
|
* PROJECT: ReactOS Console Driver DLL
|
|
* FILE: win32ss/user/winsrv/consrv/condrv/console.c
|
|
* PURPOSE: Console Management Functions
|
|
* PROGRAMMERS: Gé van Geldorp
|
|
* Jeffrey Morlan
|
|
* Hermes Belusca-Maito (hermes.belusca@sfr.fr)
|
|
*/
|
|
|
|
/* INCLUDES *******************************************************************/
|
|
|
|
#include <consrv.h>
|
|
|
|
#include <coninput.h>
|
|
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
|
|
/* GLOBALS ********************************************************************/
|
|
|
|
static ULONG CurrentConsoleID = 0;
|
|
|
|
/* Linked list of consoles */
|
|
static LIST_ENTRY ConDrvConsoleList;
|
|
static RTL_RESOURCE ListLock;
|
|
|
|
#define ConDrvLockConsoleListExclusive() \
|
|
RtlAcquireResourceExclusive(&ListLock, TRUE)
|
|
|
|
#define ConDrvLockConsoleListShared() \
|
|
RtlAcquireResourceShared(&ListLock, TRUE)
|
|
|
|
#define ConDrvUnlockConsoleList() \
|
|
RtlReleaseResource(&ListLock)
|
|
|
|
|
|
static NTSTATUS
|
|
ConDrvInsertConsole(IN PCONSOLE Console)
|
|
{
|
|
ASSERT(Console);
|
|
|
|
/* All went right, so add the console to the list */
|
|
ConDrvLockConsoleListExclusive();
|
|
|
|
DPRINT("Insert in the list\n");
|
|
InsertTailList(&ConDrvConsoleList, &Console->ListEntry);
|
|
|
|
// FIXME: Move this code to the caller function!!
|
|
/* Get a new console ID */
|
|
_InterlockedExchange((PLONG)&Console->ConsoleID, CurrentConsoleID);
|
|
_InterlockedIncrement((PLONG)&CurrentConsoleID);
|
|
|
|
/* Unlock the console list and return success */
|
|
ConDrvUnlockConsoleList();
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
static NTSTATUS
|
|
RemoveConsole(IN PCONSOLE Console)
|
|
{
|
|
// ASSERT(Console);
|
|
if (!Console) return STATUS_INVALID_PARAMETER;
|
|
|
|
/* Remove the console from the list */
|
|
ConDrvLockConsoleListExclusive();
|
|
|
|
RemoveEntryList(&Console->ListEntry);
|
|
|
|
/* Unlock the console list and return success */
|
|
ConDrvUnlockConsoleList();
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
/* PRIVATE FUNCTIONS **********************************************************/
|
|
|
|
VOID NTAPI
|
|
ConDrvPause(PCONSOLE Console)
|
|
{
|
|
/* In case we already have a pause event, just exit... */
|
|
if (Console->UnpauseEvent) return;
|
|
|
|
/* ... otherwise create it */
|
|
NtCreateEvent(&Console->UnpauseEvent, EVENT_ALL_ACCESS,
|
|
NULL, NotificationEvent, FALSE);
|
|
}
|
|
|
|
VOID NTAPI
|
|
ConDrvUnpause(PCONSOLE Console)
|
|
{
|
|
/* In case we already freed the event, just exit... */
|
|
if (!Console->UnpauseEvent) return;
|
|
|
|
/* ... otherwise set and free it */
|
|
NtSetEvent(Console->UnpauseEvent, NULL);
|
|
NtClose(Console->UnpauseEvent);
|
|
Console->UnpauseEvent = NULL;
|
|
}
|
|
|
|
|
|
/*
|
|
* Console accessibility check helpers
|
|
*/
|
|
|
|
BOOLEAN NTAPI
|
|
ConDrvValidateConsoleState(IN PCONSOLE Console,
|
|
IN CONSOLE_STATE ExpectedState)
|
|
{
|
|
// if (!Console) return FALSE;
|
|
|
|
/* The console must be locked */
|
|
// ASSERT(Console_locked);
|
|
|
|
return (Console->State == ExpectedState);
|
|
}
|
|
|
|
BOOLEAN NTAPI
|
|
ConDrvValidateConsoleUnsafe(IN PCONSOLE Console,
|
|
IN CONSOLE_STATE ExpectedState,
|
|
IN BOOLEAN LockConsole)
|
|
{
|
|
if (!Console) return FALSE;
|
|
|
|
/*
|
|
* Lock the console to forbid possible console's state changes
|
|
* (which must be done when the console is already locked).
|
|
* If we don't want to lock it, it's because the lock is already
|
|
* held. So there must be no problems.
|
|
*/
|
|
if (LockConsole) EnterCriticalSection(&Console->Lock);
|
|
|
|
// ASSERT(Console_locked);
|
|
|
|
/* Check whether the console's state is what we expect */
|
|
if (!ConDrvValidateConsoleState(Console, ExpectedState))
|
|
{
|
|
if (LockConsole) LeaveCriticalSection(&Console->Lock);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/* CONSOLE INITIALIZATION FUNCTIONS *******************************************/
|
|
|
|
VOID NTAPI
|
|
ConDrvInitConsoleSupport(VOID)
|
|
{
|
|
DPRINT("CONSRV: ConDrvInitConsoleSupport()\n");
|
|
|
|
/* Initialize the console list and its lock */
|
|
InitializeListHead(&ConDrvConsoleList);
|
|
RtlInitializeResource(&ListLock);
|
|
}
|
|
|
|
/* For resetting the terminal - defined in dummyterm.c */
|
|
VOID ResetTerminal(IN PCONSOLE Console);
|
|
|
|
NTSTATUS NTAPI
|
|
ConDrvInitConsole(OUT PCONSOLE* NewConsole,
|
|
IN PCONSOLE_INFO ConsoleInfo)
|
|
{
|
|
NTSTATUS Status;
|
|
// CONSOLE_INFO CapturedConsoleInfo;
|
|
TEXTMODE_BUFFER_INFO ScreenBufferInfo;
|
|
PCONSOLE Console;
|
|
PCONSOLE_SCREEN_BUFFER NewBuffer;
|
|
|
|
if (NewConsole == NULL || ConsoleInfo == NULL)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
*NewConsole = NULL;
|
|
|
|
/*
|
|
* Allocate a new console
|
|
*/
|
|
Console = ConsoleAllocHeap(HEAP_ZERO_MEMORY, sizeof(*Console));
|
|
if (NULL == Console)
|
|
{
|
|
DPRINT1("Not enough memory for console creation.\n");
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
/*
|
|
* Fix the screen buffer size if needed. The rule is:
|
|
* ScreenBufferSize >= ConsoleSize
|
|
*/
|
|
if (ConsoleInfo->ScreenBufferSize.X < ConsoleInfo->ConsoleSize.X)
|
|
ConsoleInfo->ScreenBufferSize.X = ConsoleInfo->ConsoleSize.X;
|
|
if (ConsoleInfo->ScreenBufferSize.Y < ConsoleInfo->ConsoleSize.Y)
|
|
ConsoleInfo->ScreenBufferSize.Y = ConsoleInfo->ConsoleSize.Y;
|
|
|
|
/*
|
|
* Initialize the console
|
|
*/
|
|
Console->State = CONSOLE_INITIALIZING;
|
|
Console->ReferenceCount = 0;
|
|
InitializeCriticalSection(&Console->Lock);
|
|
|
|
/* Initialize the terminal interface */
|
|
ResetTerminal(Console);
|
|
|
|
Console->ConsoleSize = ConsoleInfo->ConsoleSize;
|
|
Console->FixedSize = FALSE; // Value by default; is reseted by the terminals if needed.
|
|
|
|
/* Initialize the input buffer */
|
|
Status = ConDrvInitInputBuffer(Console, 0 /* ConsoleInfo->InputBufferSize */);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("ConDrvInitInputBuffer: failed, Status = 0x%08lx\n", Status);
|
|
DeleteCriticalSection(&Console->Lock);
|
|
ConsoleFreeHeap(Console);
|
|
return Status;
|
|
}
|
|
|
|
/* Set-up the code page */
|
|
if (IsValidCodePage(ConsoleInfo->CodePage))
|
|
Console->InputCodePage = Console->OutputCodePage = ConsoleInfo->CodePage;
|
|
|
|
/* Initialize a new text-mode screen buffer with default settings */
|
|
ScreenBufferInfo.ScreenBufferSize = ConsoleInfo->ScreenBufferSize;
|
|
ScreenBufferInfo.ScreenAttrib = ConsoleInfo->ScreenAttrib;
|
|
ScreenBufferInfo.PopupAttrib = ConsoleInfo->PopupAttrib;
|
|
ScreenBufferInfo.IsCursorVisible = TRUE;
|
|
ScreenBufferInfo.CursorSize = ConsoleInfo->CursorSize;
|
|
|
|
InitializeListHead(&Console->BufferList);
|
|
Status = ConDrvCreateScreenBuffer(&NewBuffer,
|
|
Console,
|
|
NULL,
|
|
CONSOLE_TEXTMODE_BUFFER,
|
|
&ScreenBufferInfo);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("ConDrvCreateScreenBuffer: failed, Status = 0x%08lx\n", Status);
|
|
ConDrvDeinitInputBuffer(Console);
|
|
DeleteCriticalSection(&Console->Lock);
|
|
ConsoleFreeHeap(Console);
|
|
return Status;
|
|
}
|
|
/* Make the new screen buffer active */
|
|
Console->ActiveBuffer = NewBuffer;
|
|
Console->UnpauseEvent = NULL;
|
|
|
|
DPRINT("Console initialized\n");
|
|
|
|
/* All went right, so add the console to the list */
|
|
Status = ConDrvInsertConsole(Console);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* Fail */
|
|
ConDrvDeleteConsole(Console);
|
|
return Status;
|
|
}
|
|
|
|
/* The initialization is finished */
|
|
DPRINT("Change state\n");
|
|
Console->State = CONSOLE_RUNNING;
|
|
|
|
/* Return the newly created console to the caller and a success code too */
|
|
*NewConsole = Console;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS NTAPI
|
|
ConDrvAttachTerminal(IN PCONSOLE Console,
|
|
IN PTERMINAL Terminal)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
if (Console == NULL || Terminal == NULL)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
/* FIXME: Lock the console before ?? */
|
|
|
|
/*
|
|
* Attach the terminal to the console. Use now the TermIFace of the console,
|
|
* and not the user-defined temporary Terminal pointer.
|
|
*/
|
|
Console->TermIFace = *Terminal;
|
|
Console->TermIFace.Console = Console;
|
|
|
|
/* Initialize the terminal AFTER having attached it to the console */
|
|
DPRINT("Finish initialization of terminal\n");
|
|
Status = Console->TermIFace.Vtbl->InitTerminal(&Console->TermIFace, Console);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("Terminal initialization failed, Status = 0x%08lx\n", Status);
|
|
|
|
/* We failed, detach the terminal from the console */
|
|
Terminal->Console = NULL; // For the caller
|
|
ResetTerminal(Console);
|
|
return Status;
|
|
}
|
|
|
|
/* Copy buffer contents to screen */
|
|
// Terminal.Draw();
|
|
|
|
DPRINT("Terminal initialization done\n");
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS NTAPI
|
|
ConDrvDetachTerminal(IN PCONSOLE Console)
|
|
{
|
|
if (Console == NULL) return STATUS_INVALID_PARAMETER;
|
|
|
|
/* FIXME: Lock the console before ?? */
|
|
|
|
/* Deinitialize the terminal BEFORE detaching it from the console */
|
|
Console->TermIFace.Vtbl->DeinitTerminal(&Console->TermIFace/*, Console*/);
|
|
|
|
/*
|
|
* Detach the terminal from the console:
|
|
* reinitialize the terminal interface.
|
|
*/
|
|
ResetTerminal(Console);
|
|
|
|
DPRINT("Terminal unregistered\n");
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
VOID NTAPI
|
|
ConDrvDeleteConsole(IN PCONSOLE Console)
|
|
{
|
|
DPRINT("ConDrvDeleteConsole(0x%p)\n", Console);
|
|
|
|
/*
|
|
* Forbid validation of any console by other threads
|
|
* during the deletion of this console.
|
|
*/
|
|
ConDrvLockConsoleListExclusive();
|
|
|
|
/*
|
|
* If the console is already being destroyed, i.e. not running
|
|
* or finishing to be initialized, just return.
|
|
*/
|
|
if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE) &&
|
|
!ConDrvValidateConsoleUnsafe(Console, CONSOLE_INITIALIZING, TRUE))
|
|
{
|
|
/* Unlock the console list and return */
|
|
ConDrvUnlockConsoleList();
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* We are about to be destroyed. Signal it to other people
|
|
* so that they can terminate what they are doing, and that
|
|
* they cannot longer validate the console.
|
|
*/
|
|
Console->State = CONSOLE_TERMINATING;
|
|
|
|
/*
|
|
* Allow other threads to finish their job: basically, unlock
|
|
* all other calls to EnterCriticalSection(&Console->Lock); by
|
|
* ConDrvValidateConsoleUnsafe functions so that they just see
|
|
* that we are not in CONSOLE_RUNNING state anymore, or unlock
|
|
* other concurrent calls to ConDrvDeleteConsole so that they
|
|
* can see that we are in fact already deleting the console.
|
|
*/
|
|
LeaveCriticalSection(&Console->Lock);
|
|
ConDrvUnlockConsoleList();
|
|
|
|
/* Deregister the terminal */
|
|
DPRINT("Deregister terminal\n");
|
|
ConDrvDetachTerminal(Console);
|
|
DPRINT("Terminal deregistered\n");
|
|
|
|
/***
|
|
* Check that the console is in terminating state before continuing
|
|
* (the cleanup code must not change the state of the console...
|
|
* ...unless to cancel console deletion ?).
|
|
***/
|
|
|
|
ConDrvLockConsoleListExclusive();
|
|
|
|
if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_TERMINATING, TRUE))
|
|
{
|
|
ConDrvUnlockConsoleList();
|
|
return;
|
|
}
|
|
|
|
/* We are now in destruction */
|
|
Console->State = CONSOLE_IN_DESTRUCTION;
|
|
|
|
/* We really delete the console. Reset the count to be sure. */
|
|
Console->ReferenceCount = 0;
|
|
|
|
/* Remove the console from the list */
|
|
RemoveConsole(Console);
|
|
|
|
/* Delete the last screen buffer */
|
|
ConDrvDeleteScreenBuffer(Console->ActiveBuffer);
|
|
Console->ActiveBuffer = NULL;
|
|
if (!IsListEmpty(&Console->BufferList))
|
|
{
|
|
/***ConDrvUnlockConsoleList();***/
|
|
ASSERTMSG("BUGBUGBUG!! screen buffer list not empty\n", FALSE);
|
|
}
|
|
|
|
/* Deinitialize the input buffer */
|
|
ConDrvDeinitInputBuffer(Console);
|
|
|
|
if (Console->UnpauseEvent) CloseHandle(Console->UnpauseEvent);
|
|
|
|
DPRINT("ConDrvDeleteConsole - Unlocking\n");
|
|
LeaveCriticalSection(&Console->Lock);
|
|
DPRINT("ConDrvDeleteConsole - Destroying lock\n");
|
|
DeleteCriticalSection(&Console->Lock);
|
|
DPRINT("ConDrvDeleteConsole - Lock destroyed ; freeing console\n");
|
|
|
|
ConsoleFreeHeap(Console);
|
|
DPRINT("ConDrvDeleteConsole - Console destroyed\n");
|
|
|
|
/* Unlock the console list and return */
|
|
ConDrvUnlockConsoleList();
|
|
}
|
|
|
|
|
|
/* PUBLIC DRIVER APIS *********************************************************/
|
|
|
|
NTSTATUS NTAPI
|
|
ConDrvGetConsoleMode(IN PCONSOLE Console,
|
|
IN PCONSOLE_IO_OBJECT Object,
|
|
OUT PULONG ConsoleMode)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
if (Console == NULL || Object == NULL || ConsoleMode == NULL)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
/* Validity check */
|
|
ASSERT(Console == Object->Console);
|
|
|
|
/*** FIXME: */ *ConsoleMode = 0; /***/
|
|
|
|
if (INPUT_BUFFER == Object->Type)
|
|
{
|
|
PCONSOLE_INPUT_BUFFER InputBuffer = (PCONSOLE_INPUT_BUFFER)Object;
|
|
*ConsoleMode = InputBuffer->Mode;
|
|
}
|
|
else if (TEXTMODE_BUFFER == Object->Type || GRAPHICS_BUFFER == Object->Type)
|
|
{
|
|
PCONSOLE_SCREEN_BUFFER Buffer = (PCONSOLE_SCREEN_BUFFER)Object;
|
|
*ConsoleMode = Buffer->Mode;
|
|
}
|
|
else
|
|
{
|
|
Status = STATUS_INVALID_HANDLE;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS NTAPI
|
|
ConDrvSetConsoleMode(IN PCONSOLE Console,
|
|
IN PCONSOLE_IO_OBJECT Object,
|
|
IN ULONG ConsoleMode)
|
|
{
|
|
#define CONSOLE_VALID_INPUT_MODES ( ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT | \
|
|
ENABLE_ECHO_INPUT | ENABLE_WINDOW_INPUT | \
|
|
ENABLE_MOUSE_INPUT )
|
|
#define CONSOLE_VALID_OUTPUT_MODES ( ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT )
|
|
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
if (Console == NULL || Object == NULL)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
/* Validity check */
|
|
ASSERT(Console == Object->Console);
|
|
|
|
if (INPUT_BUFFER == Object->Type)
|
|
{
|
|
PCONSOLE_INPUT_BUFFER InputBuffer = (PCONSOLE_INPUT_BUFFER)Object;
|
|
|
|
/* Only the presence of valid mode flags is allowed */
|
|
if (ConsoleMode & ~CONSOLE_VALID_INPUT_MODES)
|
|
{
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
}
|
|
else
|
|
{
|
|
InputBuffer->Mode = (ConsoleMode & CONSOLE_VALID_INPUT_MODES);
|
|
}
|
|
}
|
|
else if (TEXTMODE_BUFFER == Object->Type || GRAPHICS_BUFFER == Object->Type)
|
|
{
|
|
PCONSOLE_SCREEN_BUFFER Buffer = (PCONSOLE_SCREEN_BUFFER)Object;
|
|
|
|
/* Only the presence of valid mode flags is allowed */
|
|
if (ConsoleMode & ~CONSOLE_VALID_OUTPUT_MODES)
|
|
{
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
}
|
|
else
|
|
{
|
|
Buffer->Mode = (ConsoleMode & CONSOLE_VALID_OUTPUT_MODES);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Status = STATUS_INVALID_HANDLE;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS NTAPI
|
|
ConDrvGetConsoleCP(IN PCONSOLE Console,
|
|
OUT PUINT CodePage,
|
|
IN BOOLEAN OutputCP)
|
|
{
|
|
if (Console == NULL || CodePage == NULL)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
*CodePage = (OutputCP ? Console->OutputCodePage : Console->InputCodePage);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS NTAPI
|
|
ConDrvSetConsoleCP(IN PCONSOLE Console,
|
|
IN UINT CodePage,
|
|
IN BOOLEAN OutputCP)
|
|
{
|
|
if (Console == NULL || !IsValidCodePage(CodePage))
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
if (OutputCP)
|
|
Console->OutputCodePage = CodePage;
|
|
else
|
|
Console->InputCodePage = CodePage;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/* EOF */
|