[WIN32SS][WINSRV] Fullwidth character handling Part 2 (#2240)
[reactos.git] / win32ss / user / winsrv / consrv / frontends / terminal.c
index b2ba1a9..bae13a8 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * COPYRIGHT:       See COPYING in the top level directory
  * PROJECT:         ReactOS Console Server DLL
- * FILE:            frontends/terminal.c
+ * FILE:            win32ss/user/winsrv/consrv/frontends/terminal.c
  * PURPOSE:         ConSrv terminal.
  * PROGRAMMERS:     Hermes Belusca-Maito (hermes.belusca@sfr.fr)
  */
@@ -9,6 +9,7 @@
 /* INCLUDES *******************************************************************/
 
 #include <consrv.h>
+#include "concfg/font.h"
 
 // #include "frontends/gui/guiterm.h"
 #ifdef TUITERM_COMPILE
 #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)
+
+/* 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 ****************************************/
 
 /***************/
 #ifdef TUITERM_COMPILE
 NTSTATUS NTAPI
 TuiLoadFrontEnd(IN OUT PFRONTEND FrontEnd,
-                IN OUT PCONSOLE_INFO ConsoleInfo,
-                IN OUT PVOID ExtraConsoleInfo,
-                IN ULONG ProcessId);
+                IN OUT PCONSOLE_STATE_INFO ConsoleInfo,
+                IN OUT PCONSOLE_INIT_INFO ConsoleInitInfo,
+                IN HANDLE ConsoleLeaderProcessHandle);
 NTSTATUS NTAPI
 TuiUnloadFrontEnd(IN OUT PFRONTEND FrontEnd);
 #endif
 
 NTSTATUS NTAPI
 GuiLoadFrontEnd(IN OUT PFRONTEND FrontEnd,
-                IN OUT PCONSOLE_INFO ConsoleInfo,
-                IN OUT PVOID ExtraConsoleInfo,
-                IN ULONG ProcessId);
+                IN OUT PCONSOLE_STATE_INFO ConsoleInfo,
+                IN OUT PCONSOLE_INIT_INFO ConsoleInitInfo,
+                IN HANDLE ConsoleLeaderProcessHandle);
 NTSTATUS NTAPI
 GuiUnloadFrontEnd(IN OUT PFRONTEND FrontEnd);
 /***************/
 
 typedef
 NTSTATUS (NTAPI *FRONTEND_LOAD)(IN OUT PFRONTEND FrontEnd,
-                                IN OUT PCONSOLE_INFO ConsoleInfo,
-                                IN OUT PVOID ExtraConsoleInfo,
-                                IN ULONG ProcessId);
+                                IN OUT PCONSOLE_STATE_INFO ConsoleInfo,
+                                IN OUT PCONSOLE_INIT_INFO ConsoleInitInfo,
+                                IN HANDLE ConsoleLeaderProcessHandle);
 
 typedef
 NTSTATUS (NTAPI *FRONTEND_UNLOAD)(IN OUT PFRONTEND FrontEnd);
