reactos/win32ss/user/winsrv/consrv/condrv/console.c

542 lines
15 KiB
C
Raw Normal View History

/*
* 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<EFBFBD> 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
[CONSRV] - Introduce a "console configuration" library that is used by both CONSRV and the console properties applet so that they can share common code concerning getting/setting console registry properties. - Make use of the Windows-compatible (and undocumented) CONSOLE_STATE_INFO structure for that purpose (as well as the WM_SETCONSOLEINFO): see commits r63819 and r58415 and links within for more details. Note: this structure needs to be 4-byte packed (contrary to what it is said in almost all the links from above. The difference is only visible at the level of the last member that is the ConsoleTitle string array. This was tested on windows). - Simplify some parts of console settings initialization. - Some work is still needed concerning how to correctly retrieve the default console settings (without touching the ConsoleTitle member of CONSOLE_STATE_INFO, contrary to what we do currently). [CONSOLE.DLL] - Make the console properties applet windows-compatible, in the sense that you can now run it on win2k3 and use it instead of the windows one. This implies having the same strange hacks as windows' one, namely, that the window handle parameter of the CPlApplet entry point is either used as the caller (parent) window handle, OR, as a shared section handle to shared data with CONSRV. [KERNEL32] - Rework the console applet initialization accordingly. Also we reload each time the console.dll when opening the console applet, and then unload it: this "allows" to reset all the global variables that console.dll may (and does) have. svn path=/trunk/; revision=66867
2015-03-24 23:58:44 +00:00
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
[CONSRV] - Introduce a "console configuration" library that is used by both CONSRV and the console properties applet so that they can share common code concerning getting/setting console registry properties. - Make use of the Windows-compatible (and undocumented) CONSOLE_STATE_INFO structure for that purpose (as well as the WM_SETCONSOLEINFO): see commits r63819 and r58415 and links within for more details. Note: this structure needs to be 4-byte packed (contrary to what it is said in almost all the links from above. The difference is only visible at the level of the last member that is the ConsoleTitle string array. This was tested on windows). - Simplify some parts of console settings initialization. - Some work is still needed concerning how to correctly retrieve the default console settings (without touching the ConsoleTitle member of CONSOLE_STATE_INFO, contrary to what we do currently). [CONSOLE.DLL] - Make the console properties applet windows-compatible, in the sense that you can now run it on win2k3 and use it instead of the windows one. This implies having the same strange hacks as windows' one, namely, that the window handle parameter of the CPlApplet entry point is either used as the caller (parent) window handle, OR, as a shared section handle to shared data with CONSRV. [KERNEL32] - Rework the console applet initialization accordingly. Also we reload each time the console.dll when opening the console applet, and then unload it: this "allows" to reset all the global variables that console.dll may (and does) have. svn path=/trunk/; revision=66867
2015-03-24 23:58:44 +00:00
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");
[CONSRV] - Introduce a "console configuration" library that is used by both CONSRV and the console properties applet so that they can share common code concerning getting/setting console registry properties. - Make use of the Windows-compatible (and undocumented) CONSOLE_STATE_INFO structure for that purpose (as well as the WM_SETCONSOLEINFO): see commits r63819 and r58415 and links within for more details. Note: this structure needs to be 4-byte packed (contrary to what it is said in almost all the links from above. The difference is only visible at the level of the last member that is the ConsoleTitle string array. This was tested on windows). - Simplify some parts of console settings initialization. - Some work is still needed concerning how to correctly retrieve the default console settings (without touching the ConsoleTitle member of CONSOLE_STATE_INFO, contrary to what we do currently). [CONSOLE.DLL] - Make the console properties applet windows-compatible, in the sense that you can now run it on win2k3 and use it instead of the windows one. This implies having the same strange hacks as windows' one, namely, that the window handle parameter of the CPlApplet entry point is either used as the caller (parent) window handle, OR, as a shared section handle to shared data with CONSRV. [KERNEL32] - Rework the console applet initialization accordingly. Also we reload each time the console.dll when opening the console applet, and then unload it: this "allows" to reset all the global variables that console.dll may (and does) have. svn path=/trunk/; revision=66867
2015-03-24 23:58:44 +00:00
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 */