[CONSRV]: Use an optional parameter for passing things to the "line discipline" funct...
[reactos.git] / win32ss / user / winsrv / consrv / frontends / terminal.c
index b2ba1a9..5a59492 100644 (file)
 #define NDEBUG
 #include <debug.h>
 
+
+
+
+
+/********** HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK ************/
+
+/* GLOBALS ********************************************************************/
+
+/*
+ * 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 ConsoleInputUnicodeCharToAnsiChar(Console, dChar, sWChar) \
+    ASSERT((ULONG_PTR)dChar != (ULONG_PTR)sWChar); \
+    WideCharToMultiByte((Console)->InputCodePage, 0, (sWChar), 1, (dChar), 1, NULL, NULL)
+
+#define ConsoleInputAnsiCharToUnicodeChar(Console, dWChar, sChar) \
+    ASSERT((ULONG_PTR)dWChar != (ULONG_PTR)sChar); \
+    MultiByteToWideChar((Console)->InputCodePage, 0, (sChar), 1, (dWChar), 1)
+
+typedef struct ConsoleInput_t
+{
+    LIST_ENTRY ListEntry;
+    INPUT_RECORD InputEvent;
+} ConsoleInput;
+
+
+/* PRIVATE FUNCTIONS **********************************************************/
+
+#if 0
+
+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;
+        ConsoleInputUnicodeCharToAnsiChar(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;
+        ConsoleInputAnsiCharToUnicodeChar(Console,
+                                          &InputEvent->Event.KeyEvent.uChar.UnicodeChar,
+                                          &AsciiChar);
+    }
+}
+
+#endif
+
+/********** HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK ************/
+
+
+
+
+
+
+
+
 /* CONSRV TERMINAL FRONTENDS INTERFACE ****************************************/
 
 /***************/
@@ -67,7 +137,7 @@ NTSTATUS (NTAPI *FRONTEND_UNLOAD)(IN OUT PFRONTEND FrontEnd);
  * NOTE: Each entry of the table should be retrieved when loading a front-end
  *       (examples of the CSR servers which register some data for CSRSS).
  */
