/* * 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 #define NDEBUG #include /* 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); } // return STATUS_SUCCESS; Status = STATUS_SUCCESS; } if (SetWaitEvent) NtSetEvent(Console->InputBuffer.ActiveEvent, NULL); Done: if (NumEventsWritten) *NumEventsWritten = i; return Status; } static VOID PurgeInputBuffer(PCONSOLE Console) { PLIST_ENTRY CurrentEntry; ConsoleInput* Event; while (!IsListEmpty(&Console->InputBuffer.InputEvents)) { CurrentEntry = RemoveHeadList(&Console->InputBuffer.InputEvents); Event = CONTAINING_RECORD(CurrentEntry, ConsoleInput, ListEntry); ConsoleFreeHeap(Event); } // CloseHandle(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; 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); CloseHandle(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) { RemoveEntryList(&Input->ListEntry); ConsoleFreeHeap(Input); } } if (NumEventsRead) *NumEventsRead = i; if (IsListEmpty(&InputBuffer->InputEvents)) { ResetEvent(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) { PLIST_ENTRY CurrentEntry; ConsoleInput* Event; if (Console == NULL || InputBuffer == NULL) return STATUS_INVALID_PARAMETER; /* Validity check */ ASSERT(Console == InputBuffer->Header.Console); /* Discard all entries in the input event queue */ while (!IsListEmpty(&InputBuffer->InputEvents)) { CurrentEntry = RemoveHeadList(&InputBuffer->InputEvents); Event = CONTAINING_RECORD(CurrentEntry, ConsoleInput, ListEntry); ConsoleFreeHeap(Event); } ResetEvent(InputBuffer->ActiveEvent); return STATUS_SUCCESS; } NTSTATUS NTAPI ConDrvGetConsoleNumberOfInputEvents(IN PCONSOLE Console, IN PCONSOLE_INPUT_BUFFER InputBuffer, OUT PULONG NumberOfEvents) { PLIST_ENTRY CurrentInput; if (Console == NULL || InputBuffer == NULL || NumberOfEvents == NULL) return STATUS_INVALID_PARAMETER; /* Validity check */ ASSERT(Console == InputBuffer->Header.Console); *NumberOfEvents = 0; /* If there are any events ... */ CurrentInput = InputBuffer->InputEvents.Flink; while (CurrentInput != &InputBuffer->InputEvents) { CurrentInput = CurrentInput->Flink; (*NumberOfEvents)++; } return STATUS_SUCCESS; } /* EOF */