[CONSRV]
[reactos.git] / win32ss / user / consrv / coninput.c
index cf55ff0..49263c6 100644 (file)
@@ -6,7 +6,7 @@
  * PROGRAMMERS:
  */
 
-/* INCLUDES ******************************************************************/
+/* INCLUDES *******************************************************************/
 
 #include "consrv.h"
 #include "conio.h"
@@ -15,7 +15,8 @@
 #define NDEBUG
 #include <debug.h>
 
-/* GLOBALS *******************************************************************/
+
+/* GLOBALS ********************************************************************/
 
 #define ConsoleInputUnicodeCharToAnsiChar(Console, dChar, sWChar) \
     WideCharToMultiByte((Console)->CodePage, 0, (sWChar), 1, (dChar), 1, NULL, NULL)
 #define ConsoleInputAnsiCharToUnicodeChar(Console, dWChar, sChar) \
     MultiByteToWideChar((Console)->CodePage, 0, (sChar), 1, (dWChar), 1)
 
-/* FUNCTIONS *****************************************************************/
 
-CSR_API(SrvReadConsole)
+typedef struct _GET_INPUT_INFO
 {
-    PCSRSS_READ_CONSOLE ReadConsoleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.ReadConsoleRequest;
-    PLIST_ENTRY CurrentEntry;
-    ConsoleInput *Input;
-    PCHAR Buffer;
-    PWCHAR UnicodeBuffer;
-    ULONG i = 0;
-    ULONG nNumberOfCharsToRead, CharSize;
-    PCSR_PROCESS ProcessData = CsrGetClientThread()->Process;
+    PCONSOLE_PROCESS_DATA ProcessData;
     PCSRSS_CONSOLE Console;
-    NTSTATUS Status;
-
-    DPRINT("SrvReadConsole\n");
-
-    CharSize = (ReadConsoleRequest->Unicode ? sizeof(WCHAR) : sizeof(CHAR));
-
-    nNumberOfCharsToRead = ReadConsoleRequest->NrCharactersToRead;
-
-    Buffer = (PCHAR)ReadConsoleRequest->Buffer;
-    UnicodeBuffer = (PWCHAR)Buffer;
-    if (!Win32CsrValidateBuffer(ProcessData, Buffer, nNumberOfCharsToRead, CharSize))
-        return STATUS_ACCESS_VIOLATION;
-
-    if (ReadConsoleRequest->NrCharactersRead * sizeof(WCHAR) > nNumberOfCharsToRead * CharSize)
-        return STATUS_INVALID_PARAMETER;
-
-    Status = ConioLockConsole(ProcessData, ReadConsoleRequest->ConsoleHandle,
-                              &Console, GENERIC_READ);
-    if (! NT_SUCCESS(Status))
-    {
-        return Status;
-    }
-    ReadConsoleRequest->EventHandle = ProcessData->ConsoleEvent;
-
-    Status = STATUS_PENDING; /* we haven't read anything (yet) */
-    if (Console->Mode & ENABLE_LINE_INPUT)
-    {
-        if (Console->LineBuffer == NULL)
-        {
-            /* Starting a new line */
-            Console->LineMaxSize = max(256, nNumberOfCharsToRead);
-            Console->LineBuffer = HeapAlloc(ConSrvHeap, 0, Console->LineMaxSize * sizeof(WCHAR));
-            if (Console->LineBuffer == NULL)
-            {
-                Status = STATUS_NO_MEMORY;
-                goto done;
-            }
-            Console->LineComplete = FALSE;
-            Console->LineUpPressed = FALSE;
-            Console->LineInsertToggle = 0;
-            Console->LineWakeupMask = ReadConsoleRequest->CtrlWakeupMask;
-            Console->LineSize = ReadConsoleRequest->NrCharactersRead;
-            Console->LinePos = Console->LineSize;
-            /* pre-filling the buffer is only allowed in the Unicode API,
-             * so we don't need to worry about conversion */
-            memcpy(Console->LineBuffer, Buffer, Console->LineSize * sizeof(WCHAR));
-            if (Console->LineSize == Console->LineMaxSize)
-            {
-                Console->LineComplete = TRUE;
-                Console->LinePos = 0;
-            }
-        }
+} GET_INPUT_INFO, *PGET_INPUT_INFO;
 
-        /* If we don't have a complete line yet, process the pending input */
-        while (!Console->LineComplete && !IsListEmpty(&Console->InputEvents))
-        {
-            /* remove input event from queue */
-            CurrentEntry = RemoveHeadList(&Console->InputEvents);
-            if (IsListEmpty(&Console->InputEvents))
-            {
-                ResetEvent(Console->ActiveEvent);
-            }
-            Input = CONTAINING_RECORD(CurrentEntry, ConsoleInput, ListEntry);
 
-            /* only pay attention to key down */
-            if (KEY_EVENT == Input->InputEvent.EventType
-                    && Input->InputEvent.Event.KeyEvent.bKeyDown)
-            {
-                LineInputKeyDown(Console, &Input->InputEvent.Event.KeyEvent);
-                ReadConsoleRequest->ControlKeyState = Input->InputEvent.Event.KeyEvent.dwControlKeyState;
-            }
-            HeapFree(ConSrvHeap, 0, Input);
-        }
-
-        /* Check if we have a complete line to read from */
-        if (Console->LineComplete)
-        {
-            while (i < nNumberOfCharsToRead && Console->LinePos != Console->LineSize)
-            {
-                WCHAR Char = Console->LineBuffer[Console->LinePos++];
-                if (ReadConsoleRequest->Unicode)
-                    UnicodeBuffer[i++] = Char;
-                else
-                    ConsoleInputUnicodeCharToAnsiChar(Console, &Buffer[i++], &Char);
-            }
-            if (Console->LinePos == Console->LineSize)
-            {
-                /* Entire line has been read */
-                HeapFree(ConSrvHeap, 0, Console->LineBuffer);
-                Console->LineBuffer = NULL;
-            }
-            Status = STATUS_SUCCESS;
-        }
-    }
-    else
-    {
-        /* Character input */
-        while (i < nNumberOfCharsToRead && !IsListEmpty(&Console->InputEvents))
-        {
-            /* remove input event from queue */
-            CurrentEntry = RemoveHeadList(&Console->InputEvents);
-            if (IsListEmpty(&Console->InputEvents))
-            {
-                ResetEvent(Console->ActiveEvent);
-            }
-            Input = CONTAINING_RECORD(CurrentEntry, ConsoleInput, ListEntry);
-
-            /* only pay attention to valid ascii chars, on key down */
-            if (KEY_EVENT == Input->InputEvent.EventType
-                    && Input->InputEvent.Event.KeyEvent.bKeyDown
-                    && Input->InputEvent.Event.KeyEvent.uChar.UnicodeChar != L'\0')
-            {
-                WCHAR Char = Input->InputEvent.Event.KeyEvent.uChar.UnicodeChar;
-                if (ReadConsoleRequest->Unicode)
-                    UnicodeBuffer[i++] = Char;
-                else
-                    ConsoleInputUnicodeCharToAnsiChar(Console, &Buffer[i++], &Char);
-                Status = STATUS_SUCCESS; /* did read something */
-            }
-            HeapFree(ConSrvHeap, 0, Input);
-        }
-    }
-done:
-    ReadConsoleRequest->NrCharactersRead = i;
-    ConioUnlockConsole(Console);
-
-    return Status;
-}
+/* PRIVATE FUNCTIONS **********************************************************/
 
 static VOID FASTCALL
 ConioInputEventToAnsi(PCSRSS_CONSOLE Console, PINPUT_RECORD InputEvent)
