mirror of
https://github.com/reactos/reactos.git
synced 2024-11-20 14:30:57 +00:00
1106 lines
33 KiB
C
1106 lines
33 KiB
C
/*
|
|
* COPYRIGHT: See COPYING in the top level directory
|
|
* PROJECT: ReactOS Console Driver DLL
|
|
* FILE: win32ss/user/winsrv/consrv_new/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 "include/conio.h"
|
|
#include "include/conio2.h"
|
|
#include "handle.h"
|
|
#include "procinit.h"
|
|
#include "alias.h"
|
|
#include "coninput.h"
|
|
#include "conoutput.h"
|
|
#include "lineinput.h"
|
|
#include "include/settings.h"
|
|
|
|
#include "include/console.h"
|
|
#include "console.h"
|
|
#include "resource.h"
|
|
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
// FIXME: Add this prototype to winternl.h / rtlfuncs.h / ...
|
|
NTSTATUS NTAPI RtlGetLastNtStatus(VOID);
|
|
|
|
|
|
/* GLOBALS ********************************************************************/
|
|
|
|
static ULONG ConsoleListSize;
|
|
static PCONSOLE* ConsoleList; /* The list of all the allocated consoles */
|
|
static RTL_RESOURCE ListLock;
|
|
|
|
#define ConDrvLockConsoleListExclusive() \
|
|
RtlAcquireResourceExclusive(&ListLock, TRUE)
|
|
|
|
#define ConDrvLockConsoleListShared() \
|
|
RtlAcquireResourceShared(&ListLock, TRUE)
|
|
|
|
#define ConDrvUnlockConsoleList() \
|
|
RtlReleaseResource(&ListLock)
|
|
|
|
// Adapted from reactos/lib/rtl/unicode.c, RtlCreateUnicodeString line 2180
|
|
static BOOLEAN
|
|
ConsoleCreateUnicodeString(IN OUT PUNICODE_STRING UniDest,
|
|
IN PCWSTR Source)
|
|
{
|
|
SIZE_T Size = (wcslen(Source) + 1) * sizeof(WCHAR);
|
|
if (Size > MAXUSHORT) return FALSE;
|
|
|
|
UniDest->Buffer = ConsoleAllocHeap(HEAP_ZERO_MEMORY, Size);
|
|
if (UniDest->Buffer == NULL) return FALSE;
|
|
|
|
RtlCopyMemory(UniDest->Buffer, Source, Size);
|
|
UniDest->MaximumLength = (USHORT)Size;
|
|
UniDest->Length = (USHORT)Size - sizeof(WCHAR);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// Adapted from reactos/lib/rtl/unicode.c, RtlFreeUnicodeString line 431
|
|
static VOID
|
|
ConsoleFreeUnicodeString(IN PUNICODE_STRING UnicodeString)
|
|
{
|
|
if (UnicodeString->Buffer)
|
|
{
|
|
ConsoleFreeHeap(UnicodeString->Buffer);
|
|
RtlZeroMemory(UnicodeString, sizeof(UNICODE_STRING));
|
|
}
|
|
}
|
|
|
|
|
|
static NTSTATUS
|
|
InsertConsole(OUT PHANDLE Handle,
|
|
IN PCONSOLE Console)
|
|
{
|
|
#define CONSOLE_HANDLES_INCREMENT 2 * 3
|
|
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
ULONG i = 0;
|
|
PCONSOLE* Block;
|
|
|
|
ASSERT( (ConsoleList == NULL && ConsoleListSize == 0) ||
|
|
(ConsoleList != NULL && ConsoleListSize != 0) );
|
|
|
|
/* All went right, so add the console to the list */
|
|
ConDrvLockConsoleListExclusive();
|
|
DPRINT1("Insert in the list\n");
|
|
|
|
if (ConsoleList)
|
|
{
|
|
for (i = 0; i < ConsoleListSize; i++)
|
|
{
|
|
if (ConsoleList[i] == NULL) break;
|
|
}
|
|
}
|
|
|
|
if (i >= ConsoleListSize)
|
|
{
|
|
DPRINT1("Creation of a new handles table\n");
|
|
/* Allocate a new handles table */
|
|
Block = ConsoleAllocHeap(HEAP_ZERO_MEMORY,
|
|
(ConsoleListSize +
|
|
CONSOLE_HANDLES_INCREMENT) * sizeof(PCONSOLE));
|
|
if (Block == NULL)
|
|
{
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
goto Quit;
|
|
}
|
|
|
|
/* If we previously had a handles table, free it and use the new one */
|
|
if (ConsoleList)
|
|
{
|
|
/* Copy the handles from the old table to the new one */
|
|
RtlCopyMemory(Block,
|
|
ConsoleList,
|
|
ConsoleListSize * sizeof(PCONSOLE));
|
|
ConsoleFreeHeap(ConsoleList);
|
|
}
|
|
ConsoleList = Block;
|
|
ConsoleListSize += CONSOLE_HANDLES_INCREMENT;
|
|
}
|
|
|
|
ConsoleList[i] = Console;
|
|
*Handle = ULongToHandle((i << 2) | 0x3);
|
|
|
|
Quit:
|
|
/* Unlock the console list and return status */
|
|
ConDrvUnlockConsoleList();
|
|
return Status;
|
|
}
|
|
|
|
/* Unused */
|
|
#if 0
|
|
static NTSTATUS
|
|
RemoveConsoleByHandle(IN HANDLE Handle)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
ULONG Index = HandleToULong(Handle) >> 2;
|
|
PCONSOLE Console;
|
|
|
|
ASSERT( (ConsoleList == NULL && ConsoleListSize == 0) ||
|
|
(ConsoleList != NULL && ConsoleListSize != 0) );
|
|
|
|
/* Remove the console from the list */
|
|
ConDrvLockConsoleListExclusive();
|
|
|
|
if (Index >= ConsoleListSize ||
|
|
(Console = ConsoleList[Index]) == NULL)
|
|
{
|
|
Status = STATUS_INVALID_HANDLE;
|
|
goto Quit;
|
|
}
|
|
|
|
ConsoleList[Index] = NULL;
|
|
|
|
Quit:
|
|
/* Unlock the console list and return status */
|
|
ConDrvUnlockConsoleList();
|
|
return Status;
|
|
}
|
|
#endif
|
|
|
|
static NTSTATUS
|
|
RemoveConsoleByPointer(IN PCONSOLE Console)
|
|
{
|
|
ULONG i = 0;
|
|
|
|
if (!Console) return STATUS_INVALID_PARAMETER;
|
|
|
|
ASSERT( (ConsoleList == NULL && ConsoleListSize == 0) ||
|
|
(ConsoleList != NULL && ConsoleListSize != 0) );
|
|
|
|
/* Remove the console from the list */
|
|
ConDrvLockConsoleListExclusive();
|
|
|
|
if (ConsoleList)
|
|
{
|
|
for (i = 0; i < ConsoleListSize; i++)
|
|
{
|
|
if (ConsoleList[i] == Console) ConsoleList[i] = NULL;
|
|
}
|
|
}
|
|
|
|
/* Unlock the console list */
|
|
ConDrvUnlockConsoleList();
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
/* For resetting the frontend - defined in dummyfrontend.c */
|
|
VOID ResetFrontEnd(IN PCONSOLE Console);
|
|
|
|
|
|
/* PRIVATE FUNCTIONS **********************************************************/
|
|
|
|
static NTSTATUS
|
|
ConDrvConsoleCtrlEventTimeout(IN ULONG Event,
|
|
IN PCONSOLE_PROCESS_DATA ProcessData,
|
|
IN ULONG Timeout)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
DPRINT("ConDrvConsoleCtrlEventTimeout Parent ProcessId = %x\n", ProcessData->Process->ClientId.UniqueProcess);
|
|
|
|
if (ProcessData->CtrlDispatcher)
|
|
{
|
|
_SEH2_TRY
|
|
{
|
|
HANDLE Thread = NULL;
|
|
|
|
_SEH2_TRY
|
|
{
|
|
Thread = CreateRemoteThread(ProcessData->Process->ProcessHandle, NULL, 0,
|
|
ProcessData->CtrlDispatcher,
|
|
UlongToPtr(Event), 0, NULL);
|
|
if (NULL == Thread)
|
|
{
|
|
Status = RtlGetLastNtStatus();
|
|
DPRINT1("Failed thread creation, Status = 0x%08lx\n", Status);
|
|
}
|
|
else
|
|
{
|
|
DPRINT("ProcessData->CtrlDispatcher remote thread creation succeeded, ProcessId = %x, Process = 0x%p\n", ProcessData->Process->ClientId.UniqueProcess, ProcessData->Process);
|
|
WaitForSingleObject(Thread, Timeout);
|
|
}
|
|
}
|
|
_SEH2_FINALLY
|
|
{
|
|
CloseHandle(Thread);
|
|
}
|
|
_SEH2_END;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
Status = _SEH2_GetExceptionCode();
|
|
DPRINT1("ConDrvConsoleCtrlEventTimeout - Caught an exception, Status = 0x%08lx\n", Status);
|
|
}
|
|
_SEH2_END;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
static NTSTATUS
|
|
ConDrvConsoleCtrlEvent(IN ULONG Event,
|
|
IN PCONSOLE_PROCESS_DATA ProcessData)
|
|
{
|
|
return ConDrvConsoleCtrlEventTimeout(Event, ProcessData, 0);
|
|
}
|
|
|
|
VOID FASTCALL
|
|
ConioPause(PCONSOLE Console, UINT Flags)
|
|
{
|
|
Console->PauseFlags |= Flags;
|
|
if (!Console->UnpauseEvent)
|
|
Console->UnpauseEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
}
|
|
|
|
VOID FASTCALL
|
|
ConioUnpause(PCONSOLE Console, UINT Flags)
|
|
{
|
|
Console->PauseFlags &= ~Flags;
|
|
|
|
// if ((Console->PauseFlags & (PAUSED_FROM_KEYBOARD | PAUSED_FROM_SCROLLBAR | PAUSED_FROM_SELECTION)) == 0)
|
|
if (Console->PauseFlags == 0 && Console->UnpauseEvent)
|
|
{
|
|
SetEvent(Console->UnpauseEvent);
|
|
CloseHandle(Console->UnpauseEvent);
|
|
Console->UnpauseEvent = NULL;
|
|
|
|
CsrNotifyWait(&Console->WriteWaitQueue,
|
|
WaitAll,
|
|
NULL,
|
|
NULL);
|
|
if (!IsListEmpty(&Console->WriteWaitQueue))
|
|
{
|
|
CsrDereferenceWait(&Console->WriteWaitQueue);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* 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;
|
|
}
|
|
|
|
BOOLEAN NTAPI
|
|
ConDrvValidateConsole(OUT PCONSOLE* Console,
|
|
IN HANDLE ConsoleHandle,
|
|
IN CONSOLE_STATE ExpectedState,
|
|
IN BOOLEAN LockConsole)
|
|
{
|
|
BOOLEAN RetVal = FALSE;
|
|
|
|
ULONG Index = HandleToULong(ConsoleHandle) >> 2;
|
|
PCONSOLE ValidatedConsole;
|
|
|
|
if (!Console) return FALSE;
|
|
*Console = NULL;
|
|
|
|
/*
|
|
* Forbid creation or deletion of consoles when
|
|
* checking for the existence of a console.
|
|
*/
|
|
ConDrvLockConsoleListShared();
|
|
|
|
if (Index >= ConsoleListSize ||
|
|
(ValidatedConsole = ConsoleList[Index]) == NULL)
|
|
{
|
|
/* Unlock the console list */
|
|
ConDrvUnlockConsoleList();
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
ValidatedConsole = ConsoleList[Index];
|
|
|
|
/* Unlock the console list and return */
|
|
ConDrvUnlockConsoleList();
|
|
|
|
RetVal = ConDrvValidateConsoleUnsafe(ValidatedConsole,
|
|
ExpectedState,
|
|
LockConsole);
|
|
if (RetVal) *Console = ValidatedConsole;
|
|
|
|
return RetVal;
|
|
}
|
|
|
|
NTSTATUS NTAPI
|
|
ConDrvGetConsole(OUT PCONSOLE* Console,
|
|
IN HANDLE ConsoleHandle,
|
|
IN BOOLEAN LockConsole)
|
|
{
|
|
NTSTATUS Status = STATUS_INVALID_HANDLE;
|
|
PCONSOLE GrabConsole;
|
|
|
|
if (Console == NULL) return STATUS_INVALID_PARAMETER;
|
|
*Console = NULL;
|
|
|
|
if (ConDrvValidateConsole(&GrabConsole,
|
|
ConsoleHandle,
|
|
CONSOLE_RUNNING,
|
|
LockConsole))
|
|
{
|
|
InterlockedIncrement(&GrabConsole->ReferenceCount);
|
|
*Console = GrabConsole;
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
VOID NTAPI
|
|
ConDrvReleaseConsole(IN PCONSOLE Console,
|
|
IN BOOLEAN WasConsoleLocked)
|
|
{
|
|
LONG RefCount = 0;
|
|
|
|
if (!Console) return;
|
|
// if (Console->ReferenceCount == 0) return; // This shouldn't happen
|
|
ASSERT(Console->ReferenceCount > 0);
|
|
|
|
/* The console must be locked */
|
|
// ASSERT(Console_locked);
|
|
|
|
/*
|
|
* Decrement the reference count. Save the new value too,
|
|
* because Console->ReferenceCount might be modified after
|
|
* the console gets unlocked but before we check whether we
|
|
* can destroy it.
|
|
*/
|
|
RefCount = _InterlockedDecrement(&Console->ReferenceCount);
|
|
|
|
/* Unlock the console if needed */
|
|
if (WasConsoleLocked) LeaveCriticalSection(&Console->Lock);
|
|
|
|
/* Delete the console if needed */
|
|
if (RefCount <= 0) ConDrvDeleteConsole(Console);
|
|
}
|
|
|
|
|
|
/* CONSOLE INITIALIZATION FUNCTIONS *******************************************/
|
|
|
|
VOID NTAPI
|
|
ConDrvInitConsoleSupport(VOID)
|
|
{
|
|
DPRINT("CONSRV: ConDrvInitConsoleSupport()\n");
|
|
|
|
/* Initialize the console list and its lock */
|
|
ConsoleListSize = 0;
|
|
ConsoleList = NULL;
|
|
RtlInitializeResource(&ListLock);
|
|
|
|
/* Should call LoadKeyboardLayout */
|
|
}
|
|
|
|
NTSTATUS NTAPI
|
|
ConDrvInitConsole(OUT PHANDLE NewConsoleHandle,
|
|
OUT PCONSOLE* NewConsole,
|
|
IN PCONSOLE_INFO ConsoleInfo,
|
|
IN ULONG ConsoleLeaderProcessId)
|
|
{
|
|
NTSTATUS Status;
|
|
SECURITY_ATTRIBUTES SecurityAttributes;
|
|
// CONSOLE_INFO CapturedConsoleInfo;
|
|
TEXTMODE_BUFFER_INFO ScreenBufferInfo;
|
|
HANDLE ConsoleHandle;
|
|
PCONSOLE Console;
|
|
PCONSOLE_SCREEN_BUFFER NewBuffer;
|
|
// WCHAR DefaultTitle[128];
|
|
|
|
if (NewConsoleHandle == NULL || NewConsole == NULL || ConsoleInfo == NULL)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
*NewConsoleHandle = NULL;
|
|
*NewConsole = NULL;
|
|
|
|
/*
|
|
* Allocate a console structure
|
|
*/
|
|
Console = ConsoleAllocHeap(HEAP_ZERO_MEMORY, sizeof(CONSOLE));
|
|
if (NULL == Console)
|
|
{
|
|
DPRINT1("Not enough memory for console creation.\n");
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
/*
|
|
* Load the console settings
|
|
*/
|
|
|
|
/* 1. Load the default settings */
|
|
// ConSrvGetDefaultSettings(ConsoleInfo, ProcessId);
|
|
|
|
// /* 2. Get the title of the console (initialize ConsoleInfo.ConsoleTitle) */
|
|
// Length = min(wcslen(ConsoleStartInfo->ConsoleTitle),
|
|
// sizeof(ConsoleInfo.ConsoleTitle) / sizeof(ConsoleInfo.ConsoleTitle[0]) - 1);
|
|
// wcsncpy(ConsoleInfo.ConsoleTitle, ConsoleStartInfo->ConsoleTitle, Length);
|
|
// ConsoleInfo.ConsoleTitle[Length] = L'\0';
|
|
|
|
/*
|
|
* 4. Load the remaining console settings via the registry.
|
|
*/
|
|
#if 0
|
|
if ((ConsoleStartInfo->dwStartupFlags & STARTF_TITLEISLINKNAME) == 0)
|
|
{
|
|
/*
|
|
* Either we weren't created by an app launched via a shell-link,
|
|
* or we failed to load shell-link console properties.
|
|
* Therefore, load the console infos for the application from the registry.
|
|
*/
|
|
ConSrvReadUserSettings(ConsoleInfo, ProcessId);
|
|
|
|
/*
|
|
* Now, update them with the properties the user might gave to us
|
|
* via the STARTUPINFO structure before calling CreateProcess
|
|
* (and which was transmitted via the ConsoleStartInfo structure).
|
|
* We therefore overwrite the values read in the registry.
|
|
*/
|
|
if (ConsoleStartInfo->dwStartupFlags & STARTF_USEFILLATTRIBUTE)
|
|
{
|
|
ConsoleInfo->ScreenAttrib = (USHORT)ConsoleStartInfo->FillAttribute;
|
|
}
|
|
if (ConsoleStartInfo->dwStartupFlags & STARTF_USECOUNTCHARS)
|
|
{
|
|
ConsoleInfo->ScreenBufferSize = ConsoleStartInfo->ScreenBufferSize;
|
|
}
|
|
if (ConsoleStartInfo->dwStartupFlags & STARTF_USESIZE)
|
|
{
|
|
// ConsoleInfo->ConsoleSize = ConsoleStartInfo->ConsoleWindowSize;
|
|
ConsoleInfo->ConsoleSize.X = (SHORT)ConsoleStartInfo->ConsoleWindowSize.cx;
|
|
ConsoleInfo->ConsoleSize.Y = (SHORT)ConsoleStartInfo->ConsoleWindowSize.cy;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* 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);
|
|
InitializeListHead(&Console->ProcessList);
|
|
|
|
/* Initialize the frontend interface */
|
|
ResetFrontEnd(Console);
|
|
|
|
memcpy(Console->Colors, ConsoleInfo->Colors, sizeof(ConsoleInfo->Colors));
|
|
Console->ConsoleSize = ConsoleInfo->ConsoleSize;
|
|
Console->FixedSize = FALSE; // Value by default; is reseted by the front-ends if needed.
|
|
|
|
/*
|
|
* Initialize the input buffer
|
|
*/
|
|
ConSrvInitObject(&Console->InputBuffer.Header, INPUT_BUFFER, Console);
|
|
|
|
SecurityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
|
|
SecurityAttributes.lpSecurityDescriptor = NULL;
|
|
SecurityAttributes.bInheritHandle = TRUE;
|
|
Console->InputBuffer.ActiveEvent = CreateEventW(&SecurityAttributes, TRUE, FALSE, NULL);
|
|
if (NULL == Console->InputBuffer.ActiveEvent)
|
|
{
|
|
DeleteCriticalSection(&Console->Lock);
|
|
ConsoleFreeHeap(Console);
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
Console->InputBuffer.InputBufferSize = 0; // FIXME!
|
|
InitializeListHead(&Console->InputBuffer.InputEvents);
|
|
InitializeListHead(&Console->InputBuffer.ReadWaitQueue);
|
|
Console->InputBuffer.Mode = ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT |
|
|
ENABLE_ECHO_INPUT | ENABLE_MOUSE_INPUT;
|
|
|
|
Console->QuickEdit = ConsoleInfo->QuickEdit;
|
|
Console->InsertMode = ConsoleInfo->InsertMode;
|
|
Console->LineBuffer = NULL;
|
|
Console->LineMaxSize = Console->LineSize = Console->LinePos = 0;
|
|
Console->LineComplete = Console->LineUpPressed = Console->LineInsertToggle = FALSE;
|
|
// LineWakeupMask
|
|
|
|
// FIXME: This is terminal-specific !! VV
|
|
RtlZeroMemory(&Console->Selection, sizeof(CONSOLE_SELECTION_INFO));
|
|
Console->Selection.dwFlags = CONSOLE_NO_SELECTION;
|
|
// dwSelectionCursor
|
|
|
|
/* Set-up the code page */
|
|
Console->CodePage = 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,
|
|
CONSOLE_TEXTMODE_BUFFER,
|
|
&ScreenBufferInfo);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("ConDrvCreateScreenBuffer: failed, Status = 0x%08lx\n", Status);
|
|
CloseHandle(Console->InputBuffer.ActiveEvent);
|
|
DeleteCriticalSection(&Console->Lock);
|
|
ConsoleFreeHeap(Console);
|
|
return Status;
|
|
}
|
|
/* Make the new screen buffer active */
|
|
Console->ActiveBuffer = NewBuffer;
|
|
InitializeListHead(&Console->WriteWaitQueue);
|
|
Console->PauseFlags = 0;
|
|
Console->UnpauseEvent = NULL;
|
|
|
|
/*
|
|
* Initialize the alias and history buffers
|
|
*/
|
|
Console->Aliases = NULL;
|
|
InitializeListHead(&Console->HistoryBuffers);
|
|
Console->HistoryBufferSize = ConsoleInfo->HistoryBufferSize;
|
|
Console->NumberOfHistoryBuffers = ConsoleInfo->NumberOfHistoryBuffers;
|
|
Console->HistoryNoDup = ConsoleInfo->HistoryNoDup;
|
|
|
|
/* Initialize the console title */
|
|
ConsoleCreateUnicodeString(&Console->OriginalTitle, ConsoleInfo->ConsoleTitle);
|
|
// if (ConsoleInfo.ConsoleTitle[0] == L'\0')
|
|
// {
|
|
// if (LoadStringW(ConSrvDllInstance, IDS_CONSOLE_TITLE, DefaultTitle, sizeof(DefaultTitle) / sizeof(DefaultTitle[0])))
|
|
// {
|
|
// ConsoleCreateUnicodeString(&Console->Title, DefaultTitle);
|
|
// }
|
|
// else
|
|
// {
|
|
// ConsoleCreateUnicodeString(&Console->Title, L"ReactOS Console");
|
|
// }
|
|
// }
|
|
// else
|
|
// {
|
|
ConsoleCreateUnicodeString(&Console->Title, ConsoleInfo->ConsoleTitle);
|
|
// }
|
|
|
|
/* Lock the console until its initialization is finished */
|
|
// EnterCriticalSection(&Console->Lock);
|
|
|
|
DPRINT("Console initialized\n");
|
|
|
|
/* All went right, so add the console to the list */
|
|
Status = InsertConsole(&ConsoleHandle, Console);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* Fail */
|
|
ConDrvDeleteConsole(Console);
|
|
return Status;
|
|
}
|
|
|
|
/* The initialization is finished */
|
|
DPRINT("Change state\n");
|
|
Console->State = CONSOLE_RUNNING;
|
|
|
|
/* Unlock the console */
|
|
// LeaveCriticalSection(&Console->Lock);
|
|
|
|
/* Return the newly created console to the caller and a success code too */
|
|
*NewConsoleHandle = ConsoleHandle;
|
|
*NewConsole = Console;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS NTAPI
|
|
ConDrvRegisterFrontEnd(IN PCONSOLE Console,
|
|
IN PFRONTEND FrontEnd)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
if (Console == NULL || FrontEnd == NULL)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
/* FIXME: Lock the console before ?? */
|
|
|
|
/*
|
|
* Attach the frontend to the console. Use now the TermIFace of the console,
|
|
* and not the user-defined temporary FrontEnd pointer.
|
|
*/
|
|
Console->TermIFace = *FrontEnd;
|
|
Console->TermIFace.Console = Console;
|
|
|
|
/* Initialize the frontend AFTER having attached it to the console */
|
|
DPRINT("Finish initialization of frontend\n");
|
|
Status = Console->TermIFace.Vtbl->InitFrontEnd(&Console->TermIFace, Console);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("FrontEnd initialization failed, Status = 0x%08lx\n", Status);
|
|
|
|
/* We failed, detach the frontend from the console */
|
|
FrontEnd->Console = NULL; // For the caller
|
|
ResetFrontEnd(Console);
|
|
|
|
return Status;
|
|
}
|
|
|
|
/* Copy buffer contents to screen */
|
|
// FrontEnd.Draw();
|
|
// ConioDrawConsole(Console);
|
|
DPRINT("Console drawn\n");
|
|
|
|
DPRINT("Terminal FrontEnd initialization done\n");
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS NTAPI
|
|
ConDrvDeregisterFrontEnd(IN PCONSOLE Console)
|
|
{
|
|
if (Console == NULL) return STATUS_INVALID_PARAMETER;
|
|
|
|
/* FIXME: Lock the console before ?? */
|
|
|
|
/* Deinitialize the frontend BEFORE detaching it from the console */
|
|
Console->TermIFace.Vtbl->DeinitFrontEnd(&Console->TermIFace/*, Console*/);
|
|
|
|
/*
|
|
* Detach the frontend from the console:
|
|
* reinitialize the frontend interface.
|
|
*/
|
|
ResetFrontEnd(Console);
|
|
|
|
DPRINT("Terminal FrontEnd 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
|
|
* ConDrvValidateConsole(Unsafe) 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();
|
|
|
|
/* FIXME: Send a terminate message to all the processes owning this console */
|
|
|
|
/* Cleanup the UI-oriented part */
|
|
DPRINT("Deregister console\n");
|
|
ConDrvDeregisterFrontEnd(Console);
|
|
DPRINT("Console 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 */
|
|
RemoveConsoleByPointer(Console);
|
|
|
|
/* Discard all entries in the input event queue */
|
|
PurgeInputBuffer(Console);
|
|
|
|
if (Console->LineBuffer) ConsoleFreeHeap(Console->LineBuffer);
|
|
|
|
IntDeleteAllAliases(Console);
|
|
HistoryDeleteBuffers(Console);
|
|
|
|
ConioDeleteScreenBuffer(Console->ActiveBuffer);
|
|
Console->ActiveBuffer = NULL;
|
|
if (!IsListEmpty(&Console->BufferList))
|
|
{
|
|
DPRINT1("BUG: screen buffer list not empty\n");
|
|
ASSERT(FALSE);
|
|
}
|
|
|
|
/**/ CloseHandle(Console->InputBuffer.ActiveEvent); /**/
|
|
if (Console->UnpauseEvent) CloseHandle(Console->UnpauseEvent);
|
|
|
|
ConsoleFreeUnicodeString(&Console->OriginalTitle);
|
|
ConsoleFreeUnicodeString(&Console->Title);
|
|
|
|
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;
|
|
|
|
if (Console->QuickEdit || Console->InsertMode)
|
|
{
|
|
// Windows does this, even if it's not documented on MSDN
|
|
*ConsoleMode |= ENABLE_EXTENDED_FLAGS;
|
|
|
|
if (Console->QuickEdit ) *ConsoleMode |= ENABLE_QUICK_EDIT_MODE;
|
|
if (Console->InsertMode) *ConsoleMode |= ENABLE_INSERT_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_CONTROL_MODES ( ENABLE_EXTENDED_FLAGS | ENABLE_INSERT_MODE | ENABLE_QUICK_EDIT_MODE )
|
|
#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;
|
|
|
|
DPRINT("SetConsoleMode(Input, %d)\n", ConsoleMode);
|
|
|
|
/*
|
|
* 1. Only the presence of valid mode flags is allowed.
|
|
*/
|
|
if (ConsoleMode & ~(CONSOLE_VALID_INPUT_MODES | CONSOLE_VALID_CONTROL_MODES))
|
|
{
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Quit;
|
|
}
|
|
|
|
/*
|
|
* 2. If we use control mode flags without ENABLE_EXTENDED_FLAGS,
|
|
* then consider the flags invalid.
|
|
*
|
|
if ( (ConsoleMode & CONSOLE_VALID_CONTROL_MODES) &&
|
|
(ConsoleMode & ENABLE_EXTENDED_FLAGS) == 0 )
|
|
{
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Quit;
|
|
}
|
|
*/
|
|
|
|
/*
|
|
* 3. Now we can continue.
|
|
*/
|
|
if (ConsoleMode & CONSOLE_VALID_CONTROL_MODES)
|
|
{
|
|
Console->QuickEdit = !!(ConsoleMode & ENABLE_QUICK_EDIT_MODE);
|
|
Console->InsertMode = !!(ConsoleMode & ENABLE_INSERT_MODE);
|
|
}
|
|
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;
|
|
|
|
DPRINT("SetConsoleMode(Output, %d)\n", ConsoleMode);
|
|
|
|
if (ConsoleMode & ~CONSOLE_VALID_OUTPUT_MODES)
|
|
{
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
}
|
|
else
|
|
{
|
|
Buffer->Mode = (ConsoleMode & CONSOLE_VALID_OUTPUT_MODES);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Status = STATUS_INVALID_HANDLE;
|
|
}
|
|
|
|
Quit:
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS NTAPI
|
|
ConDrvGetConsoleTitle(IN PCONSOLE Console,
|
|
IN OUT PWCHAR Title,
|
|
IN OUT PULONG BufLength)
|
|
{
|
|
ULONG Length;
|
|
|
|
if (Console == NULL || Title == NULL || BufLength == NULL)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
/* Copy title of the console to the user title buffer */
|
|
if (*BufLength >= sizeof(WCHAR))
|
|
{
|
|
Length = min(*BufLength - sizeof(WCHAR), Console->Title.Length);
|
|
RtlCopyMemory(Title, Console->Title.Buffer, Length);
|
|
Title[Length / sizeof(WCHAR)] = L'\0';
|
|
}
|
|
|
|
*BufLength = Console->Title.Length;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS NTAPI
|
|
ConDrvSetConsoleTitle(IN PCONSOLE Console,
|
|
IN PWCHAR Title,
|
|
IN ULONG BufLength)
|
|
{
|
|
PWCHAR Buffer;
|
|
|
|
if (Console == NULL || Title == NULL)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
/* Allocate a new buffer to hold the new title (NULL-terminated) */
|
|
Buffer = ConsoleAllocHeap(0, BufLength + sizeof(WCHAR));
|
|
if (!Buffer) return STATUS_NO_MEMORY;
|
|
|
|
/* Free the old title */
|
|
ConsoleFreeUnicodeString(&Console->Title);
|
|
|
|
/* Copy title to console */
|
|
Console->Title.Buffer = Buffer;
|
|
Console->Title.Length = BufLength;
|
|
Console->Title.MaximumLength = Console->Title.Length + sizeof(WCHAR);
|
|
RtlCopyMemory(Console->Title.Buffer, Title, Console->Title.Length);
|
|
Console->Title.Buffer[Console->Title.Length / sizeof(WCHAR)] = L'\0';
|
|
|
|
// ConioChangeTitle(Console);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS NTAPI
|
|
ConDrvGetConsoleCP(IN PCONSOLE Console,
|
|
OUT PUINT CodePage,
|
|
IN BOOLEAN InputCP)
|
|
{
|
|
if (Console == NULL || CodePage == NULL)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
*CodePage = (InputCP ? Console->CodePage : Console->OutputCodePage);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS NTAPI
|
|
ConDrvSetConsoleCP(IN PCONSOLE Console,
|
|
IN UINT CodePage,
|
|
IN BOOLEAN InputCP)
|
|
{
|
|
if (Console == NULL || !IsValidCodePage(CodePage))
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
if (InputCP)
|
|
Console->CodePage = CodePage;
|
|
else
|
|
Console->OutputCodePage = CodePage;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS NTAPI
|
|
ConDrvGetConsoleProcessList(IN PCONSOLE Console,
|
|
IN OUT PULONG ProcessIdsList,
|
|
IN ULONG MaxIdListItems,
|
|
OUT PULONG ProcessIdsTotal)
|
|
{
|
|
PCONSOLE_PROCESS_DATA current;
|
|
PLIST_ENTRY current_entry;
|
|
|
|
if (Console == NULL || ProcessIdsList == NULL || ProcessIdsTotal == NULL)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
*ProcessIdsTotal = 0;
|
|
|
|
for (current_entry = Console->ProcessList.Flink;
|
|
current_entry != &Console->ProcessList;
|
|
current_entry = current_entry->Flink)
|
|
{
|
|
current = CONTAINING_RECORD(current_entry, CONSOLE_PROCESS_DATA, ConsoleLink);
|
|
if (++(*ProcessIdsTotal) <= MaxIdListItems)
|
|
{
|
|
*ProcessIdsList++ = HandleToUlong(current->Process->ClientId.UniqueProcess);
|
|
}
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
// ConDrvGenerateConsoleCtrlEvent
|
|
NTSTATUS NTAPI
|
|
ConDrvConsoleProcessCtrlEvent(IN PCONSOLE Console,
|
|
IN ULONG ProcessGroupId,
|
|
IN ULONG Event)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PLIST_ENTRY current_entry;
|
|
PCONSOLE_PROCESS_DATA current;
|
|
|
|
/* If the console is already being destroyed, just return */
|
|
if (!ConDrvValidateConsoleState(Console, CONSOLE_RUNNING))
|
|
return STATUS_UNSUCCESSFUL;
|
|
|
|
/*
|
|
* Loop through the process list, from the most recent process
|
|
* (the active one) to the oldest one (the first created, i.e.
|
|
* the console leader process), and for each, send an event
|
|
* (new processes are inserted at the head of the console process list).
|
|
*/
|
|
current_entry = Console->ProcessList.Flink;
|
|
while (current_entry != &Console->ProcessList)
|
|
{
|
|
current = CONTAINING_RECORD(current_entry, CONSOLE_PROCESS_DATA, ConsoleLink);
|
|
current_entry = current_entry->Flink;
|
|
|
|
/*
|
|
* Only processes belonging to the same process group are signaled.
|
|
* If the process group ID is zero, then all the processes are signaled.
|
|
*/
|
|
if (ProcessGroupId == 0 || current->Process->ProcessGroupId == ProcessGroupId)
|
|
{
|
|
Status = ConDrvConsoleCtrlEvent(Event, current);
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/* EOF */
|