-struct
+static struct
 {
     CHAR            FrontEndName[80];
     FRONTEND_LOAD   FrontEndLoad;
@@ -190,7 +260,7 @@ ConSrvDeinitTerminal(IN OUT PTERMINAL Terminal)
 
 static NTSTATUS NTAPI
 ConSrvTermInitTerminal(IN OUT PTERMINAL This,
-                  IN PCONSOLE Console)
+                       IN PCONSOLE Console)
 {
     NTSTATUS Status;
     PFRONTEND FrontEnd = This->Data;
@@ -203,6 +273,8 @@ ConSrvTermInitTerminal(IN OUT PTERMINAL This,
     Console->FrontEndIFace = *FrontEnd;
 
     Status = FrontEnd->Vtbl->InitFrontEnd(FrontEnd, FrontEnd->Console);
+    if (!NT_SUCCESS(Status))
+        DPRINT1("InitFrontEnd failed, Status = 0x%08lx\n", Status);
 
     /** HACK HACK!! Be sure FrontEndIFace is correctly updated in the console!! **/
     DPRINT1("Using FrontEndIFace HACK(2), should be removed after proper implementation!\n");
@@ -218,31 +290,371 @@ ConSrvTermDeinitTerminal(IN OUT PTERMINAL This)
     FrontEnd->Vtbl->DeinitFrontEnd(FrontEnd);
 }
 
-static VOID NTAPI
-ConSrvTermDrawRegion(IN OUT PTERMINAL This,
-                SMALL_RECT* Region)
+
+
+/************ Line discipline ***************/
+
+static NTSTATUS NTAPI
+ConSrvTermReadStream(IN OUT PTERMINAL This,
+                     IN BOOLEAN Unicode,
+                     /**PWCHAR Buffer,**/
+                     OUT PVOID Buffer,
+                     IN OUT PCONSOLE_READCONSOLE_CONTROL ReadControl,
+                     IN PVOID Parameter OPTIONAL,
+                     IN ULONG NumCharsToRead,
+                     OUT PULONG NumCharsRead OPTIONAL)
 {
     PFRONTEND FrontEnd = This->Data;
-    FrontEnd->Vtbl->DrawRegion(FrontEnd, Region);
+    PCONSRV_CONSOLE Console = FrontEnd->Console;
+    PCONSOLE_INPUT_BUFFER InputBuffer = &Console->InputBuffer;
+    PUNICODE_STRING ExeName = Parameter;
+
+    // STATUS_PENDING : Wait if more to read ; STATUS_SUCCESS : Don't wait.
+    NTSTATUS Status = STATUS_PENDING;
+
+    PLIST_ENTRY CurrentEntry;
+    ConsoleInput *Input;
+    ULONG i;
+
+    /* Validity checks */
+    // ASSERT(Console == InputBuffer->Header.Console);
+    ASSERT((Buffer != NULL) || (Buffer == NULL && NumCharsToRead == 0));
+
+    /* We haven't read anything (yet) */
+    i = ReadControl->nInitialChars;
+
+    if (InputBuffer->Mode & ENABLE_LINE_INPUT)
+    {
+        /* COOKED mode, call the line discipline */
+
+        if (Console->LineBuffer == NULL)
+        {
+            /* Starting a new line */
+            Console->LineMaxSize = max(256, NumCharsToRead);
+
+            Console->LineBuffer = ConsoleAllocHeap(0, Console->LineMaxSize * sizeof(WCHAR));
+            if (Console->LineBuffer == NULL) return STATUS_NO_MEMORY;
+
+            Console->LinePos = Console->LineSize = ReadControl->nInitialChars;
+            Console->LineComplete = Console->LineUpPressed = FALSE;
+            Console->LineInsertToggle = Console->InsertMode;
+            Console->LineWakeupMask = ReadControl->dwCtrlWakeupMask;
+
+            /*
+             * Pre-filling the buffer is only allowed in the Unicode API,
+             * so we don't need to worry about ANSI <-> Unicode conversion.
+             */
+            memcpy(Console->LineBuffer, Buffer, Console->LineSize * sizeof(WCHAR));
+            if (Console->LineSize == Console->LineMaxSize)
+            {
+                Console->LineComplete = TRUE;
+                Console->LinePos = 0;
+            }
+        }
+
+        /* If we don't have a complete line yet, process the pending input */
+        while (!Console->LineComplete && !IsListEmpty(&InputBuffer->InputEvents))
+        {
+            /* Remove input event from queue */
+            CurrentEntry = RemoveHeadList(&InputBuffer->InputEvents);
+            if (IsListEmpty(&InputBuffer->InputEvents))
+            {
+                ResetEvent(InputBuffer->ActiveEvent);
+            }
+            Input = CONTAINING_RECORD(CurrentEntry, ConsoleInput, ListEntry);
+
+            /* Only pay attention to key down */
+            if (Input->InputEvent.EventType == KEY_EVENT &&
+                Input->InputEvent.Event.KeyEvent.bKeyDown)
+            {
+                LineInputKeyDown(Console, ExeName,
+                                 &Input->InputEvent.Event.KeyEvent);
+                ReadControl->dwControlKeyState = Input->InputEvent.Event.KeyEvent.dwControlKeyState;
+            }
+            ConsoleFreeHeap(Input);
+        }
+
+        /* Check if we have a complete line to read from */
+        if (Console->LineComplete)
+        {
+            while (i < NumCharsToRead && Console->LinePos != Console->LineSize)
+            {
+                WCHAR Char = Console->LineBuffer[Console->LinePos++];
+
+                if (Unicode)
+                {
+                    ((PWCHAR)Buffer)[i] = Char;
+                }
+                else
+                {
+                    ConsoleInputUnicodeCharToAnsiChar(Console, &((PCHAR)Buffer)[i], &Char);
+                }
+                ++i;
+            }
+
+            if (Console->LinePos == Console->LineSize)
+            {
+                /* Entire line has been read */
+                ConsoleFreeHeap(Console->LineBuffer);
+                Console->LineBuffer = NULL;
+            }
+
+            Status = STATUS_SUCCESS;
+        }
+    }
+    else
+    {
+        /* RAW mode */
+
+        /* Character input */
+        while (i < NumCharsToRead && !IsListEmpty(&InputBuffer->InputEvents))
+        {
+            /* Remove input event from queue */
+            CurrentEntry = RemoveHeadList(&InputBuffer->InputEvents);
+            if (IsListEmpty(&InputBuffer->InputEvents))
+            {
+                ResetEvent(InputBuffer->ActiveEvent);
+            }
+            Input = CONTAINING_RECORD(CurrentEntry, ConsoleInput, ListEntry);
+
+            /* Only pay attention to valid characters, on key down */
+            if (Input->InputEvent.EventType == KEY_EVENT  &&
+                Input->InputEvent.Event.KeyEvent.bKeyDown &&
+                Input->InputEvent.Event.KeyEvent.uChar.UnicodeChar != L'\0')
+            {
+                WCHAR Char = Input->InputEvent.Event.KeyEvent.uChar.UnicodeChar;
+
+                if (Unicode)
+                {
+                    ((PWCHAR)Buffer)[i] = Char;
+                }
+                else
+                {
+                    ConsoleInputUnicodeCharToAnsiChar(Console, &((PCHAR)Buffer)[i], &Char);
+                }
+                ++i;
+
+                /* Did read something */
+                Status = STATUS_SUCCESS;
+            }
+            ConsoleFreeHeap(Input);
+        }
+    }
+
+    // FIXME: Only set if Status == STATUS_SUCCESS ???
+    if (NumCharsRead) *NumCharsRead = i;
+
+    return Status;
 }
 
-static VOID NTAPI
+
+
+
+/* GLOBALS ********************************************************************/
+
+#define TAB_WIDTH   8
+
+// See condrv/text.c
+/*static*/ VOID
+ClearLineBuffer(PTEXTMODE_SCREEN_BUFFER Buff);
+
+static VOID
+ConioNextLine(PTEXTMODE_SCREEN_BUFFER Buff, PSMALL_RECT UpdateRect, PUINT ScrolledLines)
+{
+    /* If we hit bottom, slide the viewable screen */
+    if (++Buff->CursorPosition.Y == Buff->ScreenBufferSize.Y)
+    {
+        Buff->CursorPosition.Y--;
+        if (++Buff->VirtualY == Buff->ScreenBufferSize.Y)
+        {
+            Buff->VirtualY = 0;
+        }
+        (*ScrolledLines)++;
+        ClearLineBuffer(Buff);
+        if (UpdateRect->Top != 0)
+        {
+            UpdateRect->Top--;
+        }
+    }
+    UpdateRect->Left = 0;
+    UpdateRect->Right = Buff->ScreenBufferSize.X - 1;
+    UpdateRect->Bottom = Buff->CursorPosition.Y;
+}
+
+static NTSTATUS
+ConioWriteConsole(PFRONTEND FrontEnd,
+                  PTEXTMODE_SCREEN_BUFFER Buff,
+                  PWCHAR Buffer,
+                  DWORD Length,
+                  BOOL Attrib)
+{
+    PCONSRV_CONSOLE Console = FrontEnd->Console;
+
+    UINT i;
+    PCHAR_INFO Ptr;
+    SMALL_RECT UpdateRect;
+    SHORT CursorStartX, CursorStartY;
+    UINT ScrolledLines;
+
+    CursorStartX = Buff->CursorPosition.X;
+    CursorStartY = Buff->CursorPosition.Y;
+    UpdateRect.Left = Buff->ScreenBufferSize.X;
+    UpdateRect.Top = Buff->CursorPosition.Y;
+    UpdateRect.Right = -1;
+    UpdateRect.Bottom = Buff->CursorPosition.Y;
+    ScrolledLines = 0;
+
+    for (i = 0; i < Length; i++)
+    {
+        /*
+         * If we are in processed mode, interpret special characters and
+         * display them correctly. Otherwise, just put them into the buffer.
+         */
+        if (Buff->Mode & ENABLE_PROCESSED_OUTPUT)
+        {
+            /* --- CR --- */
+            if (Buffer[i] == L'\r')
+            {
+                Buff->CursorPosition.X = 0;
+                UpdateRect.Left = min(UpdateRect.Left, Buff->CursorPosition.X);
+                UpdateRect.Right = max(UpdateRect.Right, Buff->CursorPosition.X);
+                continue;
+            }
+            /* --- LF --- */
+            else if (Buffer[i] == L'\n')
+            {
+                Buff->CursorPosition.X = 0;
+                ConioNextLine(Buff, &UpdateRect, &ScrolledLines);
+                continue;
+            }
+            /* --- BS --- */
+            else if (Buffer[i] == L'\b')
+            {
+                /* Only handle BS if we're not on the first pos of the first line */
+                if (0 != Buff->CursorPosition.X || 0 != Buff->CursorPosition.Y)
+                {
+                    if (0 == Buff->CursorPosition.X)
+                    {
+                        /* slide virtual position up */
+                        Buff->CursorPosition.X = Buff->ScreenBufferSize.X - 1;
+                        Buff->CursorPosition.Y--;
+                        UpdateRect.Top = min(UpdateRect.Top, Buff->CursorPosition.Y);
+                    }
+                    else
+                    {
+                        Buff->CursorPosition.X--;
+                    }
+                    Ptr = ConioCoordToPointer(Buff, Buff->CursorPosition.X, Buff->CursorPosition.Y);
+                    Ptr->Char.UnicodeChar = L' ';
+                    Ptr->Attributes  = Buff->ScreenDefaultAttrib;
+                    UpdateRect.Left  = min(UpdateRect.Left, Buff->CursorPosition.X);
+                    UpdateRect.Right = max(UpdateRect.Right, Buff->CursorPosition.X);
+                }
+                continue;
+            }
+            /* --- TAB --- */
+            else if (Buffer[i] == L'\t')
+            {
+                UINT EndX;
+
+                UpdateRect.Left = min(UpdateRect.Left, Buff->CursorPosition.X);
+                EndX = (Buff->CursorPosition.X + TAB_WIDTH) & ~(TAB_WIDTH - 1);
+                EndX = min(EndX, (UINT)Buff->ScreenBufferSize.X);
+                Ptr = ConioCoordToPointer(Buff, Buff->CursorPosition.X, Buff->CursorPosition.Y);
+                while (Buff->CursorPosition.X < EndX)
+                {
+                    Ptr->Char.UnicodeChar = L' ';
+                    Ptr->Attributes = Buff->ScreenDefaultAttrib;
+                    ++Ptr;
+                    Buff->CursorPosition.X++;
+                }
+                UpdateRect.Right = max(UpdateRect.Right, Buff->CursorPosition.X - 1);
+                if (Buff->CursorPosition.X == Buff->ScreenBufferSize.X)
+                {
+                    if (Buff->Mode & ENABLE_WRAP_AT_EOL_OUTPUT)
+                    {
+                        Buff->CursorPosition.X = 0;
+                        ConioNextLine(Buff, &UpdateRect, &ScrolledLines);
+                    }
+                    else
+                    {
+                        Buff->CursorPosition.X--;
+                    }
+                }
+                continue;
+            }
+            /* --- BEL ---*/
+            else if (Buffer[i] == L'\a')
+            {
+                FrontEnd->Vtbl->RingBell(FrontEnd);
+                continue;
+            }
+        }
+        UpdateRect.Left  = min(UpdateRect.Left, Buff->CursorPosition.X);
+        UpdateRect.Right = max(UpdateRect.Right, Buff->CursorPosition.X);
+
+        Ptr = ConioCoordToPointer(Buff, Buff->CursorPosition.X, Buff->CursorPosition.Y);
+        Ptr->Char.UnicodeChar = Buffer[i];
+        if (Attrib) Ptr->Attributes = Buff->ScreenDefaultAttrib;
+
+        Buff->CursorPosition.X++;
+        if (Buff->CursorPosition.X == Buff->ScreenBufferSize.X)
+        {
+            if (Buff->Mode & ENABLE_WRAP_AT_EOL_OUTPUT)
+            {
+                Buff->CursorPosition.X = 0;
+                ConioNextLine(Buff, &UpdateRect, &ScrolledLines);
+            }
+            else
+            {
+                Buff->CursorPosition.X = CursorStartX;
+            }
+        }
+    }
+
+    if (!ConioIsRectEmpty(&UpdateRect) && (PCONSOLE_SCREEN_BUFFER)Buff == Console->ActiveBuffer)
+    {
+        // TermWriteStream(Console, &UpdateRect, CursorStartX, CursorStartY,
+                        // ScrolledLines, Buffer, Length);
+        FrontEnd->Vtbl->WriteStream(FrontEnd,
+                                    &UpdateRect,
+                                    CursorStartX,
+                                    CursorStartY,
+                                    ScrolledLines,
+                                    Buffer,
+                                    Length);
+    }
+
+    return STATUS_SUCCESS;
+}
+
+
+
+static NTSTATUS NTAPI
 ConSrvTermWriteStream(IN OUT PTERMINAL This,
-                 SMALL_RECT* Region,
-                 SHORT CursorStartX,
-                 SHORT CursorStartY,
-                 UINT ScrolledLines,
-                 PWCHAR Buffer,
-                 UINT Length)
+                      PTEXTMODE_SCREEN_BUFFER Buff,
+                      PWCHAR Buffer,
+                      DWORD Length,
+                      BOOL Attrib)
 {
     PFRONTEND FrontEnd = This->Data;
-    FrontEnd->Vtbl->WriteStream(FrontEnd,
-                                Region,
-                                CursorStartX,
-                                CursorStartY,
-                                ScrolledLines,
-                                Buffer,
-                                Length);
+    return ConioWriteConsole(FrontEnd,
+                             Buff,
+                             Buffer,
+                             Length,
+                             Attrib);
+}
+
+/************ Line discipline ***************/
+
+
+
+static VOID NTAPI
+ConSrvTermDrawRegion(IN OUT PTERMINAL This,
+                SMALL_RECT* Region)
+{
+    PFRONTEND FrontEnd = This->Data;
+    FrontEnd->Vtbl->DrawRegion(FrontEnd, Region);
 }
 
 static BOOL NTAPI