@@ -217,7 +86,13 @@ ConioProcessChar(PCSRSS_CONSOLE Console,
         return STATUS_INSUFFICIENT_RESOURCES;
     ConInRec->InputEvent = *InputEvent;
     InsertTailList(&Console->InputEvents, &ConInRec->ListEntry);
+
     SetEvent(Console->ActiveEvent);
+    CsrNotifyWait(&Console->ReadWaitQueue,
+                  WaitAny,
+                  NULL,
+                  NULL);
+
     return STATUS_SUCCESS;
 }
 
@@ -349,16 +224,16 @@ ConioProcessKey(MSG *msg, PCSRSS_CONSOLE Console, BOOL TextMode)
     if (NotChar)
         LastVirtualKey = msg->wParam;
 
-    DPRINT  ("csrss: %s %s %s %s %02x %02x '%lc' %04x\n",
-             Down ? "down" : "up  ",
-             (msg->message == WM_CHAR || msg->message == WM_SYSCHAR) ?
-             "char" : "key ",
-             Fake ? "fake" : "real",
-             NotChar ? "notc" : "char",
-             VirtualScanCode,
-             VirtualKeyCode,
-             (UnicodeChar >= L' ') ? UnicodeChar : L'.',
-             ShiftState);
+    DPRINT("CONSRV: %s %s %s %s %02x %02x '%lc' %04x\n",
+           Down ? "down" : "up  ",
+           (msg->message == WM_CHAR || msg->message == WM_SYSCHAR) ?
+           "char" : "key ",
+           Fake ? "fake" : "real",
+           NotChar ? "notc" : "char",
+           VirtualScanCode,
+           VirtualKeyCode,
+           (UnicodeChar >= L' ') ? UnicodeChar : L'.',
+           ShiftState);
 
     if (Fake)
         return;
