mirror of
https://github.com/reactos/reactos.git
synced 2024-11-18 13:01:40 +00:00
9261110760
All the per-API message structure unpacking and console validation done with ConSrvGetConsole() is now automatically generated using the macros, so that the actual implementation of each API can be done independently of how the console object is obtained. This could also allow reusing these API implementations with a different mechanism for obtaining the console without changing anything in them.
854 lines
29 KiB
C
854 lines
29 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))
|
|
|
|
|
|
/*
|
|
* From MSDN:
|
|
* "The lpMultiByteStr and lpWideCharStr pointers must not be the same.
|
|
* If they are the same, the function fails, and GetLastError returns
|
|
* ERROR_INVALID_PARAMETER."
|
|
*/
|
|
#define ConsoleInputUnicodeToAnsiChar(Console, dChar, sWChar) \
|
|
do { \
|
|
ASSERT((ULONG_PTR)(dChar) != (ULONG_PTR)(sWChar)); \
|
|
WideCharToMultiByte((Console)->InputCodePage, 0, (sWChar), 1, (dChar), 1, NULL, NULL); \
|
|
} while (0)
|
|
|
|
#define ConsoleInputAnsiToUnicodeChar(Console, dWChar, sChar) \
|
|
do { \
|
|
ASSERT((ULONG_PTR)(dWChar) != (ULONG_PTR)(sChar)); \
|
|
MultiByteToWideChar((Console)->InputCodePage, 0, (sChar), 1, (dWChar), 1); \
|
|
} while (0)
|
|
|
|
|
|
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 VOID
|
|
ConioInputEventToAnsi(PCONSOLE Console, PINPUT_RECORD InputEvent)
|
|
{
|
|
if (InputEvent->EventType == KEY_EVENT)
|
|
{
|
|
WCHAR UnicodeChar = InputEvent->Event.KeyEvent.uChar.UnicodeChar;
|
|
InputEvent->Event.KeyEvent.uChar.UnicodeChar = 0;
|
|
ConsoleInputUnicodeToAnsiChar(Console,
|
|
&InputEvent->Event.KeyEvent.uChar.AsciiChar,
|
|
&UnicodeChar);
|
|
}
|
|
}
|
|
|
|
static VOID
|
|
ConioInputEventToUnicode(PCONSOLE Console, PINPUT_RECORD InputEvent)
|
|
{
|
|
if (InputEvent->EventType == KEY_EVENT)
|
|
{
|
|
CHAR AsciiChar = InputEvent->Event.KeyEvent.uChar.AsciiChar;
|
|
InputEvent->Event.KeyEvent.uChar.AsciiChar = 0;
|
|
ConsoleInputAnsiToUnicodeChar(Console,
|
|
&InputEvent->Event.KeyEvent.uChar.UnicodeChar,
|
|
&AsciiChar);
|
|
}
|
|
}
|
|
|
|
static ULONG
|
|
PreprocessInput(PCONSRV_CONSOLE Console,
|
|
PINPUT_RECORD InputEvent,
|
|
ULONG NumEventsToWrite)
|
|
{
|
|
ULONG NumEvents;
|
|
|
|
/*
|
|
* Loop each event, and for each, check for pause or unpause
|
|
* and perform adequate behaviour.
|
|
*/
|
|
for (NumEvents = NumEventsToWrite; NumEvents > 0; --NumEvents)
|
|
{
|
|
/* Check for pause or unpause */
|
|
if (InputEvent->EventType == KEY_EVENT && InputEvent->Event.KeyEvent.bKeyDown)
|
|
{
|
|
WORD vk = InputEvent->Event.KeyEvent.wVirtualKeyCode;
|
|
if (!(Console->PauseFlags & PAUSED_FROM_KEYBOARD))
|
|
{
|
|
DWORD cks = InputEvent->Event.KeyEvent.dwControlKeyState;
|
|
if (Console->InputBuffer.Mode & ENABLE_LINE_INPUT &&
|
|
(vk == VK_PAUSE ||
|
|
(vk == 'S' && (cks & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) &&
|
|
!(cks & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)))))
|
|
{
|
|
ConioPause(Console, PAUSED_FROM_KEYBOARD);
|
|
|
|
/* Skip the event */
|
|
RtlMoveMemory(InputEvent,
|
|
InputEvent + 1,
|
|
(NumEvents - 1) * sizeof(INPUT_RECORD));
|
|
--NumEventsToWrite;
|
|
continue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((vk < VK_SHIFT || vk > VK_CAPITAL) && vk != VK_LWIN &&
|
|
vk != VK_RWIN && vk != VK_NUMLOCK && vk != VK_SCROLL)
|
|
{
|
|
ConioUnpause(Console, PAUSED_FROM_KEYBOARD);
|
|
|
|
/* Skip the event */
|
|
RtlMoveMemory(InputEvent,
|
|
InputEvent + 1,
|
|
(NumEvents - 1) * sizeof(INPUT_RECORD));
|
|
--NumEventsToWrite;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Go to the next event */
|
|
++InputEvent;
|
|
}
|
|
|
|
return NumEventsToWrite;
|
|
}
|
|
|
|
static VOID
|
|
PostprocessInput(PCONSRV_CONSOLE Console)
|
|
{
|
|
CsrNotifyWait(&Console->ReadWaitQueue,
|
|
FALSE,
|
|
NULL,
|
|
NULL);
|
|
if (!IsListEmpty(&Console->ReadWaitQueue))
|
|
{
|
|
CsrDereferenceWait(&Console->ReadWaitQueue);
|
|
}
|
|
}
|
|
|
|
|
|
NTSTATUS NTAPI
|
|
ConDrvWriteConsoleInput(IN PCONSOLE Console,
|
|
IN PCONSOLE_INPUT_BUFFER InputBuffer,
|
|
IN BOOLEAN AppendToEnd,
|
|
IN PINPUT_RECORD InputRecord,
|
|
IN ULONG NumEventsToWrite,
|
|
OUT PULONG NumEventsWritten OPTIONAL);
|
|
static NTSTATUS
|
|
ConioAddInputEvents(PCONSRV_CONSOLE Console,
|
|
PINPUT_RECORD InputRecords, // InputEvent
|
|
ULONG NumEventsToWrite,
|
|
PULONG NumEventsWritten,
|
|
BOOLEAN AppendToEnd)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
if (NumEventsWritten) *NumEventsWritten = 0;
|
|
|
|
NumEventsToWrite = PreprocessInput(Console, InputRecords, NumEventsToWrite);
|
|
if (NumEventsToWrite == 0) return STATUS_SUCCESS;
|
|
|
|
// Status = ConDrvAddInputEvents(Console,
|
|
// InputRecords,
|
|
// NumEventsToWrite,
|
|
// NumEventsWritten,
|
|
// AppendToEnd);
|
|
|
|
Status = ConDrvWriteConsoleInput((PCONSOLE)Console,
|
|
&Console->InputBuffer,
|
|
AppendToEnd,
|
|
InputRecords,
|
|
NumEventsToWrite,
|
|
NumEventsWritten);
|
|
|
|
// if (NT_SUCCESS(Status))
|
|
if (Status == STATUS_SUCCESS) PostprocessInput(Console);
|
|
|
|
return Status;
|
|
}
|
|
|
|
/* FIXME: This function can be called by CONDRV, in ConioResizeBuffer() in text.c */
|
|
NTSTATUS
|
|
ConioProcessInputEvent(PCONSRV_CONSOLE Console,
|
|
PINPUT_RECORD InputEvent)
|
|
{
|
|
ULONG NumEventsWritten;
|
|
|
|
if (InputEvent->EventType == KEY_EVENT)
|
|
{
|
|
BOOL Down = InputEvent->Event.KeyEvent.bKeyDown;
|
|
UINT VirtualKeyCode = InputEvent->Event.KeyEvent.wVirtualKeyCode;
|
|
DWORD ShiftState = InputEvent->Event.KeyEvent.dwControlKeyState;
|
|
|
|
/* Process Ctrl-C and Ctrl-Break */
|
|
if ( (GetConsoleInputBufferMode(Console) & ENABLE_PROCESSED_INPUT) &&
|
|
Down && (VirtualKeyCode == VK_PAUSE || VirtualKeyCode == 'C') &&
|
|
(ShiftState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) )
|
|
{
|
|
DPRINT1("Console_Api Ctrl-C\n");
|
|
ConSrvConsoleProcessCtrlEvent(Console, 0, CTRL_C_EVENT);
|
|
|
|
if (Console->LineBuffer && !Console->LineComplete)
|
|
{
|
|
/* Line input is in progress; end it */
|
|
Console->LinePos = Console->LineSize = 0;
|
|
Console->LineComplete = TRUE;
|
|
}
|
|
return STATUS_SUCCESS; // STATUS_CONTROL_C_EXIT;
|
|
}
|
|
}
|
|
|
|
return ConioAddInputEvents(Console,
|
|
InputEvent,
|
|
1,
|
|
&NumEventsWritten,
|
|
TRUE);
|
|
}
|
|
|
|
|
|
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;
|
|
PCONSRV_CONSOLE Console = (PCONSRV_CONSOLE)InputInfo->InputBuffer->Header.Console;
|
|
|
|
CapturedInputInfo = ConsoleAllocHeap(0, sizeof(GET_INPUT_INFO));
|
|
if (!CapturedInputInfo) return STATUS_NO_MEMORY;
|
|
|
|
RtlMoveMemory(CapturedInputInfo, InputInfo, sizeof(GET_INPUT_INFO));
|
|
|
|
if (!CsrCreateWait(&Console->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 PVOID Parameter OPTIONAL,
|
|
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;
|
|
|
|
UNICODE_STRING ExeName;
|
|
|
|
PVOID Buffer;
|
|
ULONG NrCharactersRead = 0;
|
|
ULONG CharSize = (ReadConsoleRequest->Unicode ? sizeof(WCHAR) : sizeof(CHAR));
|
|
|
|
/* Retrieve the executable name, if needed */
|
|
if (ReadConsoleRequest->InitialNumBytes == 0 &&
|
|
ReadConsoleRequest->ExeLength <= sizeof(ReadConsoleRequest->StaticBuffer))
|
|
{
|
|
ExeName.Length = ExeName.MaximumLength = ReadConsoleRequest->ExeLength;
|
|
ExeName.Buffer = (PWCHAR)ReadConsoleRequest->StaticBuffer;
|
|
}
|
|
else
|
|
{
|
|
ExeName.Length = ExeName.MaximumLength = 0;
|
|
ExeName.Buffer = NULL;
|
|
}
|
|
|
|
/* Build the ReadControl structure */
|
|
ReadControl.nLength = sizeof(CONSOLE_READCONSOLE_CONTROL);
|
|
ReadControl.nInitialChars = ReadConsoleRequest->InitialNumBytes / CharSize;
|
|
ReadControl.dwCtrlWakeupMask = ReadConsoleRequest->CtrlWakeupMask;
|
|
ReadControl.dwControlKeyState = ReadConsoleRequest->ControlKeyState;
|
|
|
|
/*
|
|
* For optimization purposes, Windows (and hence ReactOS, too, for
|
|
* compatibility reasons) uses a static buffer if no more than eighty
|
|
* bytes are read. Otherwise a new buffer is used.
|
|
* The client-side expects that we know this behaviour.
|
|
*/
|
|
if (ReadConsoleRequest->CaptureBufferSize <= sizeof(ReadConsoleRequest->StaticBuffer))
|
|
{
|
|
/*
|
|
* Adjust the internal pointer, because its old value points to
|
|
* the static buffer in the original ApiMessage structure.
|
|
*/
|
|
// ReadConsoleRequest->Buffer = ReadConsoleRequest->StaticBuffer;
|
|
Buffer = ReadConsoleRequest->StaticBuffer;
|
|
}
|
|
else
|
|
{
|
|
Buffer = ReadConsoleRequest->Buffer;
|
|
}
|
|
|
|
DPRINT("Calling ConDrvReadConsole(%wZ)\n", &ExeName);
|
|
Status = ConDrvReadConsole(InputBuffer->Header.Console,
|
|
InputBuffer,
|
|
ReadConsoleRequest->Unicode,
|
|
Buffer,
|
|
&ReadControl,
|
|
&ExeName,
|
|
ReadConsoleRequest->NumBytes / CharSize, // NrCharactersToRead
|
|
&NrCharactersRead);
|
|
DPRINT("ConDrvReadConsole returned (%d ; Status = 0x%08x)\n",
|
|
NrCharactersRead, Status);
|
|
|
|
// 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. Set the number of bytes read and
|
|
* return the error code we were given.
|
|
*/
|
|
ReadConsoleRequest->NumBytes = NrCharactersRead * CharSize;
|
|
ReadConsoleRequest->ControlKeyState = ReadControl.dwControlKeyState;
|
|
|
|
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,
|
|
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;
|
|
ULONG NumEventsRead;
|
|
|
|
PINPUT_RECORD InputRecord;
|
|
|
|
/*
|
|
* For optimization purposes, Windows (and hence ReactOS, too, for
|
|
* compatibility reasons) uses a static buffer if no more than five
|
|
* input records are read. Otherwise a new buffer is used.
|
|
* The client-side expects that we know this behaviour.
|
|
*/
|
|
if (GetInputRequest->NumRecords <= sizeof(GetInputRequest->RecordStaticBuffer)/sizeof(INPUT_RECORD))
|
|
{
|
|
/*
|
|
* Adjust the internal pointer, because its old value points to
|
|
* the static buffer in the original ApiMessage structure.
|
|
*/
|
|
// GetInputRequest->RecordBufPtr = GetInputRequest->RecordStaticBuffer;
|
|
InputRecord = GetInputRequest->RecordStaticBuffer;
|
|
}
|
|
else
|
|
{
|
|
InputRecord = GetInputRequest->RecordBufPtr;
|
|
}
|
|
|
|
NumEventsRead = 0;
|
|
Status = ConDrvGetConsoleInput(InputBuffer->Header.Console,
|
|
InputBuffer,
|
|
(GetInputRequest->Flags & CONSOLE_READ_KEEPEVENT) != 0,
|
|
(GetInputRequest->Flags & CONSOLE_READ_CONTINUE ) == 0,
|
|
InputRecord,
|
|
GetInputRequest->NumRecords,
|
|
&NumEventsRead);
|
|
|
|
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. Set the number of events read and
|
|
* return the error code we were given.
|
|
*/
|
|
GetInputRequest->NumRecords = NumEventsRead;
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
/* Now translate everything to ANSI */
|
|
if (!GetInputRequest->Unicode)
|
|
{
|
|
ULONG i;
|
|
for (i = 0; i < NumEventsRead; ++i)
|
|
{
|
|
ConioInputEventToAnsi(InputBuffer->Header.Console, &InputRecord[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
// return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
|
|
/* PUBLIC SERVER APIS *********************************************************/
|
|
|
|
/* API_NUMBER: ConsolepReadConsole */
|
|
CON_API(SrvReadConsole,
|
|
CONSOLE_READCONSOLE, ReadConsoleRequest)
|
|
{
|
|
NTSTATUS Status;
|
|
PVOID HandleEntry;
|
|
PCONSOLE_INPUT_BUFFER InputBuffer;
|
|
GET_INPUT_INFO InputInfo;
|
|
|
|
DPRINT("SrvReadConsole\n");
|
|
|
|
/*
|
|
* For optimization purposes, Windows (and hence ReactOS, too, for
|
|
* compatibility reasons) uses a static buffer if no more than eighty
|
|
* bytes are read. Otherwise a new buffer is used.
|
|
* The client-side expects that we know this behaviour.
|
|
*/
|
|
if (ReadConsoleRequest->CaptureBufferSize <= sizeof(ReadConsoleRequest->StaticBuffer))
|
|
{
|
|
/*
|
|
* Adjust the internal pointer, because its old value points to
|
|
* the static buffer in the original ApiMessage structure.
|
|
*/
|
|
// ReadConsoleRequest->Buffer = ReadConsoleRequest->StaticBuffer;
|
|
}
|
|
else
|
|
{
|
|
if (!CsrValidateMessageBuffer(ApiMessage,
|
|
(PVOID*)&ReadConsoleRequest->Buffer,
|
|
ReadConsoleRequest->CaptureBufferSize,
|
|
sizeof(BYTE)))
|
|
{
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
if (ReadConsoleRequest->InitialNumBytes > ReadConsoleRequest->NumBytes)
|
|
{
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
Status = ConSrvGetInputBufferAndHandleEntry(ProcessData,
|
|
ReadConsoleRequest->InputHandle,
|
|
&InputBuffer,
|
|
&HandleEntry,
|
|
GENERIC_READ,
|
|
TRUE);
|
|
if (!NT_SUCCESS(Status))
|
|
return Status;
|
|
|
|
ASSERT((PCONSOLE)Console == InputBuffer->Header.Console);
|
|
|
|
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;
|
|
}
|
|
|
|
/* API_NUMBER: ConsolepGetConsoleInput */
|
|
CON_API(SrvGetConsoleInput,
|
|
CONSOLE_GETINPUT, GetInputRequest)
|
|
{
|
|
NTSTATUS Status;
|
|
PVOID HandleEntry;
|
|
PCONSOLE_INPUT_BUFFER InputBuffer;
|
|
GET_INPUT_INFO InputInfo;
|
|
|
|
DPRINT("SrvGetConsoleInput\n");
|
|
|
|
if (GetInputRequest->Flags & ~(CONSOLE_READ_KEEPEVENT | CONSOLE_READ_CONTINUE))
|
|
{
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/*
|
|
* For optimization purposes, Windows (and hence ReactOS, too, for
|
|
* compatibility reasons) uses a static buffer if no more than five
|
|
* input records are read. Otherwise a new buffer is used.
|
|
* The client-side expects that we know this behaviour.
|
|
*/
|
|
if (GetInputRequest->NumRecords <= sizeof(GetInputRequest->RecordStaticBuffer)/sizeof(INPUT_RECORD))
|
|
{
|
|
/*
|
|
* Adjust the internal pointer, because its old value points to
|
|
* the static buffer in the original ApiMessage structure.
|
|
*/
|
|
// GetInputRequest->RecordBufPtr = GetInputRequest->RecordStaticBuffer;
|
|
}
|
|
else
|
|
{
|
|
if (!CsrValidateMessageBuffer(ApiMessage,
|
|
(PVOID*)&GetInputRequest->RecordBufPtr,
|
|
GetInputRequest->NumRecords,
|
|
sizeof(INPUT_RECORD)))
|
|
{
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
Status = ConSrvGetInputBufferAndHandleEntry(ProcessData,
|
|
GetInputRequest->InputHandle,
|
|
&InputBuffer,
|
|
&HandleEntry,
|
|
GENERIC_READ,
|
|
TRUE);
|
|
if (!NT_SUCCESS(Status))
|
|
return Status;
|
|
|
|
ASSERT((PCONSOLE)Console == InputBuffer->Header.Console);
|
|
|
|
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;
|
|
}
|
|
|
|
#if 0
|
|
NTSTATUS NTAPI
|
|
ConDrvWriteConsoleInput(IN PCONSOLE Console,
|
|
IN PCONSOLE_INPUT_BUFFER InputBuffer,
|
|
IN BOOLEAN AppendToEnd,
|
|
IN PINPUT_RECORD InputRecord,
|
|
IN ULONG NumEventsToWrite,
|
|
OUT PULONG NumEventsWritten OPTIONAL);
|
|
#endif
|
|
|
|
/* API_NUMBER: ConsolepWriteConsoleInput */
|
|
CON_API(SrvWriteConsoleInput,
|
|
CONSOLE_WRITEINPUT, WriteInputRequest)
|
|
{
|
|
NTSTATUS Status;
|
|
PCONSOLE_INPUT_BUFFER InputBuffer;
|
|
ULONG NumEventsWritten;
|
|
PINPUT_RECORD InputRecord;
|
|
|
|
/*
|
|
* For optimization purposes, Windows (and hence ReactOS, too, for
|
|
* compatibility reasons) uses a static buffer if no more than five
|
|
* input records are written. Otherwise a new buffer is used.
|
|
* The client-side expects that we know this behaviour.
|
|
*/
|
|
if (WriteInputRequest->NumRecords <= sizeof(WriteInputRequest->RecordStaticBuffer)/sizeof(INPUT_RECORD))
|
|
{
|
|
/*
|
|
* Adjust the internal pointer, because its old value points to
|
|
* the static buffer in the original ApiMessage structure.
|
|
*/
|
|
// WriteInputRequest->RecordBufPtr = WriteInputRequest->RecordStaticBuffer;
|
|
InputRecord = WriteInputRequest->RecordStaticBuffer;
|
|
}
|
|
else
|
|
{
|
|
if (!CsrValidateMessageBuffer(ApiMessage,
|
|
(PVOID*)&WriteInputRequest->RecordBufPtr,
|
|
WriteInputRequest->NumRecords,
|
|
sizeof(INPUT_RECORD)))
|
|
{
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
InputRecord = WriteInputRequest->RecordBufPtr;
|
|
}
|
|
|
|
Status = ConSrvGetInputBuffer(ProcessData,
|
|
WriteInputRequest->InputHandle,
|
|
&InputBuffer, GENERIC_WRITE, TRUE);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
WriteInputRequest->NumRecords = 0;
|
|
return Status;
|
|
}
|
|
|
|
ASSERT((PCONSOLE)Console == InputBuffer->Header.Console);
|
|
|
|
/* First translate everything to UNICODE */
|
|
if (!WriteInputRequest->Unicode)
|
|
{
|
|
ULONG i;
|
|
for (i = 0; i < WriteInputRequest->NumRecords; ++i)
|
|
{
|
|
ConioInputEventToUnicode((PCONSOLE)Console, &InputRecord[i]);
|
|
}
|
|
}
|
|
|
|
/* Now, add the events */
|
|
NumEventsWritten = 0;
|
|
Status = ConioAddInputEvents(Console,
|
|
// InputBuffer,
|
|
InputRecord,
|
|
WriteInputRequest->NumRecords,
|
|
&NumEventsWritten,
|
|
WriteInputRequest->AppendToEnd);
|
|
|
|
// Status = ConDrvWriteConsoleInput((PCONSOLE)Console,
|
|
// InputBuffer,
|
|
// WriteInputRequest->AppendToEnd,
|
|
// InputRecord,
|
|
// WriteInputRequest->NumRecords,
|
|
// &NumEventsWritten);
|
|
|
|
WriteInputRequest->NumRecords = NumEventsWritten;
|
|
|
|
ConSrvReleaseInputBuffer(InputBuffer, TRUE);
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS NTAPI
|
|
ConDrvFlushConsoleInputBuffer(IN PCONSOLE Console,
|
|
IN PCONSOLE_INPUT_BUFFER InputBuffer);
|
|
/* API_NUMBER: ConsolepFlushInputBuffer */
|
|
CON_API(SrvFlushConsoleInputBuffer,
|
|
CONSOLE_FLUSHINPUTBUFFER, FlushInputBufferRequest)
|
|
{
|
|
NTSTATUS Status;
|
|
PCONSOLE_INPUT_BUFFER InputBuffer;
|
|
|
|
Status = ConSrvGetInputBuffer(ProcessData,
|
|
FlushInputBufferRequest->InputHandle,
|
|
&InputBuffer, GENERIC_WRITE, TRUE);
|
|
if (!NT_SUCCESS(Status))
|
|
return Status;
|
|
|
|
ASSERT((PCONSOLE)Console == InputBuffer->Header.Console);
|
|
|
|
Status = ConDrvFlushConsoleInputBuffer((PCONSOLE)Console, InputBuffer);
|
|
|
|
ConSrvReleaseInputBuffer(InputBuffer, TRUE);
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS NTAPI
|
|
ConDrvGetConsoleNumberOfInputEvents(IN PCONSOLE Console,
|
|
IN PCONSOLE_INPUT_BUFFER InputBuffer,
|
|
OUT PULONG NumberOfEvents);
|
|
/* API_NUMBER: ConsolepGetNumberOfInputEvents */
|
|
CON_API(SrvGetConsoleNumberOfInputEvents,
|
|
CONSOLE_GETNUMINPUTEVENTS, GetNumInputEventsRequest)
|
|
{
|
|
NTSTATUS Status;
|
|
PCONSOLE_INPUT_BUFFER InputBuffer;
|
|
|
|
Status = ConSrvGetInputBuffer(ProcessData,
|
|
GetNumInputEventsRequest->InputHandle,
|
|
&InputBuffer, GENERIC_READ, TRUE);
|
|
if (!NT_SUCCESS(Status))
|
|
return Status;
|
|
|
|
ASSERT((PCONSOLE)Console == InputBuffer->Header.Console);
|
|
|
|
Status = ConDrvGetConsoleNumberOfInputEvents((PCONSOLE)Console,
|
|
InputBuffer,
|
|
&GetNumInputEventsRequest->NumberOfEvents);
|
|
|
|
ConSrvReleaseInputBuffer(InputBuffer, TRUE);
|
|
return Status;
|
|
}
|
|
|
|
/* EOF */
|