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

389 lines
12 KiB
C

/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS Console Driver DLL
* FILE: win32ss/user/winsrv/consrv/condrv/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>
/* PRIVATE FUNCTIONS **********************************************************/
// ConDrvAddInputEvents
static NTSTATUS
AddInputEvents(PCONSOLE Console,
PINPUT_RECORD InputRecords, // InputEvent
ULONG NumEventsToWrite,
PULONG NumEventsWritten,
BOOLEAN AppendToEnd)
{
NTSTATUS Status = STATUS_SUCCESS;
ULONG i = 0;
BOOLEAN SetWaitEvent = FALSE;
if (NumEventsWritten) *NumEventsWritten = 0;
/*
* When adding many single events, in the case of repeated mouse move or
* key down events, we try to coalesce them so that we do not saturate
* too quickly the input buffer.
*/
if (NumEventsToWrite == 1 && !IsListEmpty(&Console->InputBuffer.InputEvents))
{
PINPUT_RECORD InputRecord = InputRecords; // Only one element
PINPUT_RECORD LastInputRecord;
ConsoleInput* ConInRec; // Input
/* Get the "next" event of the input buffer */
if (AppendToEnd)
{
/* Get the tail element */
ConInRec = CONTAINING_RECORD(Console->InputBuffer.InputEvents.Blink,
ConsoleInput, ListEntry);
}
else
{
/* Get the head element */
ConInRec = CONTAINING_RECORD(Console->InputBuffer.InputEvents.Flink,
ConsoleInput, ListEntry);
}
LastInputRecord = &ConInRec->InputEvent;
if (InputRecord->EventType == MOUSE_EVENT &&
InputRecord->Event.MouseEvent.dwEventFlags == MOUSE_MOVED)
{
if (LastInputRecord->EventType == MOUSE_EVENT &&
LastInputRecord->Event.MouseEvent.dwEventFlags == MOUSE_MOVED)
{
/* Update the mouse position */
LastInputRecord->Event.MouseEvent.dwMousePosition.X =
InputRecord->Event.MouseEvent.dwMousePosition.X;
LastInputRecord->Event.MouseEvent.dwMousePosition.Y =
InputRecord->Event.MouseEvent.dwMousePosition.Y;
i = 1;
// return STATUS_SUCCESS;
Status = STATUS_SUCCESS;
}
}
else if (InputRecord->EventType == KEY_EVENT &&
InputRecord->Event.KeyEvent.bKeyDown)
{
if (LastInputRecord->EventType == KEY_EVENT &&
LastInputRecord->Event.KeyEvent.bKeyDown &&
(LastInputRecord->Event.KeyEvent.wVirtualScanCode == // Same scancode
InputRecord->Event.KeyEvent.wVirtualScanCode) &&
(LastInputRecord->Event.KeyEvent.uChar.UnicodeChar == // Same character
InputRecord->Event.KeyEvent.uChar.UnicodeChar) &&
(LastInputRecord->Event.KeyEvent.dwControlKeyState == // Same Ctrl/Alt/Shift state
InputRecord->Event.KeyEvent.dwControlKeyState) )
{
/* Update the repeat count */
LastInputRecord->Event.KeyEvent.wRepeatCount +=
InputRecord->Event.KeyEvent.wRepeatCount;
i = 1;
// return STATUS_SUCCESS;
Status = STATUS_SUCCESS;
}
}
}
/* If we coalesced the only one element, we can quit */
if (i == 1 && Status == STATUS_SUCCESS /* && NumEventsToWrite == 1 */)
goto Done;
/*
* No event coalesced, add them in the usual way.
*/
if (AppendToEnd)
{
/* Go to the beginning of the list */
// InputRecords = InputRecords;
}
else
{
/* Go to the end of the list */
InputRecords = &InputRecords[NumEventsToWrite - 1];
}
/* Set the event if the list is going to be non-empty */
if (IsListEmpty(&Console->InputBuffer.InputEvents))
SetWaitEvent = TRUE;
for (i = 0; i < NumEventsToWrite && NT_SUCCESS(Status); ++i)
{
PINPUT_RECORD InputRecord;
ConsoleInput* ConInRec;
if (AppendToEnd)
{
/* Select the event and go to the next one */
InputRecord = InputRecords++;
}
else
{
/* Select the event and go to the previous one */
InputRecord = InputRecords--;
}
/* Add event to the queue */
ConInRec = ConsoleAllocHeap(0, sizeof(ConsoleInput));
if (ConInRec == NULL)
{
// return STATUS_INSUFFICIENT_RESOURCES;
Status = STATUS_INSUFFICIENT_RESOURCES;
continue;
}
ConInRec->InputEvent = *InputRecord;
if (AppendToEnd)
{
/* Append the event to the end of the queue */
InsertTailList(&Console->InputBuffer.InputEvents, &ConInRec->ListEntry);
}
else
{
/* Append the event to the beginning of the queue */
InsertHeadList(&Console->InputBuffer.InputEvents, &ConInRec->ListEntry);
}
_InterlockedIncrement((PLONG)&Console->InputBuffer.NumberOfEvents);
// return STATUS_SUCCESS;
Status = STATUS_SUCCESS;
}
if (SetWaitEvent) NtSetEvent(Console->InputBuffer.ActiveEvent, NULL);
Done:
if (NumEventsWritten) *NumEventsWritten = i;
return Status;
}
static VOID
PurgeInputBuffer(IN PCONSOLE_INPUT_BUFFER InputBuffer)
{
PLIST_ENTRY CurrentEntry;
ConsoleInput* Event;
/* Discard all entries in the input event queue */
_InterlockedExchange((PLONG)&InputBuffer->NumberOfEvents, 0);
while (!IsListEmpty(&InputBuffer->InputEvents))
{
CurrentEntry = RemoveHeadList(&InputBuffer->InputEvents);
Event = CONTAINING_RECORD(CurrentEntry, ConsoleInput, ListEntry);
ConsoleFreeHeap(Event);
}
// NtClose(Console->InputBuffer.ActiveEvent);
}
NTSTATUS NTAPI
ConDrvInitInputBuffer(IN PCONSOLE Console,
IN ULONG InputBufferSize)
{
NTSTATUS Status;
OBJECT_ATTRIBUTES ObjectAttributes;
ConSrvInitObject(&Console->InputBuffer.Header, INPUT_BUFFER, Console);
InitializeObjectAttributes(&ObjectAttributes,
NULL,
OBJ_INHERIT,
NULL,
NULL);
Status = NtCreateEvent(&Console->InputBuffer.ActiveEvent, EVENT_ALL_ACCESS,
&ObjectAttributes, NotificationEvent, FALSE);
if (!NT_SUCCESS(Status))
return Status;
Console->InputBuffer.InputBufferSize = InputBufferSize;
Console->InputBuffer.NumberOfEvents = 0;
InitializeListHead(&Console->InputBuffer.InputEvents);
Console->InputBuffer.Mode = ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT |
ENABLE_ECHO_INPUT | ENABLE_MOUSE_INPUT;
return STATUS_SUCCESS;
}
VOID NTAPI
ConDrvDeinitInputBuffer(IN PCONSOLE Console)
{
PurgeInputBuffer(&Console->InputBuffer);
NtClose(Console->InputBuffer.ActiveEvent);
}
/* PUBLIC DRIVER APIS *********************************************************/
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)
{
// STATUS_PENDING : Wait if more to read ; STATUS_SUCCESS : Don't wait.
// NTSTATUS Status; = STATUS_PENDING;
if (Console == NULL || InputBuffer == NULL || /* Buffer == NULL || */
ReadControl == NULL || ReadControl->nLength != sizeof(CONSOLE_READCONSOLE_CONTROL))
{
return STATUS_INVALID_PARAMETER;
}
/* Validity checks */
ASSERT(Console == InputBuffer->Header.Console);
ASSERT((Buffer != NULL) || (Buffer == NULL && NumCharsToRead == 0));
/* Call the line-discipline */
return TermReadStream(Console,
Unicode,
Buffer,
ReadControl,
Parameter,
NumCharsToRead,
NumCharsRead);
}
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)
{
PLIST_ENTRY CurrentInput;
ConsoleInput* Input;
ULONG i = 0;
if (Console == NULL || InputBuffer == NULL /* || InputRecord == NULL */)
return STATUS_INVALID_PARAMETER;
/* Validity checks */
ASSERT(Console == InputBuffer->Header.Console);
ASSERT((InputRecord != NULL) || (InputRecord == NULL && NumEventsToRead == 0));
if (NumEventsRead) *NumEventsRead = 0;
if (IsListEmpty(&InputBuffer->InputEvents))
{
/*
* No input is available. Wait for more input if requested,
* otherwise, we don't wait, so we return success.
*/
return (WaitForMoreEvents ? STATUS_PENDING : STATUS_SUCCESS);
}
/* Only get input if there is any */
CurrentInput = InputBuffer->InputEvents.Flink;
i = 0;
while ((CurrentInput != &InputBuffer->InputEvents) && (i < NumEventsToRead))
{
Input = CONTAINING_RECORD(CurrentInput, ConsoleInput, ListEntry);
*InputRecord = Input->InputEvent;
++InputRecord;
++i;
CurrentInput = CurrentInput->Flink;
/* Remove the events from the queue if needed */
if (!KeepEvents)
{
_InterlockedDecrement((PLONG)&InputBuffer->NumberOfEvents);
RemoveEntryList(&Input->ListEntry);
ConsoleFreeHeap(Input);
}
}
if (NumEventsRead) *NumEventsRead = i;
if (IsListEmpty(&InputBuffer->InputEvents))
{
NtClearEvent(InputBuffer->ActiveEvent);
}
// FIXME: If we add back UNICODE support, it's here that we need to do the translation.
/* We read all the inputs available, we return success */
return STATUS_SUCCESS;
}
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)
{
if (Console == NULL || InputBuffer == NULL /* || InputRecord == NULL */)
return STATUS_INVALID_PARAMETER;
/* Validity checks */
ASSERT(Console == InputBuffer->Header.Console);
ASSERT((InputRecord != NULL) || (InputRecord == NULL && NumEventsToWrite == 0));
/* Now, add the events */
if (NumEventsWritten) *NumEventsWritten = 0;
// FIXME: If we add back UNICODE support, it's here that we need to do the translation.
return AddInputEvents(Console,
InputRecord,
NumEventsToWrite,
NumEventsWritten,
AppendToEnd);
}
NTSTATUS NTAPI
ConDrvFlushConsoleInputBuffer(IN PCONSOLE Console,
IN PCONSOLE_INPUT_BUFFER InputBuffer)
{
if (Console == NULL || InputBuffer == NULL)
return STATUS_INVALID_PARAMETER;
/* Validity check */
ASSERT(Console == InputBuffer->Header.Console);
/* Discard all entries in the input event queue */
PurgeInputBuffer(InputBuffer);
NtClearEvent(InputBuffer->ActiveEvent);
return STATUS_SUCCESS;
}
NTSTATUS NTAPI
ConDrvGetConsoleNumberOfInputEvents(IN PCONSOLE Console,
IN PCONSOLE_INPUT_BUFFER InputBuffer,
OUT PULONG NumberOfEvents)
{
if (Console == NULL || InputBuffer == NULL || NumberOfEvents == NULL)
return STATUS_INVALID_PARAMETER;
/* Validity check */
ASSERT(Console == InputBuffer->Header.Console);
*NumberOfEvents = InputBuffer->NumberOfEvents;
return STATUS_SUCCESS;
}
/* EOF */