@@ -370,13 +245,15 @@ ConioProcessKey(MSG *msg, PCSRSS_CONSOLE Console, BOOL TextMode)
              (er.Event.KeyEvent.wVirtualKeyCode == 'C')) &&
             (er.Event.KeyEvent.dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED) || KeyState[VK_CONTROL] & 0x80))
     {
-        PCSR_PROCESS current;
+        PCONSOLE_PROCESS_DATA current;
         PLIST_ENTRY current_entry;
+
         DPRINT1("Console_Api Ctrl-C\n");
+
         current_entry = Console->ProcessList.Flink;
         while (current_entry != &Console->ProcessList)
         {
-            current = CONTAINING_RECORD(current_entry, CSR_PROCESS, ConsoleLink);
+            current = CONTAINING_RECORD(current_entry, CONSOLE_PROCESS_DATA, ConsoleLink);
             current_entry = current_entry->Flink;
             ConioConsoleCtrlEvent((DWORD)CTRL_C_EVENT, current);
         }
@@ -425,222 +302,390 @@ ConioProcessKey(MSG *msg, PCSRSS_CONSOLE Console, BOOL TextMode)
     ConioProcessChar(Console, &er);
 }
 
-CSR_API(CsrReadInputEvent)
+static NTSTATUS
+WaitBeforeReading(IN PGET_INPUT_INFO InputInfo,
+                  IN PCSR_API_MESSAGE ApiMessage,
+                  IN CSR_WAIT_FUNCTION WaitFunction OPTIONAL,
+                  IN BOOL CreateWaitBlock OPTIONAL)
 {
-    PCSRSS_READ_INPUT ReadInputRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.ReadInputRequest;
-    PLIST_ENTRY CurrentEntry;
-    PCSR_PROCESS ProcessData = CsrGetClientThread()->Process;
-    PCSRSS_CONSOLE Console;
-    NTSTATUS Status;
-    BOOLEAN Done = FALSE;
-    ConsoleInput *Input;
+    if (CreateWaitBlock)
+    {
+        PGET_INPUT_INFO CapturedInputInfo;
 
-    DPRINT("CsrReadInputEvent\n");
+        CapturedInputInfo = HeapAlloc(ConSrvHeap, 0, sizeof(GET_INPUT_INFO));
+        if (!CapturedInputInfo) return STATUS_NO_MEMORY;
 
-    ReadInputRequest->Event = ProcessData->ConsoleEvent;
+        memmove(CapturedInputInfo, InputInfo, sizeof(GET_INPUT_INFO));
 
-    Status = ConioLockConsole(ProcessData, ReadInputRequest->ConsoleHandle, &Console, GENERIC_READ);
-    if (! NT_SUCCESS(Status))
-    {
-        return Status;
+        if (!CsrCreateWait(&InputInfo->Console->ReadWaitQueue,
+                           WaitFunction,
+                           CsrGetClientThread(),
+                           ApiMessage,
+                           CapturedInputInfo,
+                           NULL))
+        {
+            HeapFree(ConSrvHeap, 0, CapturedInputInfo);
+            return STATUS_NO_MEMORY;
+        }
     }
 
-    /* only get input if there is any */
-    CurrentEntry = Console->InputEvents.Flink;
-    while (CurrentEntry != &Console->InputEvents)
+    /* Wait for input */
+    return STATUS_PENDING;
+}
+
+static NTSTATUS
+ReadInputBuffer(IN PGET_INPUT_INFO InputInfo,
+                IN BOOL Wait,
+                IN PCSR_API_MESSAGE ApiMessage,
+                IN BOOL CreateWaitBlock OPTIONAL);
+
+// Wait function CSR_WAIT_FUNCTION
+static BOOLEAN
+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;
+    PCSRSS_GET_CONSOLE_INPUT GetConsoleInputRequest = &((PCONSOLE_API_MESSAGE)WaitApiMessage)->Data.GetConsoleInputRequest;
+    PGET_INPUT_INFO InputInfo = (PGET_INPUT_INFO)WaitContext;
+
+    Status = ReadInputBuffer(InputInfo,
+                             GetConsoleInputRequest->bRead,
+                             WaitApiMessage,
+                             FALSE);
+
+    if (Status != STATUS_PENDING)
     {
-        Input = CONTAINING_RECORD(CurrentEntry, ConsoleInput, ListEntry);
-        CurrentEntry = CurrentEntry->Flink;
+        WaitApiMessage->Status = Status;
+        HeapFree(ConSrvHeap, 0, InputInfo);
+    }
+
+    return (Status == STATUS_PENDING ? FALSE : TRUE);
+}
 