@@ -67,7 +131,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;
@@ -84,9 +148,9 @@ struct
 
 static NTSTATUS
 ConSrvLoadFrontEnd(IN OUT PFRONTEND FrontEnd,
-                   IN OUT PCONSOLE_INFO ConsoleInfo,
-                   IN OUT PVOID ExtraConsoleInfo,
-                   IN ULONG ProcessId)
+                   IN OUT PCONSOLE_STATE_INFO ConsoleInfo,
+                   IN OUT PCONSOLE_INIT_INFO ConsoleInitInfo,
+                   IN HANDLE ConsoleLeaderProcessHandle)
 {
     NTSTATUS Status = STATUS_SUCCESS;
     ULONG i;
@@ -94,14 +158,14 @@ ConSrvLoadFrontEnd(IN OUT PFRONTEND FrontEnd,
     /*
      * Choose an adequate terminal front-end to load, and load it
      */
-    for (i = 0; i < sizeof(FrontEndLoadingMethods) / sizeof(FrontEndLoadingMethods[0]); ++i)
+    for (i = 0; i < ARRAYSIZE(FrontEndLoadingMethods); ++i)
     {
         DPRINT("CONSRV: Trying to load %s frontend...\n",
                FrontEndLoadingMethods[i].FrontEndName);
         Status = FrontEndLoadingMethods[i].FrontEndLoad(FrontEnd,
                                                         ConsoleInfo,
-                                                        ExtraConsoleInfo,
-                                                        ProcessId);
+                                                        ConsoleInitInfo,
+                                                        ConsoleLeaderProcessHandle);
         if (NT_SUCCESS(Status))
         {
             /* Save the unload callback */
@@ -134,9 +198,9 @@ static TERMINAL_VTBL ConSrvTermVtbl;
 
 NTSTATUS NTAPI
 ConSrvInitTerminal(IN OUT PTERMINAL Terminal,
-                   IN OUT PCONSOLE_INFO ConsoleInfo,
-                   IN OUT PVOID ExtraConsoleInfo,
-                   IN ULONG ProcessId)
+                   IN OUT PCONSOLE_STATE_INFO ConsoleInfo,
+                   IN OUT PCONSOLE_INIT_INFO ConsoleInitInfo,
+                   IN HANDLE ConsoleLeaderProcessHandle)
 {
     NTSTATUS Status;
     PFRONTEND FrontEnd;
@@ -147,8 +211,8 @@ ConSrvInitTerminal(IN OUT PTERMINAL Terminal,
 
     Status = ConSrvLoadFrontEnd(FrontEnd,
                                 ConsoleInfo,
-                                ExtraConsoleInfo,
-                                ProcessId);
+                                ConsoleInitInfo,
+                                ConsoleLeaderProcessHandle);
     if (!NT_SUCCESS(Status))
     {
         DPRINT1("CONSRV: Failed to initialize a frontend, Status = 0x%08lx\n", Status);
@@ -159,8 +223,8 @@ ConSrvInitTerminal(IN OUT PTERMINAL Terminal,
 
     /* Initialize the ConSrv terminal */
     Terminal->Vtbl = &ConSrvTermVtbl;
-    // Terminal->Console will be initialized by ConDrvRegisterTerminal
-    Terminal->Data = FrontEnd; /* We store the frontend pointer in the terminal private data */
+    // Terminal->Console will be initialized by ConDrvAttachTerminal
+    Terminal->Context = FrontEnd; /* We store the frontend pointer in the terminal private context */
 
     return STATUS_SUCCESS;
 }
@@ -169,10 +233,10 @@ NTSTATUS NTAPI
 ConSrvDeinitTerminal(IN OUT PTERMINAL Terminal)
 {
     NTSTATUS Status = STATUS_SUCCESS;
-    PFRONTEND FrontEnd = Terminal->Data;
+    PFRONTEND FrontEnd = Terminal->Context;
 
     /* Reset the ConSrv terminal */
-    Terminal->Data = NULL;
+    Terminal->Context = NULL;
     Terminal->Vtbl = NULL;
 
     /* Unload the frontend */
@@ -190,22 +254,24 @@ 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;
+    PFRONTEND FrontEnd = This->Context;
 
     /* Initialize the console pointer for our frontend */
     FrontEnd->Console = Console;
 
     /** HACK HACK!! Copy FrontEnd into the console!! **/
-    DPRINT1("Using FrontEndIFace HACK(1), should be removed after proper implementation!\n");
+    DPRINT("Using FrontEndIFace HACK(1), should be removed after proper implementation!\n");
     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");
+    DPRINT("Using FrontEndIFace HACK(2), should be removed after proper implementation!\n");
     Console->FrontEndIFace = *FrontEnd;
 
     return Status;
@@ -214,42 +280,485 @@ ConSrvTermInitTerminal(IN OUT PTERMINAL This,
 static VOID NTAPI
 ConSrvTermDeinitTerminal(IN OUT PTERMINAL This)
 {
-    PFRONTEND FrontEnd = This->Data;
+    PFRONTEND FrontEnd = This->Context;
     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);
+    PFRONTEND FrontEnd = This->Context;
+    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 = 0;
+
+    /* Validity checks */
+    // ASSERT(Console == InputBuffer->Header.Console);
+    ASSERT((Buffer != NULL) || (Buffer == NULL && NumCharsToRead == 0));
+
+    /* We haven't read anything (yet) */
+
+    if (InputBuffer->Mode & ENABLE_LINE_INPUT)
+    {
+        /* COOKED mode, call the line discipline */
+
+        if (Console->LineBuffer == NULL)
+        {
+            /* Start a new line */
+            Console->LineMaxSize = max(256, NumCharsToRead);
+
+            /*
+             * Fixup ReadControl->nInitialChars in case the number of initial
+             * characters is bigger than the number of characters to be read.
+             * It will always be, lesser than or equal to Console->LineMaxSize.
+             */
+            ReadControl->nInitialChars = min(ReadControl->nInitialChars, 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-fill the buffer with the nInitialChars from the user buffer.
+             * Since pre-filling is only allowed in Unicode, 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 an input event from the 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)
+        {
+            /*
+             * Console->LinePos keeps the next position of the character to read
+             * in the line buffer across the different calls of the function,
+             * so that the line buffer can be read by chunks after all the input
+             * has been buffered.
+             */
+
+            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)
+            {
+                /* The entire line has been read */
+                ConsoleFreeHeap(Console->LineBuffer);
+                Console->LineBuffer = NULL;
+                Console->LinePos = Console->LineMaxSize = Console->LineSize = 0;
+                // Console->LineComplete = Console->LineUpPressed = FALSE;
+                Console->LineComplete = FALSE;
+            }
+
+            Status = STATUS_SUCCESS;
+        }
+    }
+    else
+    {
+        /* RAW mode */
+
+        /* Character input */
+        while (i < NumCharsToRead && !IsListEmpty(&InputBuffer->InputEvents))
+        {
+            /* Remove an input event from the 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;
+    BOOL bCJK = Console->IsCJK;
+
+    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' ';
+
+                    if (Ptr->Attributes & COMMON_LVB_TRAILING_BYTE)
+                    {
+                        /* Delete a full-width character */
+                        Ptr->Attributes  = Buff->ScreenDefaultAttrib;
+                        if (Buff->CursorPosition.X > 0)
+                            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 ((UINT)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);
+
+        /* For Chinese, Japanese and Korean */
+        if (bCJK && Buffer[i] >= 0x80 && mk_wcwidth_cjk(Buffer[i]) == 2)
+        {
+            /* Buffer[i] is a fullwidth character */
+
+            if (Buff->CursorPosition.X > 0)
+            {
+                /* Kill the previous leading byte */
+                Ptr = ConioCoordToPointer(Buff, Buff->CursorPosition.X - 1, Buff->CursorPosition.Y);
+                if (Ptr->Attributes & COMMON_LVB_LEADING_BYTE)
+                {
+                    Ptr->Char.UnicodeChar = L' ';
+                    if (Attrib)
+                        Ptr->Attributes &= ~COMMON_LVB_LEADING_BYTE;
+                }
+            }
+
+            if (Buff->CursorPosition.X == Buff->ScreenBufferSize.X - 1)
+            {
+                /* New line */
+                if (Buff->Mode & ENABLE_WRAP_AT_EOL_OUTPUT)
+                {
+                    Buff->CursorPosition.X = 0;
+                    ConioNextLine(Buff, &UpdateRect, &ScrolledLines);
+                }
+                else
+                {
+                    Buff->CursorPosition.X = CursorStartX;
+                }
+            }
+
+            /* Set leading */
+            Ptr = ConioCoordToPointer(Buff, Buff->CursorPosition.X, Buff->CursorPosition.Y);
+            Ptr->Char.UnicodeChar = Buffer[i];
+            if (Attrib)
+                Ptr->Attributes = Buff->ScreenDefaultAttrib | COMMON_LVB_LEADING_BYTE;
+
+            /* Set trailing */
+            Buff->CursorPosition.X++;
+            Ptr = ConioCoordToPointer(Buff, Buff->CursorPosition.X, Buff->CursorPosition.Y);
+            if (Attrib)
+                Ptr->Attributes = Buff->ScreenDefaultAttrib | COMMON_LVB_TRAILING_BYTE;
+        }
+        else
+        {
+            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 (bCJK && Buff->CursorPosition.X > 0)
+    {
+        /* Delete trailing */
+        Ptr = ConioCoordToPointer(Buff, Buff->CursorPosition.X, Buff->CursorPosition.Y);
+        if (Ptr->Attributes & COMMON_LVB_TRAILING_BYTE)
+        {
+            Ptr->Char.UnicodeChar = L' ';
+            if (Attrib)
+                Ptr->Attributes = Buff->ScreenDefaultAttrib;
+        }
+    }
+
+    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->Context;
+    return ConioWriteConsole(FrontEnd,
+                             Buff,
+                             Buffer,
+                             Length,
+                             Attrib);
+}
+
+/************ Line discipline ***************/
+
+
+
+VOID
+ConioDrawConsole(PCONSRV_CONSOLE Console)
 {
-    PFRONTEND FrontEnd = This->Data;
-    FrontEnd->Vtbl->WriteStream(FrontEnd,
-                                Region,
-                                CursorStartX,
-                                CursorStartY,
-                                ScrolledLines,
-                                Buffer,
-                                Length);
+    SMALL_RECT Region;
+    PCONSOLE_SCREEN_BUFFER ActiveBuffer = Console->ActiveBuffer;
+
+    if (!ActiveBuffer) return;
+
+    ConioInitRect(&Region, 0, 0,
+                  ActiveBuffer->ViewSize.Y - 1,
+                  ActiveBuffer->ViewSize.X - 1);
+    TermDrawRegion(Console, &Region);
+    // Console->FrontEndIFace.Vtbl->DrawRegion(&Console->FrontEndIFace, &Region);
+}
+
+static VOID NTAPI
+ConSrvTermDrawRegion(IN OUT PTERMINAL This,
+                SMALL_RECT* Region)
+{
+    PFRONTEND FrontEnd = This->Context;
+    FrontEnd->Vtbl->DrawRegion(FrontEnd, Region);
 }
 
 static BOOL NTAPI
 ConSrvTermSetCursorInfo(IN OUT PTERMINAL This,
                    PCONSOLE_SCREEN_BUFFER ScreenBuffer)
 {
-    PFRONTEND FrontEnd = This->Data;
+    PFRONTEND FrontEnd = This->Context;
     return FrontEnd->Vtbl->SetCursorInfo(FrontEnd, ScreenBuffer);
 }
 
@@ -259,7 +768,7 @@ ConSrvTermSetScreenInfo(IN OUT PTERMINAL This,
                    SHORT OldCursorX,
                    SHORT OldCursorY)
 {
-    PFRONTEND FrontEnd = This->Data;
+    PFRONTEND FrontEnd = This->Context;
     return FrontEnd->Vtbl->SetScreenInfo(FrontEnd,
                                          ScreenBuffer,
                                          OldCursorX,
@@ -269,14 +778,14 @@ ConSrvTermSetScreenInfo(IN OUT PTERMINAL This,
 static VOID NTAPI
 ConSrvTermResizeTerminal(IN OUT PTERMINAL This)
 {
-    PFRONTEND FrontEnd = This->Data;
+    PFRONTEND FrontEnd = This->Context;
     FrontEnd->Vtbl->ResizeTerminal(FrontEnd);
 }
 
 static VOID NTAPI
 ConSrvTermSetActiveScreenBuffer(IN OUT PTERMINAL This)
 {
-    PFRONTEND FrontEnd = This->Data;
+    PFRONTEND FrontEnd = This->Context;
     FrontEnd->Vtbl->SetActiveScreenBuffer(FrontEnd);
 }
 
@@ -284,41 +793,24 @@ static VOID NTAPI
 ConSrvTermReleaseScreenBuffer(IN OUT PTERMINAL This,
                          IN PCONSOLE_SCREEN_BUFFER ScreenBuffer)
 {
-    PFRONTEND FrontEnd = This->Data;
+    PFRONTEND FrontEnd = This->Context;
     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)
 {
-    PFRONTEND FrontEnd = This->Data;
+    PFRONTEND FrontEnd = This->Context;
     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,
                 UINT PaletteUsage)
 {
-    PFRONTEND FrontEnd = This->Data;
+    PFRONTEND FrontEnd = This->Context;
     return FrontEnd->Vtbl->SetPalette(FrontEnd, PaletteHandle, PaletteUsage);
 }
 
@@ -326,7 +818,7 @@ static INT NTAPI
 ConSrvTermShowMouseCursor(IN OUT PTERMINAL This,
                      BOOL Show)
 {
-    PFRONTEND FrontEnd = This->Data;
+    PFRONTEND FrontEnd = This->Context;
     return FrontEnd->Vtbl->ShowMouseCursor(FrontEnd, Show);
 }
 
@@ -334,16 +826,17 @@ static TERMINAL_VTBL ConSrvTermVtbl =
 {
     ConSrvTermInitTerminal,
     ConSrvTermDeinitTerminal,
-    ConSrvTermDrawRegion,
+
+    ConSrvTermReadStream,
     ConSrvTermWriteStream,
+
+    ConSrvTermDrawRegion,
     ConSrvTermSetCursorInfo,
     ConSrvTermSetScreenInfo,
     ConSrvTermResizeTerminal,
     ConSrvTermSetActiveScreenBuffer,
     ConSrvTermReleaseScreenBuffer,
-    ConSrvTermChangeTitle,
     ConSrvTermGetLargestConsoleWindowSize,
-    // ConSrvTermGetSelectionInfo,
     ConSrvTermSetPalette,
     ConSrvTermShowMouseCursor,
 };