reactos/win32ss/user/winsrv/consrv/coninput.c
Amine Khaldi ddb3d908c9 * Sync up to trunk HEAD (r62285). Branch guys deserve the significant speedups too ;)
svn path=/branches/shell-experiments/; revision=62286
2014-02-22 10:31:26 +00:00

469 lines
16 KiB
C

/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS Console Server DLL
* FILE: win32ss/user/winsrv/consrv/coninput.c
* PURPOSE: Console Input functions
* PROGRAMMERS: Jeffrey Morlan
* Hermes Belusca-Maito (hermes.belusca@sfr.fr)
*/
/* INCLUDES *******************************************************************/
#include "consrv.h"
#define NDEBUG
#include <debug.h>
/* GLOBALS ********************************************************************/
#define ConSrvGetInputBuffer(ProcessData, Handle, Ptr, Access, LockConsole) \
ConSrvGetObject((ProcessData), (Handle), (PCONSOLE_IO_OBJECT*)(Ptr), NULL, \
(Access), (LockConsole), INPUT_BUFFER)
#define ConSrvGetInputBufferAndHandleEntry(ProcessData, Handle, Ptr, Entry, Access, LockConsole) \
ConSrvGetObject((ProcessData), (Handle), (PCONSOLE_IO_OBJECT*)(Ptr), (Entry), \
(Access), (LockConsole), INPUT_BUFFER)
#define ConSrvReleaseInputBuffer(Buff, IsConsoleLocked) \
ConSrvReleaseObject(&(Buff)->Header, (IsConsoleLocked))
typedef struct _GET_INPUT_INFO
{
PCSR_THREAD CallingThread; // The thread which called the input API.
PVOID HandleEntry; // The handle data associated with the wait thread.
PCONSOLE_INPUT_BUFFER InputBuffer; // The input buffer corresponding to the handle.
} GET_INPUT_INFO, *PGET_INPUT_INFO;
/* PRIVATE FUNCTIONS **********************************************************/
static NTSTATUS
WaitBeforeReading(IN PGET_INPUT_INFO InputInfo,
IN PCSR_API_MESSAGE ApiMessage,
IN CSR_WAIT_FUNCTION WaitFunction OPTIONAL,
IN BOOLEAN CreateWaitBlock OPTIONAL)
{
if (CreateWaitBlock)
{
PGET_INPUT_INFO CapturedInputInfo;
CapturedInputInfo = ConsoleAllocHeap(0, sizeof(GET_INPUT_INFO));
if (!CapturedInputInfo) return STATUS_NO_MEMORY;
RtlMoveMemory(CapturedInputInfo, InputInfo, sizeof(GET_INPUT_INFO));
if (!CsrCreateWait(&InputInfo->InputBuffer->ReadWaitQueue,
WaitFunction,
InputInfo->CallingThread,
ApiMessage,
CapturedInputInfo))
{
ConsoleFreeHeap(CapturedInputInfo);
return STATUS_NO_MEMORY;
}
}
/* Wait for input */
return STATUS_PENDING;
}
static NTSTATUS
ReadChars(IN PGET_INPUT_INFO InputInfo,
IN PCSR_API_MESSAGE ApiMessage,
IN BOOLEAN CreateWaitBlock OPTIONAL);
// Wait function CSR_WAIT_FUNCTION
static BOOLEAN
NTAPI
ReadCharsThread(IN PLIST_ENTRY WaitList,
IN PCSR_THREAD WaitThread,
IN PCSR_API_MESSAGE WaitApiMessage,
IN PVOID WaitContext,
IN PVOID WaitArgument1,
IN PVOID WaitArgument2,
IN ULONG WaitFlags)
{
NTSTATUS Status;
PGET_INPUT_INFO InputInfo = (PGET_INPUT_INFO)WaitContext;
PVOID InputHandle = WaitArgument2;
DPRINT("ReadCharsThread - WaitContext = 0x%p, WaitArgument1 = 0x%p, WaitArgument2 = 0x%p, WaitFlags = %lu\n", WaitContext, WaitArgument1, WaitArgument2, WaitFlags);
/*
* If we are notified of the process termination via a call
* to CsrNotifyWaitBlock triggered by CsrDestroyProcess or
* CsrDestroyThread, just return.
*/
if (WaitFlags & CsrProcessTerminating)
{
Status = STATUS_THREAD_IS_TERMINATING;
goto Quit;
}
/*
* Somebody is closing a handle to this input buffer,
* by calling ConSrvCloseHandleEntry.
* See whether we are linked to that handle (ie. we
* are a waiter for this handle), and if so, return.
* Otherwise, ignore the call and continue waiting.
*/
if (InputHandle != NULL)
{
Status = (InputHandle == InputInfo->HandleEntry ? STATUS_ALERTED
: STATUS_PENDING);
goto Quit;
}
/*
* If we go there, that means we are notified for some new input.
* The console is therefore already locked.
*/
Status = ReadChars(InputInfo, WaitApiMessage, FALSE);
Quit:
if (Status != STATUS_PENDING)
{
WaitApiMessage->Status = Status;
ConsoleFreeHeap(InputInfo);
}
return (Status == STATUS_PENDING ? FALSE : TRUE);
}
NTSTATUS NTAPI
ConDrvReadConsole(IN PCONSOLE Console,
IN PCONSOLE_INPUT_BUFFER InputBuffer,
IN BOOLEAN Unicode,
OUT PVOID Buffer,
IN OUT PCONSOLE_READCONSOLE_CONTROL ReadControl,
IN ULONG NumCharsToRead,
OUT PULONG NumCharsRead OPTIONAL);
static NTSTATUS
ReadChars(IN PGET_INPUT_INFO InputInfo,
IN PCSR_API_MESSAGE ApiMessage,
IN BOOLEAN CreateWaitBlock OPTIONAL)
{
NTSTATUS Status;
PCONSOLE_READCONSOLE ReadConsoleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.ReadConsoleRequest;
PCONSOLE_INPUT_BUFFER InputBuffer = InputInfo->InputBuffer;
CONSOLE_READCONSOLE_CONTROL ReadControl;
ReadControl.nLength = sizeof(CONSOLE_READCONSOLE_CONTROL);
ReadControl.nInitialChars = ReadConsoleRequest->NrCharactersRead;
ReadControl.dwCtrlWakeupMask = ReadConsoleRequest->CtrlWakeupMask;
ReadControl.dwControlKeyState = ReadConsoleRequest->ControlKeyState;
Status = ConDrvReadConsole(InputBuffer->Header.Console,
InputBuffer,
ReadConsoleRequest->Unicode,
ReadConsoleRequest->Buffer,
&ReadControl,
ReadConsoleRequest->NrCharactersToRead,
&ReadConsoleRequest->NrCharactersRead);
ReadConsoleRequest->ControlKeyState = ReadControl.dwControlKeyState;
if (Status == STATUS_PENDING)
{
/* We haven't completed a read, so start a wait */
return WaitBeforeReading(InputInfo,
ApiMessage,
ReadCharsThread,
CreateWaitBlock);
}
else
{
/* We read all what we wanted, we return the error code we were given */
return Status;
// return STATUS_SUCCESS;
}
}
static NTSTATUS
ReadInputBuffer(IN PGET_INPUT_INFO InputInfo,
IN PCSR_API_MESSAGE ApiMessage,
IN BOOLEAN CreateWaitBlock OPTIONAL);
// Wait function CSR_WAIT_FUNCTION
static BOOLEAN
NTAPI
ReadInputBufferThread(IN PLIST_ENTRY WaitList,
IN PCSR_THREAD WaitThread,
IN PCSR_API_MESSAGE WaitApiMessage,
IN PVOID WaitContext,
IN PVOID WaitArgument1,
IN PVOID WaitArgument2,
IN ULONG WaitFlags)
{
NTSTATUS Status;
PGET_INPUT_INFO InputInfo = (PGET_INPUT_INFO)WaitContext;
PVOID InputHandle = WaitArgument2;
DPRINT("ReadInputBufferThread - WaitContext = 0x%p, WaitArgument1 = 0x%p, WaitArgument2 = 0x%p, WaitFlags = %lu\n", WaitContext, WaitArgument1, WaitArgument2, WaitFlags);
/*
* If we are notified of the process termination via a call
* to CsrNotifyWaitBlock triggered by CsrDestroyProcess or
* CsrDestroyThread, just return.
*/
if (WaitFlags & CsrProcessTerminating)
{
Status = STATUS_THREAD_IS_TERMINATING;
goto Quit;
}
/*
* Somebody is closing a handle to this input buffer,
* by calling ConSrvCloseHandleEntry.
* See whether we are linked to that handle (ie. we
* are a waiter for this handle), and if so, return.
* Otherwise, ignore the call and continue waiting.
*/
if (InputHandle != NULL)
{
Status = (InputHandle == InputInfo->HandleEntry ? STATUS_ALERTED
: STATUS_PENDING);
goto Quit;
}
/*
* If we go there, that means we are notified for some new input.
* The console is therefore already locked.
*/
Status = ReadInputBuffer(InputInfo, WaitApiMessage, FALSE);
Quit:
if (Status != STATUS_PENDING)
{
WaitApiMessage->Status = Status;
ConsoleFreeHeap(InputInfo);
}
return (Status == STATUS_PENDING ? FALSE : TRUE);
}
NTSTATUS NTAPI
ConDrvGetConsoleInput(IN PCONSOLE Console,
IN PCONSOLE_INPUT_BUFFER InputBuffer,
IN BOOLEAN KeepEvents,
IN BOOLEAN WaitForMoreEvents,
IN BOOLEAN Unicode,
OUT PINPUT_RECORD InputRecord,
IN ULONG NumEventsToRead,
OUT PULONG NumEventsRead OPTIONAL);
static NTSTATUS
ReadInputBuffer(IN PGET_INPUT_INFO InputInfo,
IN PCSR_API_MESSAGE ApiMessage,
IN BOOLEAN CreateWaitBlock OPTIONAL)
{
NTSTATUS Status;
PCONSOLE_GETINPUT GetInputRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetInputRequest;
PCONSOLE_INPUT_BUFFER InputBuffer = InputInfo->InputBuffer;
// GetInputRequest->InputsRead = 0;
Status = ConDrvGetConsoleInput(InputBuffer->Header.Console,
InputBuffer,
(GetInputRequest->wFlags & CONSOLE_READ_KEEPEVENT) != 0,
(GetInputRequest->wFlags & CONSOLE_READ_CONTINUE ) == 0,
GetInputRequest->Unicode,
GetInputRequest->InputRecord,
GetInputRequest->Length,
&GetInputRequest->InputsRead);
if (Status == STATUS_PENDING)
{
/* We haven't completed a read, so start a wait */
return WaitBeforeReading(InputInfo,
ApiMessage,
ReadInputBufferThread,
CreateWaitBlock);
}
else
{
/* We read all what we wanted, we return the error code we were given */
return Status;
// return STATUS_SUCCESS;
}
}
/* PUBLIC SERVER APIS *********************************************************/
CSR_API(SrvReadConsole)
{
NTSTATUS Status;
PCONSOLE_READCONSOLE ReadConsoleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.ReadConsoleRequest;
PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process);
PVOID HandleEntry;
PCONSOLE_INPUT_BUFFER InputBuffer;
GET_INPUT_INFO InputInfo;
DPRINT("SrvReadConsole\n");
if (!CsrValidateMessageBuffer(ApiMessage,
(PVOID*)&ReadConsoleRequest->Buffer,
ReadConsoleRequest->BufferSize,
sizeof(BYTE)))
{
return STATUS_INVALID_PARAMETER;
}
if (ReadConsoleRequest->NrCharactersRead > ReadConsoleRequest->NrCharactersToRead)
{
return STATUS_INVALID_PARAMETER;
}
Status = ConSrvGetInputBufferAndHandleEntry(ProcessData, ReadConsoleRequest->InputHandle, &InputBuffer, &HandleEntry, GENERIC_READ, TRUE);
if (!NT_SUCCESS(Status)) return Status;
// This member is set by the caller (IntReadConsole in kernel32)
// ReadConsoleRequest->NrCharactersRead = 0;
InputInfo.CallingThread = CsrGetClientThread();
InputInfo.HandleEntry = HandleEntry;
InputInfo.InputBuffer = InputBuffer;
Status = ReadChars(&InputInfo, ApiMessage, TRUE);
ConSrvReleaseInputBuffer(InputBuffer, TRUE);
if (Status == STATUS_PENDING) *ReplyCode = CsrReplyPending;
return Status;
}
CSR_API(SrvGetConsoleInput)
{
NTSTATUS Status;
PCONSOLE_GETINPUT GetInputRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetInputRequest;
PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process);
PVOID HandleEntry;
PCONSOLE_INPUT_BUFFER InputBuffer;
GET_INPUT_INFO InputInfo;
DPRINT("SrvGetConsoleInput\n");
if (GetInputRequest->wFlags & ~(CONSOLE_READ_KEEPEVENT | CONSOLE_READ_CONTINUE))
return STATUS_INVALID_PARAMETER;
if (!CsrValidateMessageBuffer(ApiMessage,
(PVOID*)&GetInputRequest->InputRecord,
GetInputRequest->Length,
sizeof(INPUT_RECORD)))
{
return STATUS_INVALID_PARAMETER;
}
Status = ConSrvGetInputBufferAndHandleEntry(ProcessData, GetInputRequest->InputHandle, &InputBuffer, &HandleEntry, GENERIC_READ, TRUE);
if (!NT_SUCCESS(Status)) return Status;
GetInputRequest->InputsRead = 0;
InputInfo.CallingThread = CsrGetClientThread();
InputInfo.HandleEntry = HandleEntry;
InputInfo.InputBuffer = InputBuffer;
Status = ReadInputBuffer(&InputInfo, ApiMessage, TRUE);
ConSrvReleaseInputBuffer(InputBuffer, TRUE);
if (Status == STATUS_PENDING) *ReplyCode = CsrReplyPending;
return Status;
}
NTSTATUS NTAPI
ConDrvWriteConsoleInput(IN PCONSOLE Console,
IN PCONSOLE_INPUT_BUFFER InputBuffer,
IN BOOLEAN Unicode,
IN BOOLEAN AppendToEnd,
IN PINPUT_RECORD InputRecord,
IN ULONG NumEventsToWrite,
OUT PULONG NumEventsWritten OPTIONAL);
CSR_API(SrvWriteConsoleInput)
{
NTSTATUS Status;
PCONSOLE_WRITEINPUT WriteInputRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.WriteInputRequest;
PCONSOLE_INPUT_BUFFER InputBuffer;
ULONG NumEventsWritten;
DPRINT("SrvWriteConsoleInput\n");
if (!CsrValidateMessageBuffer(ApiMessage,
(PVOID*)&WriteInputRequest->InputRecord,
WriteInputRequest->Length,
sizeof(INPUT_RECORD)))
{
return STATUS_INVALID_PARAMETER;
}
Status = ConSrvGetInputBuffer(ConsoleGetPerProcessData(CsrGetClientThread()->Process),
WriteInputRequest->InputHandle,
&InputBuffer, GENERIC_WRITE, TRUE);
if (!NT_SUCCESS(Status)) return Status;
NumEventsWritten = 0;
Status = ConDrvWriteConsoleInput(InputBuffer->Header.Console,
InputBuffer,
WriteInputRequest->Unicode,
WriteInputRequest->AppendToEnd,
WriteInputRequest->InputRecord,
WriteInputRequest->Length,
&NumEventsWritten);
WriteInputRequest->Length = NumEventsWritten;
ConSrvReleaseInputBuffer(InputBuffer, TRUE);
return Status;
}
NTSTATUS NTAPI
ConDrvFlushConsoleInputBuffer(IN PCONSOLE Console,
IN PCONSOLE_INPUT_BUFFER InputBuffer);
CSR_API(SrvFlushConsoleInputBuffer)
{
NTSTATUS Status;
PCONSOLE_FLUSHINPUTBUFFER FlushInputBufferRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.FlushInputBufferRequest;
PCONSOLE_INPUT_BUFFER InputBuffer;
DPRINT("SrvFlushConsoleInputBuffer\n");
Status = ConSrvGetInputBuffer(ConsoleGetPerProcessData(CsrGetClientThread()->Process),
FlushInputBufferRequest->InputHandle,
&InputBuffer, GENERIC_WRITE, TRUE);
if (!NT_SUCCESS(Status)) return Status;
Status = ConDrvFlushConsoleInputBuffer(InputBuffer->Header.Console,
InputBuffer);
ConSrvReleaseInputBuffer(InputBuffer, TRUE);
return Status;
}
NTSTATUS NTAPI
ConDrvGetConsoleNumberOfInputEvents(IN PCONSOLE Console,
IN PCONSOLE_INPUT_BUFFER InputBuffer,
OUT PULONG NumEvents);
CSR_API(SrvGetConsoleNumberOfInputEvents)
{
NTSTATUS Status;
PCONSOLE_GETNUMINPUTEVENTS GetNumInputEventsRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetNumInputEventsRequest;
PCONSOLE_INPUT_BUFFER InputBuffer;
DPRINT("SrvGetConsoleNumberOfInputEvents\n");
Status = ConSrvGetInputBuffer(ConsoleGetPerProcessData(CsrGetClientThread()->Process),
GetNumInputEventsRequest->InputHandle,
&InputBuffer, GENERIC_READ, TRUE);
if (!NT_SUCCESS(Status)) return Status;
Status = ConDrvGetConsoleNumberOfInputEvents(InputBuffer->Header.Console,
InputBuffer,
&GetNumInputEventsRequest->NumInputEvents);
ConSrvReleaseInputBuffer(InputBuffer, TRUE);
return Status;
}
/* EOF */