-        if (Done)
+static NTSTATUS
+ReadInputBuffer(IN PGET_INPUT_INFO InputInfo,
+                IN BOOL Wait,   // TRUE --> Read ; FALSE --> Peek
+                IN PCSR_API_MESSAGE ApiMessage,
+                IN BOOL CreateWaitBlock OPTIONAL)
+{
+    if (IsListEmpty(&InputInfo->Console->InputEvents))
+    {
+        if (Wait)
+        {
+            return WaitBeforeReading(InputInfo,
+                                     ApiMessage,
+                                     ReadInputBufferThread,
+                                     CreateWaitBlock);
+        }
+        else
         {
-            ReadInputRequest->MoreEvents = TRUE;
-            break;
+            /* No input available and we don't wait, so we return success */
+            return STATUS_SUCCESS;
         }
+    }
+    else
+    {
+        PCSRSS_GET_CONSOLE_INPUT GetConsoleInputRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetConsoleInputRequest;
+        PLIST_ENTRY CurrentInput;
+        ConsoleInput* Input;
+        ULONG Length = GetConsoleInputRequest->Length;
+        PINPUT_RECORD InputRecord = GetConsoleInputRequest->InputRecord;
 
-        RemoveEntryList(&Input->ListEntry);
+        /* Only get input if there is any */
+        CurrentInput = InputInfo->Console->InputEvents.Flink;
 
-        if (!Done)
+        while ( CurrentInput != &InputInfo->Console->InputEvents &&
+                GetConsoleInputRequest->InputsRead < Length )
         {
-            ReadInputRequest->Input = Input->InputEvent;
-            if (ReadInputRequest->Unicode == FALSE)
+            Input = CONTAINING_RECORD(CurrentInput, ConsoleInput, ListEntry);
+
+            GetConsoleInputRequest->InputsRead++;
+            *InputRecord = Input->InputEvent;
+
+            if (GetConsoleInputRequest->Unicode == FALSE)
             {
-                ConioInputEventToAnsi(Console, &ReadInputRequest->Input);
+                ConioInputEventToAnsi(InputInfo->Console, InputRecord);
+            }
+
+            InputRecord++;
+            CurrentInput = CurrentInput->Flink;
+
+            if (Wait) // TRUE --> Read, we remove inputs from the buffer ; FALSE --> Peek, we keep inputs.
+            {
+                RemoveEntryList(&Input->ListEntry);
+                HeapFree(ConSrvHeap, 0, Input);
             }
-            Done = TRUE;
         }
 
-        HeapFree(ConSrvHeap, 0, Input);
+        if (IsListEmpty(&InputInfo->Console->InputEvents))
+        {
+            ResetEvent(InputInfo->Console->ActiveEvent);
+        }
+
+        /* We read all the inputs available, we return success */
+        return STATUS_SUCCESS;
     }
+}
 
-    if (Done)
-        Status = STATUS_SUCCESS;
-    else
-        Status = STATUS_PENDING;
+static NTSTATUS
+ReadChars(IN PGET_INPUT_INFO InputInfo,
+          IN PCSR_API_MESSAGE ApiMessage,
+          IN BOOL CreateWaitBlock OPTIONAL);
+
+// Wait function CSR_WAIT_FUNCTION
+static BOOLEAN
+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;
+
+    Status = ReadChars(InputInfo,
+                       WaitApiMessage,
+                       FALSE);
 
-    if (IsListEmpty(&Console->InputEvents))
+    if (Status != STATUS_PENDING)
     {
-        ResetEvent(Console->ActiveEvent);
+        WaitApiMessage->Status = Status;
+        HeapFree(ConSrvHeap, 0, InputInfo);
     }
 
-    ConioUnlockConsole(Console);
-
-    return Status;
+    return (Status == STATUS_PENDING ? FALSE : TRUE);
 }
 
