mirror of
https://github.com/reactos/reactos.git
synced 2024-11-01 12:26:32 +00:00
3a49e26f13
Addendum to commit b8b8819c7
(r60920)
ReadConsoleInputEx() and its flags used to be undocumented.
In the meantime they became documented on MSDN, see:
https://learn.microsoft.com/en-us/windows/console/readconsoleinputex
We can therefore adopt these now-documented flag names.
855 lines
29 KiB
C
855 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_NOREMOVE) != 0,
|
|
(GetInputRequest->Flags & CONSOLE_READ_NOWAIT ) == 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_NOREMOVE | CONSOLE_READ_NOWAIT))
|
|
{
|
|
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 */
|