mirror of
https://github.com/reactos/reactos.git
synced 2024-11-19 13:33:42 +00:00
ddb3d908c9
svn path=/branches/shell-experiments/; revision=62286
469 lines
16 KiB
C
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 */
|