-CSR_API(SrvFlushConsoleInputBuffer)
+static NTSTATUS
+ReadChars(IN PGET_INPUT_INFO InputInfo,
+          IN PCSR_API_MESSAGE ApiMessage,
+          IN BOOL CreateWaitBlock OPTIONAL)
 {
-    PCSRSS_FLUSH_INPUT_BUFFER FlushInputBufferRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.FlushInputBufferRequest;
+    BOOL WaitForMoreToRead = TRUE; // TRUE : Wait if more to read ; FALSE : Don't wait.
+
+    PCSRSS_READ_CONSOLE ReadConsoleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.ReadConsoleRequest;
     PLIST_ENTRY CurrentEntry;
-    PCSRSS_CONSOLE Console;
-    ConsoleInput* Input;
-    NTSTATUS Status;
+    ConsoleInput *Input;
+    PCHAR Buffer = (PCHAR)ReadConsoleRequest->Buffer;
+    PWCHAR UnicodeBuffer = (PWCHAR)Buffer;
+    ULONG nNumberOfCharsToRead = ReadConsoleRequest->NrCharactersToRead;
 
-    DPRINT("SrvFlushConsoleInputBuffer\n");
+    /* We haven't read anything (yet) */
 
-    Status = ConioLockConsole(CsrGetClientThread()->Process,
-                              FlushInputBufferRequest->ConsoleInput,
-                              &Console,
-                              GENERIC_WRITE);
-    if(! NT_SUCCESS(Status))
+    if (InputInfo->Console->Mode & ENABLE_LINE_INPUT)
     {
-        return Status;
-    }
+        if (InputInfo->Console->LineBuffer == NULL)
+        {
+            /* Starting a new line */
+            InputInfo->Console->LineMaxSize = max(256, nNumberOfCharsToRead);
+            InputInfo->Console->LineBuffer = HeapAlloc(ConSrvHeap, 0, InputInfo->Console->LineMaxSize * sizeof(WCHAR));
+            if (InputInfo->Console->LineBuffer == NULL)
+            {
+                return STATUS_NO_MEMORY;
+            }
+            InputInfo->Console->LineComplete = FALSE;
+            InputInfo->Console->LineUpPressed = FALSE;
+            InputInfo->Console->LineInsertToggle = 0;
+            InputInfo->Console->LineWakeupMask = ReadConsoleRequest->CtrlWakeupMask;
+            InputInfo->Console->LineSize = ReadConsoleRequest->NrCharactersRead;
+            InputInfo->Console->LinePos = InputInfo->Console->LineSize;
+
+            /*
+             * Pre-filling the buffer is only allowed in the Unicode API,
+             * so we don't need to worry about ANSI <-> Unicode conversion.
+             */
+            memcpy(InputInfo->Console->LineBuffer, Buffer, InputInfo->Console->LineSize * sizeof(WCHAR));
+            if (InputInfo->Console->LineSize == InputInfo->Console->LineMaxSize)
+            {
+                InputInfo->Console->LineComplete = TRUE;
+                InputInfo->Console->LinePos = 0;
+            }
+        }
 
-    /* Discard all entries in the input event queue */
-    while (!IsListEmpty(&Console->InputEvents))
-    {
-        CurrentEntry = RemoveHeadList(&Console->InputEvents);
-        Input = CONTAINING_RECORD(CurrentEntry, ConsoleInput, ListEntry);
-        /* Destroy the event */
-        HeapFree(ConSrvHeap, 0, Input);
-    }
-    ResetEvent(Console->ActiveEvent);
+        /* If we don't have a complete line yet, process the pending input */
+        while ( !InputInfo->Console->LineComplete &&
+                !IsListEmpty(&InputInfo->Console->InputEvents) )
+        {
+            /* Remove input event from queue */
+            CurrentEntry = RemoveHeadList(&InputInfo->Console->InputEvents);
+            if (IsListEmpty(&InputInfo->Console->InputEvents))
+            {
+                ResetEvent(InputInfo->Console->ActiveEvent);
+            }
+            Input = CONTAINING_RECORD(CurrentEntry, ConsoleInput, ListEntry);
 
-    ConioUnlockConsole(Console);
+            /* Only pay attention to key down */
+            if (KEY_EVENT == Input->InputEvent.EventType
+                    && Input->InputEvent.Event.KeyEvent.bKeyDown)
+            {
+                LineInputKeyDown(InputInfo->Console, &Input->InputEvent.Event.KeyEvent);
+                ReadConsoleRequest->ControlKeyState = Input->InputEvent.Event.KeyEvent.dwControlKeyState;
+            }
+            HeapFree(ConSrvHeap, 0, Input);
+        }
 
-    return STATUS_SUCCESS;
-}
+        /* Check if we have a complete line to read from */
+        if (InputInfo->Console->LineComplete)
+        {
+            while ( ReadConsoleRequest->NrCharactersRead < nNumberOfCharsToRead &&
+                    InputInfo->Console->LinePos != InputInfo->Console->LineSize )
+            {
+                WCHAR Char = InputInfo->Console->LineBuffer[InputInfo->Console->LinePos++];
 
-CSR_API(SrvGetConsoleNumberOfInputEvents)
-{
-    NTSTATUS Status;
-    PCSRSS_GET_NUM_INPUT_EVENTS GetNumInputEventsRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetNumInputEventsRequest;
-    PCSRSS_CONSOLE Console;
-    PLIST_ENTRY CurrentItem;
-    DWORD NumEvents;
+                if (ReadConsoleRequest->Unicode)
+                {
+                    UnicodeBuffer[ReadConsoleRequest->NrCharactersRead] = Char;
+                }
+                else
+                {
+                    ConsoleInputUnicodeCharToAnsiChar(InputInfo->Console,
+                                                      &Buffer[ReadConsoleRequest->NrCharactersRead],
+                                                      &Char);
+                }
 
-    DPRINT("SrvGetConsoleNumberOfInputEvents\n");
+                ReadConsoleRequest->NrCharactersRead++;
+            }
 
-    Status = ConioLockConsole(CsrGetClientThread()->Process, GetNumInputEventsRequest->ConsoleHandle, &Console, GENERIC_READ);
-    if (! NT_SUCCESS(Status))
-    {
-        return Status;
+            if (InputInfo->Console->LinePos == InputInfo->Console->LineSize)
+            {
+                /* Entire line has been read */
+                HeapFree(ConSrvHeap, 0, InputInfo->Console->LineBuffer);
+                InputInfo->Console->LineBuffer = NULL;
+            }
+
+            WaitForMoreToRead = FALSE;
+        }
     }
+    else
+    {
+        /* Character input */
+        while ( ReadConsoleRequest->NrCharactersRead < nNumberOfCharsToRead &&
+                !IsListEmpty(&InputInfo->Console->InputEvents) )
+        {
+            /* Remove input event from queue */
+            CurrentEntry = RemoveHeadList(&InputInfo->Console->InputEvents);
+            if (IsListEmpty(&InputInfo->Console->InputEvents))
+            {
+                ResetEvent(InputInfo->Console->ActiveEvent);
+            }
+            Input = CONTAINING_RECORD(CurrentEntry, ConsoleInput, ListEntry);
 
-    CurrentItem = Console->InputEvents.Flink;
-    NumEvents = 0;
+            /* Only pay attention to valid ascii chars, on key down */
+            if (KEY_EVENT == Input->InputEvent.EventType
+                    && Input->InputEvent.Event.KeyEvent.bKeyDown
+                    && Input->InputEvent.Event.KeyEvent.uChar.UnicodeChar != L'\0')
+            {
+                WCHAR Char = Input->InputEvent.Event.KeyEvent.uChar.UnicodeChar;
 
-    /* If there are any events ... */
-    while (CurrentItem != &Console->InputEvents)
-    {
-        CurrentItem = CurrentItem->Flink;
-        NumEvents++;
-    }
+                if (ReadConsoleRequest->Unicode)
+                {
+                    UnicodeBuffer[ReadConsoleRequest->NrCharactersRead] = Char;
+                }
+                else
+                {
+                    ConsoleInputUnicodeCharToAnsiChar(InputInfo->Console,
+                                                      &Buffer[ReadConsoleRequest->NrCharactersRead],
+                                                      &Char);
+                }
 
-    ConioUnlockConsole(Console);
+                ReadConsoleRequest->NrCharactersRead++;
 
-    GetNumInputEventsRequest->NumInputEvents = NumEvents;
+                /* Did read something */
+                WaitForMoreToRead = FALSE;
+            }
+            HeapFree(ConSrvHeap, 0, Input);
+        }
+    }
 
-    return STATUS_SUCCESS;
+    /* We haven't completed a read, so start a wait */
+    if (WaitForMoreToRead == TRUE)
+    {
+        return WaitBeforeReading(InputInfo,
+                                 ApiMessage,
+                                 ReadCharsThread,
+                                 CreateWaitBlock);
+    }
+    else /* We read all what we wanted, we return success */
+    {
+        return STATUS_SUCCESS;
+    }
 }
 