@@ -288,13 +700,6 @@ ConSrvTermReleaseScreenBuffer(IN OUT PTERMINAL This,
     FrontEnd->Vtbl->ReleaseScreenBuffer(FrontEnd, ScreenBuffer);
 }
 
-static VOID NTAPI
-ConSrvTermChangeTitle(IN OUT PTERMINAL This)
-{
-    PFRONTEND FrontEnd = This->Data;
-    FrontEnd->Vtbl->ChangeTitle(FrontEnd);
-}
-
 static VOID NTAPI
 ConSrvTermGetLargestConsoleWindowSize(IN OUT PTERMINAL This,
                                  PCOORD pSize)
@@ -303,16 +708,6 @@ ConSrvTermGetLargestConsoleWindowSize(IN OUT PTERMINAL This,
     FrontEnd->Vtbl->GetLargestConsoleWindowSize(FrontEnd, pSize);
 }
 
-/*
-static BOOL NTAPI
-ConSrvTermGetSelectionInfo(IN OUT PTERMINAL This,
-                      PCONSOLE_SELECTION_INFO pSelectionInfo)
-{
-    PFRONTEND FrontEnd = This->Data;
-    return FrontEnd->Vtbl->GetSelectionInfo(FrontEnd, pSelectionInfo);
-}
-*/
-
 static BOOL NTAPI
 ConSrvTermSetPalette(IN OUT PTERMINAL This,
                 HPALETTE PaletteHandle,
@@ -334,16 +729,17 @@ static TERMINAL_VTBL ConSrvTermVtbl =
 {
     ConSrvTermInitTerminal,
     ConSrvTermDeinitTerminal,
-    ConSrvTermDrawRegion,
+
+    ConSrvTermReadStream,
     ConSrvTermWriteStream,
+
+    ConSrvTermDrawRegion,
     ConSrvTermSetCursorInfo,
     ConSrvTermSetScreenInfo,
     ConSrvTermResizeTerminal,
     ConSrvTermSetActiveScreenBuffer,
     ConSrvTermReleaseScreenBuffer,
-    ConSrvTermChangeTitle,
     ConSrvTermGetLargestConsoleWindowSize,
-    // ConSrvTermGetSelectionInfo,
     ConSrvTermSetPalette,
     ConSrvTermShowMouseCursor,
 };