mirror of
https://github.com/reactos/reactos.git
synced 2024-11-10 16:48:16 +00:00
388 lines
12 KiB
C
388 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 */
|