+
+/* PUBLIC APIS ****************************************************************/
+
 CSR_API(SrvGetConsoleInput)
 {
     NTSTATUS Status;
-    PCSRSS_PEEK_CONSOLE_INPUT PeekConsoleInputRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.PeekConsoleInputRequest;
-    PCSR_PROCESS ProcessData = CsrGetClientThread()->Process;
+    PCSRSS_GET_CONSOLE_INPUT GetConsoleInputRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetConsoleInputRequest;
+    PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process);
     PCSRSS_CONSOLE Console;
-    DWORD Length;
-    PLIST_ENTRY CurrentItem;
-    PINPUT_RECORD InputRecord;
-    ConsoleInput* Item;
-    UINT NumItems;
+    GET_INPUT_INFO InputInfo;
 
     DPRINT("SrvGetConsoleInput\n");
 
-    Status = ConioLockConsole(ProcessData, PeekConsoleInputRequest->ConsoleHandle, &Console, GENERIC_READ);
-    if(! NT_SUCCESS(Status))
+    if (!CsrValidateMessageBuffer(ApiMessage,
+                                  (PVOID*)&GetConsoleInputRequest->InputRecord,
+                                  GetConsoleInputRequest->Length,
+                                  sizeof(INPUT_RECORD)))
     {
-        return Status;
-    }
-
-    InputRecord = PeekConsoleInputRequest->InputRecord;
-    Length = PeekConsoleInputRequest->Length;
-
-    if (!Win32CsrValidateBuffer(ProcessData, InputRecord, Length, sizeof(INPUT_RECORD)))
-    {
-        ConioUnlockConsole(Console);
-        return STATUS_ACCESS_VIOLATION;
+        return STATUS_INVALID_PARAMETER;
     }
 
