reactos/win32ss/user/winsrv/consrv/coninput.c

855 lines
29 KiB
C
Raw Normal View History

/*
* 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));
[KERNEL32/CONSRV] - Move the waiting loop of IntReadConsole into SrvReadConsole instead. - Merge IntPeekConsoleInput and IntReadConsoleInput helpers into a single IntGetConsoleInput helper function and make PeekConsoleInputW/A and ReadConsoleInputW/A use it. Indeed, the "only" difference between these two functions, is that the first one keeps the data read inside the console input buffer, but on the contrary the second removes the data and does not return until at least one input record has been read. In server-side, CsrReadInputEvent and CsrPeekConsoleInput are merged into SrvGetConsoleInput. Merge the corresponding structures. - Use shorter variable names in IntReadConsoleOutput and use CONSOLE_PROCESS_DATA structure in SrvReadConsoleOutput. - Merge IntReadConsoleOutputCharacter and ReadConsoleOutputAttribute functions into a single IntReadConsoleOutputCode helper function, and make ReadConsoleOutputAttribute and ReadConsoleOutputCharacterW/A use it. In server-side, CsrReadConsoleOutputChar and CsrReadConsoleOutputAttrib are merged into SrvReadConsoleOutputString. (Remark: the SrvReadConsoleOutputString name comes from http://j00ru.vexillium.org/csrss_list/api_list.html and I checked that it was indeed the server function that was called by both ReadConsoleOutputAttribute and ReadConsoleOutputCharacter). The idea behind merging these two functions is, that an attribute or a character are instances of the same entity, namely a "code" (what would be called an (ANSI) escape sequence). Therefore I encode them inside the same CSRSS_READ_CONSOLE_OUTPUT_CODE structure and I use it with the previous functions. [CONSRV] - Make use of the CONSOLE_PROCESS_DATA structure (introduced in r57685). - Use CsrValidateMessageBuffer instead of Win32CsrValidateBuffer. - Reorganize conmsg.h a little bit. svn path=/branches/ros-csrss/; revision=57721
2012-11-17 21:39:41 +00:00
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;
[KERNEL32/CONSRV] - Move the waiting loop of IntReadConsole into SrvReadConsole instead. - Merge IntPeekConsoleInput and IntReadConsoleInput helpers into a single IntGetConsoleInput helper function and make PeekConsoleInputW/A and ReadConsoleInputW/A use it. Indeed, the "only" difference between these two functions, is that the first one keeps the data read inside the console input buffer, but on the contrary the second removes the data and does not return until at least one input record has been read. In server-side, CsrReadInputEvent and CsrPeekConsoleInput are merged into SrvGetConsoleInput. Merge the corresponding structures. - Use shorter variable names in IntReadConsoleOutput and use CONSOLE_PROCESS_DATA structure in SrvReadConsoleOutput. - Merge IntReadConsoleOutputCharacter and ReadConsoleOutputAttribute functions into a single IntReadConsoleOutputCode helper function, and make ReadConsoleOutputAttribute and ReadConsoleOutputCharacterW/A use it. In server-side, CsrReadConsoleOutputChar and CsrReadConsoleOutputAttrib are merged into SrvReadConsoleOutputString. (Remark: the SrvReadConsoleOutputString name comes from http://j00ru.vexillium.org/csrss_list/api_list.html and I checked that it was indeed the server function that was called by both ReadConsoleOutputAttribute and ReadConsoleOutputCharacter). The idea behind merging these two functions is, that an attribute or a character are instances of the same entity, namely a "code" (what would be called an (ANSI) escape sequence). Therefore I encode them inside the same CSRSS_READ_CONSOLE_OUTPUT_CODE structure and I use it with the previous functions. [CONSRV] - Make use of the CONSOLE_PROCESS_DATA structure (introduced in r57685). - Use CsrValidateMessageBuffer instead of Win32CsrValidateBuffer. - Reorganize conmsg.h a little bit. svn path=/branches/ros-csrss/; revision=57721
2012-11-17 21:39:41 +00:00
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);
[KERNEL32/CONSRV] - Move the waiting loop of IntReadConsole into SrvReadConsole instead. - Merge IntPeekConsoleInput and IntReadConsoleInput helpers into a single IntGetConsoleInput helper function and make PeekConsoleInputW/A and ReadConsoleInputW/A use it. Indeed, the "only" difference between these two functions, is that the first one keeps the data read inside the console input buffer, but on the contrary the second removes the data and does not return until at least one input record has been read. In server-side, CsrReadInputEvent and CsrPeekConsoleInput are merged into SrvGetConsoleInput. Merge the corresponding structures. - Use shorter variable names in IntReadConsoleOutput and use CONSOLE_PROCESS_DATA structure in SrvReadConsoleOutput. - Merge IntReadConsoleOutputCharacter and ReadConsoleOutputAttribute functions into a single IntReadConsoleOutputCode helper function, and make ReadConsoleOutputAttribute and ReadConsoleOutputCharacterW/A use it. In server-side, CsrReadConsoleOutputChar and CsrReadConsoleOutputAttrib are merged into SrvReadConsoleOutputString. (Remark: the SrvReadConsoleOutputString name comes from http://j00ru.vexillium.org/csrss_list/api_list.html and I checked that it was indeed the server function that was called by both ReadConsoleOutputAttribute and ReadConsoleOutputCharacter). The idea behind merging these two functions is, that an attribute or a character are instances of the same entity, namely a "code" (what would be called an (ANSI) escape sequence). Therefore I encode them inside the same CSRSS_READ_CONSOLE_OUTPUT_CODE structure and I use it with the previous functions. [CONSRV] - Make use of the CONSOLE_PROCESS_DATA structure (introduced in r57685). - Use CsrValidateMessageBuffer instead of Win32CsrValidateBuffer. - Reorganize conmsg.h a little bit. svn path=/branches/ros-csrss/; revision=57721
2012-11-17 21:39:41 +00:00
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;
}
}
[KERNEL32/CONSRV] - Move the waiting loop of IntReadConsole into SrvReadConsole instead. - Merge IntPeekConsoleInput and IntReadConsoleInput helpers into a single IntGetConsoleInput helper function and make PeekConsoleInputW/A and ReadConsoleInputW/A use it. Indeed, the "only" difference between these two functions, is that the first one keeps the data read inside the console input buffer, but on the contrary the second removes the data and does not return until at least one input record has been read. In server-side, CsrReadInputEvent and CsrPeekConsoleInput are merged into SrvGetConsoleInput. Merge the corresponding structures. - Use shorter variable names in IntReadConsoleOutput and use CONSOLE_PROCESS_DATA structure in SrvReadConsoleOutput. - Merge IntReadConsoleOutputCharacter and ReadConsoleOutputAttribute functions into a single IntReadConsoleOutputCode helper function, and make ReadConsoleOutputAttribute and ReadConsoleOutputCharacterW/A use it. In server-side, CsrReadConsoleOutputChar and CsrReadConsoleOutputAttrib are merged into SrvReadConsoleOutputString. (Remark: the SrvReadConsoleOutputString name comes from http://j00ru.vexillium.org/csrss_list/api_list.html and I checked that it was indeed the server function that was called by both ReadConsoleOutputAttribute and ReadConsoleOutputCharacter). The idea behind merging these two functions is, that an attribute or a character are instances of the same entity, namely a "code" (what would be called an (ANSI) escape sequence). Therefore I encode them inside the same CSRSS_READ_CONSOLE_OUTPUT_CODE structure and I use it with the previous functions. [CONSRV] - Make use of the CONSOLE_PROCESS_DATA structure (introduced in r57685). - Use CsrValidateMessageBuffer instead of Win32CsrValidateBuffer. - Reorganize conmsg.h a little bit. svn path=/branches/ros-csrss/; revision=57721
2012-11-17 21:39:41 +00:00
/* 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;
[KERNEL32/CONSRV] - Move the waiting loop of IntReadConsole into SrvReadConsole instead. - Merge IntPeekConsoleInput and IntReadConsoleInput helpers into a single IntGetConsoleInput helper function and make PeekConsoleInputW/A and ReadConsoleInputW/A use it. Indeed, the "only" difference between these two functions, is that the first one keeps the data read inside the console input buffer, but on the contrary the second removes the data and does not return until at least one input record has been read. In server-side, CsrReadInputEvent and CsrPeekConsoleInput are merged into SrvGetConsoleInput. Merge the corresponding structures. - Use shorter variable names in IntReadConsoleOutput and use CONSOLE_PROCESS_DATA structure in SrvReadConsoleOutput. - Merge IntReadConsoleOutputCharacter and ReadConsoleOutputAttribute functions into a single IntReadConsoleOutputCode helper function, and make ReadConsoleOutputAttribute and ReadConsoleOutputCharacterW/A use it. In server-side, CsrReadConsoleOutputChar and CsrReadConsoleOutputAttrib are merged into SrvReadConsoleOutputString. (Remark: the SrvReadConsoleOutputString name comes from http://j00ru.vexillium.org/csrss_list/api_list.html and I checked that it was indeed the server function that was called by both ReadConsoleOutputAttribute and ReadConsoleOutputCharacter). The idea behind merging these two functions is, that an attribute or a character are instances of the same entity, namely a "code" (what would be called an (ANSI) escape sequence). Therefore I encode them inside the same CSRSS_READ_CONSOLE_OUTPUT_CODE structure and I use it with the previous functions. [CONSRV] - Make use of the CONSOLE_PROCESS_DATA structure (introduced in r57685). - Use CsrValidateMessageBuffer instead of Win32CsrValidateBuffer. - Reorganize conmsg.h a little bit. svn path=/branches/ros-csrss/; revision=57721
2012-11-17 21:39:41 +00:00
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)
{
[KERNEL32/CONSRV] - Move the waiting loop of IntReadConsole into SrvReadConsole instead. - Merge IntPeekConsoleInput and IntReadConsoleInput helpers into a single IntGetConsoleInput helper function and make PeekConsoleInputW/A and ReadConsoleInputW/A use it. Indeed, the "only" difference between these two functions, is that the first one keeps the data read inside the console input buffer, but on the contrary the second removes the data and does not return until at least one input record has been read. In server-side, CsrReadInputEvent and CsrPeekConsoleInput are merged into SrvGetConsoleInput. Merge the corresponding structures. - Use shorter variable names in IntReadConsoleOutput and use CONSOLE_PROCESS_DATA structure in SrvReadConsoleOutput. - Merge IntReadConsoleOutputCharacter and ReadConsoleOutputAttribute functions into a single IntReadConsoleOutputCode helper function, and make ReadConsoleOutputAttribute and ReadConsoleOutputCharacterW/A use it. In server-side, CsrReadConsoleOutputChar and CsrReadConsoleOutputAttrib are merged into SrvReadConsoleOutputString. (Remark: the SrvReadConsoleOutputString name comes from http://j00ru.vexillium.org/csrss_list/api_list.html and I checked that it was indeed the server function that was called by both ReadConsoleOutputAttribute and ReadConsoleOutputCharacter). The idea behind merging these two functions is, that an attribute or a character are instances of the same entity, namely a "code" (what would be called an (ANSI) escape sequence). Therefore I encode them inside the same CSRSS_READ_CONSOLE_OUTPUT_CODE structure and I use it with the previous functions. [CONSRV] - Make use of the CONSOLE_PROCESS_DATA structure (introduced in r57685). - Use CsrValidateMessageBuffer instead of Win32CsrValidateBuffer. - Reorganize conmsg.h a little bit. svn path=/branches/ros-csrss/; revision=57721
2012-11-17 21:39:41 +00:00
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)
{
[KERNEL32/CONSRV] - Move the waiting loop of IntReadConsole into SrvReadConsole instead. - Merge IntPeekConsoleInput and IntReadConsoleInput helpers into a single IntGetConsoleInput helper function and make PeekConsoleInputW/A and ReadConsoleInputW/A use it. Indeed, the "only" difference between these two functions, is that the first one keeps the data read inside the console input buffer, but on the contrary the second removes the data and does not return until at least one input record has been read. In server-side, CsrReadInputEvent and CsrPeekConsoleInput are merged into SrvGetConsoleInput. Merge the corresponding structures. - Use shorter variable names in IntReadConsoleOutput and use CONSOLE_PROCESS_DATA structure in SrvReadConsoleOutput. - Merge IntReadConsoleOutputCharacter and ReadConsoleOutputAttribute functions into a single IntReadConsoleOutputCode helper function, and make ReadConsoleOutputAttribute and ReadConsoleOutputCharacterW/A use it. In server-side, CsrReadConsoleOutputChar and CsrReadConsoleOutputAttrib are merged into SrvReadConsoleOutputString. (Remark: the SrvReadConsoleOutputString name comes from http://j00ru.vexillium.org/csrss_list/api_list.html and I checked that it was indeed the server function that was called by both ReadConsoleOutputAttribute and ReadConsoleOutputCharacter). The idea behind merging these two functions is, that an attribute or a character are instances of the same entity, namely a "code" (what would be called an (ANSI) escape sequence). Therefore I encode them inside the same CSRSS_READ_CONSOLE_OUTPUT_CODE structure and I use it with the previous functions. [CONSRV] - Make use of the CONSOLE_PROCESS_DATA structure (introduced in r57685). - Use CsrValidateMessageBuffer instead of Win32CsrValidateBuffer. - Reorganize conmsg.h a little bit. svn path=/branches/ros-csrss/; revision=57721
2012-11-17 21:39:41 +00:00
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 */