-    NumItems = 0;
-
-    if (! IsListEmpty(&Console->InputEvents))
-    {
-        CurrentItem = Console->InputEvents.Flink;
-
-        while (CurrentItem != &Console->InputEvents && NumItems < Length)
-        {
-            Item = CONTAINING_RECORD(CurrentItem, ConsoleInput, ListEntry);
+    Status = ConioLockConsole(ProcessData, GetConsoleInputRequest->ConsoleHandle, &Console, GENERIC_READ);
+    if(!NT_SUCCESS(Status)) return Status;
 
-            ++NumItems;
-            *InputRecord = Item->InputEvent;
+    GetConsoleInputRequest->InputsRead = 0;
 
-            if (PeekConsoleInputRequest->Unicode == FALSE)
-            {
-                ConioInputEventToAnsi(Console, InputRecord);
-            }
+    InputInfo.ProcessData = ProcessData; // ConsoleGetPerProcessData(CsrGetClientThread()->Process);
+    InputInfo.Console     = Console;
 
-            InputRecord++;
-            CurrentItem = CurrentItem->Flink;
-        }
-    }
+    Status = ReadInputBuffer(&InputInfo,
+                             GetConsoleInputRequest->bRead,
+                             ApiMessage,
+                             TRUE);
 
     ConioUnlockConsole(Console);
 
-    PeekConsoleInputRequest->Length = NumItems;
+    if (Status == STATUS_PENDING)
+        *ReplyCode = CsrReplyPending;
 
-    return STATUS_SUCCESS;
+    return Status;
 }
 
 CSR_API(SrvWriteConsoleInput)
 {
+    NTSTATUS Status;
     PCSRSS_WRITE_CONSOLE_INPUT WriteConsoleInputRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.WriteConsoleInputRequest;
     PINPUT_RECORD InputRecord;
-    PCSR_PROCESS ProcessData = CsrGetClientThread()->Process;
+    PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process);
     PCSRSS_CONSOLE Console;
-    NTSTATUS Status;
     DWORD Length;
     DWORD i;
 
     DPRINT("SrvWriteConsoleInput\n");
 
-    Status = ConioLockConsole(ProcessData, WriteConsoleInputRequest->ConsoleHandle, &Console, GENERIC_WRITE);
-    if (! NT_SUCCESS(Status))
+    if (!CsrValidateMessageBuffer(ApiMessage,
+                                  (PVOID*)&WriteConsoleInputRequest->InputRecord,
+                                  WriteConsoleInputRequest->Length,
+                                  sizeof(INPUT_RECORD)))
     {
-        return Status;
+        return STATUS_INVALID_PARAMETER;
     }
 
+    Status = ConioLockConsole(ProcessData, WriteConsoleInputRequest->ConsoleHandle, &Console, GENERIC_WRITE);
+    if (!NT_SUCCESS(Status)) return Status;
+
     InputRecord = WriteConsoleInputRequest->InputRecord;
     Length = WriteConsoleInputRequest->Length;
 
-    if (!Win32CsrValidateBuffer(ProcessData, InputRecord, Length, sizeof(INPUT_RECORD)))
-    {
-        ConioUnlockConsole(Console);
-        return STATUS_ACCESS_VIOLATION;
-    }
-
     for (i = 0; i < Length && NT_SUCCESS(Status); i++)
     {
         if (!WriteConsoleInputRequest->Unicode &&
@@ -651,6 +696,7 @@ CSR_API(SrvWriteConsoleInput)
                                               &InputRecord->Event.KeyEvent.uChar.UnicodeChar,
                                               &AsciiChar);
         }
+
         Status = ConioProcessChar(Console, InputRecord++);
     }
 
@@ -661,4 +707,109 @@ CSR_API(SrvWriteConsoleInput)
     return Status;
 }
 
+CSR_API(SrvReadConsole)
+{
+    NTSTATUS Status;
+    PCSRSS_READ_CONSOLE ReadConsoleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.ReadConsoleRequest;
+    PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process);
+    PCSRSS_CONSOLE Console;
+    GET_INPUT_INFO InputInfo;
+
+    DPRINT("SrvReadConsole\n");
+
+    if (!CsrValidateMessageBuffer(ApiMessage,
+                                  (PVOID*)&ReadConsoleRequest->Buffer,
+                                  ReadConsoleRequest->BufferSize,
+                                  sizeof(BYTE)))
+    {
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    // if (Request->Data.ReadConsoleRequest.NrCharactersRead * sizeof(WCHAR) > nNumberOfCharsToRead * CharSize)
+    if (ReadConsoleRequest->NrCharactersRead > ReadConsoleRequest->NrCharactersToRead)
+    {
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    Status = ConioLockConsole(ProcessData, ReadConsoleRequest->ConsoleHandle, &Console, GENERIC_READ);
+    if (!NT_SUCCESS(Status)) return Status;
+
+    ReadConsoleRequest->NrCharactersRead = 0;
+
+    InputInfo.ProcessData = ProcessData; // ConsoleGetPerProcessData(CsrGetClientThread()->Process);
+    InputInfo.Console     = Console;
+
+    Status = ReadChars(&InputInfo,
+                       ApiMessage,
+                       TRUE);
+
+    ConioUnlockConsole(Console);
+
+    if (Status == STATUS_PENDING)
+        *ReplyCode = CsrReplyPending;
+
+    return Status;
+}
+
+CSR_API(SrvFlushConsoleInputBuffer)
+{
+    NTSTATUS Status;
+    PCSRSS_FLUSH_INPUT_BUFFER FlushInputBufferRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.FlushInputBufferRequest;
+    PLIST_ENTRY CurrentEntry;
+    PCSRSS_CONSOLE Console;
+    ConsoleInput* Input;
+
+    DPRINT("SrvFlushConsoleInputBuffer\n");
+
+    Status = ConioLockConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process),
+                              FlushInputBufferRequest->ConsoleInput,
+                              &Console,
+                              GENERIC_WRITE);
+    if(!NT_SUCCESS(Status)) return Status;
+
+    /* Discard all entries in the input event queue */
+    while (!IsListEmpty(&Console->InputEvents))
+    {
+        CurrentEntry = RemoveHeadList(&Console->InputEvents);
+        Input = CONTAINING_RECORD(CurrentEntry, ConsoleInput, ListEntry);
+        /* Destroy the event */
+        HeapFree(ConSrvHeap, 0, Input);
+    }
+    ResetEvent(Console->ActiveEvent);
+
+    ConioUnlockConsole(Console);
+
+    return STATUS_SUCCESS;
+}
+
+CSR_API(SrvGetConsoleNumberOfInputEvents)
+{
+    NTSTATUS Status;
+    PCSRSS_GET_NUM_INPUT_EVENTS GetNumInputEventsRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetNumInputEventsRequest;
+    PCSRSS_CONSOLE Console;
+    PLIST_ENTRY CurrentInput;
+    DWORD NumEvents;
+
+    DPRINT("SrvGetConsoleNumberOfInputEvents\n");
+
+    Status = ConioLockConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), GetNumInputEventsRequest->ConsoleHandle, &Console, GENERIC_READ);
+    if (!NT_SUCCESS(Status)) return Status;
+
+    CurrentInput = Console->InputEvents.Flink;
+    NumEvents = 0;
+
+    /* If there are any events ... */
+    while (CurrentInput != &Console->InputEvents)
+    {
+        CurrentInput = CurrentInput->Flink;
+        NumEvents++;
+    }
+
+    ConioUnlockConsole(Console);
+
+    GetNumInputEventsRequest->NumInputEvents = NumEvents;
+
+    return STATUS_SUCCESS;
+}
+
 /* EOF */