[SHELL32] SHChangeNotify: Use tree for CDirectoryList (#6784)
[reactos.git] / win32ss / user / winsrv / consrv / frontends / terminal.c
index 668b4d6..ddf8f75 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
  *  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)
+do { \
+    ASSERT((ULONG_PTR)(dChar) != (ULONG_PTR)(sWChar)); \
+    WideCharToMultiByte((Console)->InputCodePage, 0, (sWChar), 1, (dChar), 1, NULL, NULL); \
+} while (0)
 
 #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;
-
+do { \
+    ASSERT((ULONG_PTR)(dWChar) != (ULONG_PTR)(sChar)); \
+    MultiByteToWideChar((Console)->InputCodePage, 0, (sChar), 1, (dWChar), 1); \
+} while (0)
 
 /* PRIVATE FUNCTIONS **********************************************************/
 
@@ -94,27 +92,27 @@ ConioInputEventToUnicode(PCONSOLE Console, PINPUT_RECORD InputEvent)
 #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);
@@ -154,9 +152,9 @@ static 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;
@@ -164,14 +162,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 */
@@ -204,9 +202,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;
@@ -217,8 +215,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);
@@ -229,8 +227,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;
 }
@@ -239,10 +237,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 */
@@ -263,22 +261,23 @@ ConSrvTermInitTerminal(IN OUT PTERMINAL This,
                        IN PCONSOLE Console)
 {
     NTSTATUS Status;
-    PFRONTEND FrontEnd = This->Data;
+    PFRONTEND FrontEnd = This->Context;
+    PCONSRV_CONSOLE ConSrvConsole = (PCONSRV_CONSOLE)Console;
 
     /* Initialize the console pointer for our frontend */
-    FrontEnd->Console = Console;
+    FrontEnd->Console = ConSrvConsole;
 
     /** HACK HACK!! Copy FrontEnd into the console!! **/
-    DPRINT1("Using FrontEndIFace HACK(1), should be removed after proper implementation!\n");
-    Console->FrontEndIFace = *FrontEnd;
+    DPRINT("Using FrontEndIFace HACK(1), should be removed after proper implementation!\n");
+    ConSrvConsole->FrontEndIFace = *FrontEnd;
 
-    Status = FrontEnd->Vtbl->InitFrontEnd(FrontEnd, FrontEnd->Console);
+    Status = FrontEnd->Vtbl->InitFrontEnd(FrontEnd, ConSrvConsole);
     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");
-    Console->FrontEndIFace = *FrontEnd;
+    DPRINT("Using FrontEndIFace HACK(2), should be removed after proper implementation!\n");
+    ConSrvConsole->FrontEndIFace = *FrontEnd;
 
     return Status;
 }
@@ -286,7 +285,7 @@ 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);
 }
 
@@ -296,31 +295,31 @@ ConSrvTermDeinitTerminal(IN OUT PTERMINAL This)
 
 static NTSTATUS NTAPI
 ConSrvTermReadStream(IN OUT PTERMINAL This,
-                     /**/IN PUNICODE_STRING ExeName /**/OPTIONAL/**/,/**/
                      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;
+    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;
+    ULONG i = 0;
 
     /* 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)
     {
@@ -328,9 +327,16 @@ ConSrvTermReadStream(IN OUT PTERMINAL This,
 
         if (Console->LineBuffer == NULL)
         {
-            /* Starting a new line */
+            /* 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;
 
@@ -340,11 +346,12 @@ ConSrvTermReadStream(IN OUT PTERMINAL This,
             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.
+             * 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)
+            if (Console->LineSize >= Console->LineMaxSize)
             {
                 Console->LineComplete = TRUE;
                 Console->LinePos = 0;
@@ -354,11 +361,12 @@ ConSrvTermReadStream(IN OUT PTERMINAL This,
         /* If we don't have a complete line yet, process the pending input */
         while (!Console->LineComplete && !IsListEmpty(&InputBuffer->InputEvents))
         {
-            /* Remove input event from queue */
+            /* Remove an input event from the queue */
+            _InterlockedDecrement((PLONG)&InputBuffer->NumberOfEvents);
             CurrentEntry = RemoveHeadList(&InputBuffer->InputEvents);
             if (IsListEmpty(&InputBuffer->InputEvents))
             {
-                ResetEvent(InputBuffer->ActiveEvent);
+                NtClearEvent(InputBuffer->ActiveEvent);
             }
             Input = CONTAINING_RECORD(CurrentEntry, ConsoleInput, ListEntry);
 
@@ -376,7 +384,14 @@ ConSrvTermReadStream(IN OUT PTERMINAL This,
         /* Check if we have a complete line to read from */
         if (Console->LineComplete)
         {
-            while (i < NumCharsToRead && Console->LinePos != Console->LineSize)
+            /*
+             * 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++];
 
@@ -391,11 +406,14 @@ ConSrvTermReadStream(IN OUT PTERMINAL This,
                 ++i;
             }
 
-            if (Console->LinePos == Console->LineSize)
+            if (Console->LinePos >= Console->LineSize)
             {
-                /* Entire line has been read */
+                /* 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;
@@ -408,11 +426,12 @@ ConSrvTermReadStream(IN OUT PTERMINAL This,
         /* Character input */
         while (i < NumCharsToRead && !IsListEmpty(&InputBuffer->InputEvents))
         {
-            /* Remove input event from queue */
+            /* Remove an input event from the queue */
+            _InterlockedDecrement((PLONG)&InputBuffer->NumberOfEvents);
             CurrentEntry = RemoveHeadList(&InputBuffer->InputEvents);
             if (IsListEmpty(&InputBuffer->InputEvents))
             {
-                ResetEvent(InputBuffer->ActiveEvent);
+                NtClearEvent(InputBuffer->ActiveEvent);
             }
             Input = CONTAINING_RECORD(CurrentEntry, ConsoleInput, ListEntry);
 
@@ -494,12 +513,18 @@ ConioWriteConsole(PFRONTEND FrontEnd,
     SMALL_RECT UpdateRect;
     SHORT CursorStartX, CursorStartY;
     UINT ScrolledLines;
+    BOOLEAN bFullwidth;
+    BOOLEAN bCJK = Console->IsCJK;
+
+    /* If nothing to write, bail out now */
+    if (Length == 0)
+        return STATUS_SUCCESS;
 
     CursorStartX = Buff->CursorPosition.X;
     CursorStartY = Buff->CursorPosition.Y;
     UpdateRect.Left = Buff->ScreenBufferSize.X;
-    UpdateRect.Top = Buff->CursorPosition.Y;
-    UpdateRect.Right = -1;
+    UpdateRect.Top  = Buff->CursorPosition.Y;
+    UpdateRect.Right  = -1;
     UpdateRect.Bottom = Buff->CursorPosition.Y;
     ScrolledLines = 0;
 
@@ -515,28 +540,58 @@ ConioWriteConsole(PFRONTEND FrontEnd,
             if (Buffer[i] == L'\r')
             {
                 Buff->CursorPosition.X = 0;
-                UpdateRect.Left = min(UpdateRect.Left, Buff->CursorPosition.X);
+                CursorStartX = Buff->CursorPosition.X;
+                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;
+                Buff->CursorPosition.X = 0; // TODO: Make this behaviour optional!
+                CursorStartX = Buff->CursorPosition.X;
                 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)
+                /* Only handle BS if we are not on the first position of the first line */
+                if (Buff->CursorPosition.X == 0 && Buff->CursorPosition.Y == 0)
+                    continue;
+
+                if (Buff->CursorPosition.X == 0)
+                {
+                    /* Slide virtual position up */
+                    Buff->CursorPosition.X = Buff->ScreenBufferSize.X - 1;
+                    Buff->CursorPosition.Y--;
+                    // TODO? : Update CursorStartY = Buff->CursorPosition.Y;
+                    UpdateRect.Top = min(UpdateRect.Top, Buff->CursorPosition.Y);
+                }
+                else
                 {
-                    if (0 == Buff->CursorPosition.X)
+                    Buff->CursorPosition.X--;
+                }
+                Ptr = ConioCoordToPointer(Buff, Buff->CursorPosition.X, Buff->CursorPosition.Y);
+
+                if (Ptr->Attributes & COMMON_LVB_LEADING_BYTE)
+                {
+                    /*
+                     * The cursor just moved on the leading byte of the same
+                     * current character. We should go one position before to
+                     * go to the actual previous character to erase.
+                     */
+
+                    /* Only handle BS if we are not on the first position of the first line */
+                    if (Buff->CursorPosition.X == 0 && Buff->CursorPosition.Y == 0)
+                        continue;
+
+                    if (Buff->CursorPosition.X == 0)
                     {
-                        /* slide virtual position up */
+                        /* Slide virtual position up */
                         Buff->CursorPosition.X = Buff->ScreenBufferSize.X - 1;
                         Buff->CursorPosition.Y--;
+                        // TODO? : Update CursorStartY = Buff->CursorPosition.Y;
                         UpdateRect.Top = min(UpdateRect.Top, Buff->CursorPosition.Y);
                     }
                     else
@@ -544,11 +599,31 @@ ConioWriteConsole(PFRONTEND FrontEnd,
                         Buff->CursorPosition.X--;
                     }
                     Ptr = ConioCoordToPointer(Buff, Buff->CursorPosition.X, Buff->CursorPosition.Y);
+                }
+
+                if (Ptr->Attributes & COMMON_LVB_TRAILING_BYTE)
+                {
+                    /* The cursor is on the trailing byte of a full-width character */
+
+                    /* Delete its trailing byte... */
                     Ptr->Char.UnicodeChar = L' ';
-                    Ptr->Attributes  = Buff->ScreenDefaultAttrib;
-                    UpdateRect.Left  = min(UpdateRect.Left, Buff->CursorPosition.X);
-                    UpdateRect.Right = max(UpdateRect.Right, Buff->CursorPosition.X);
+                    if (Attrib)
+                        Ptr->Attributes = Buff->ScreenDefaultAttrib;
+                    Ptr->Attributes &= ~COMMON_LVB_SBCSDBCS;
+
+                    if (Buff->CursorPosition.X > 0)
+                        Buff->CursorPosition.X--;
+                    /* ... and now its leading byte */
+                    Ptr = ConioCoordToPointer(Buff, Buff->CursorPosition.X, Buff->CursorPosition.Y);
                 }
+
+                Ptr->Char.UnicodeChar = L' ';
+                if (Attrib)
+                    Ptr->Attributes = Buff->ScreenDefaultAttrib;
+                Ptr->Attributes &= ~COMMON_LVB_SBCSDBCS;
+
+                UpdateRect.Left  = min(UpdateRect.Left , Buff->CursorPosition.X);
+                UpdateRect.Right = max(UpdateRect.Right, Buff->CursorPosition.X);
                 continue;
             }
             /* --- TAB --- */
@@ -556,28 +631,60 @@ ConioWriteConsole(PFRONTEND FrontEnd,
             {
                 UINT EndX;
 
+                Ptr = ConioCoordToPointer(Buff, Buff->CursorPosition.X, Buff->CursorPosition.Y);
+
+                if (Ptr->Attributes & COMMON_LVB_TRAILING_BYTE)
+                {
+                    /*
+                     * The cursor is on the trailing byte of a full-width character.
+                     * Go back one position to be on its leading byte.
+                     */
+                    if (Buff->CursorPosition.X > 0)
+                        Buff->CursorPosition.X--;
+                    Ptr = ConioCoordToPointer(Buff, Buff->CursorPosition.X, Buff->CursorPosition.Y);
+                }
+
                 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)
+
+                while ((UINT)Buff->CursorPosition.X < EndX)
                 {
                     Ptr->Char.UnicodeChar = L' ';
-                    Ptr->Attributes = Buff->ScreenDefaultAttrib;
+                    if (Attrib)
+                        Ptr->Attributes = Buff->ScreenDefaultAttrib;
+                    Ptr->Attributes &= ~COMMON_LVB_SBCSDBCS;
+
                     ++Ptr;
                     Buff->CursorPosition.X++;
                 }
-                UpdateRect.Right = max(UpdateRect.Right, Buff->CursorPosition.X - 1);
-                if (Buff->CursorPosition.X == Buff->ScreenBufferSize.X)
+                if (Buff->CursorPosition.X < Buff->ScreenBufferSize.X)
+                {
+                    /* If the following cell is the trailing byte of a full-width character, reset it */
+                    if (Ptr->Attributes & COMMON_LVB_TRAILING_BYTE)
+                    {
+                        Ptr->Char.UnicodeChar = L' ';
+                        if (Attrib)
+                            Ptr->Attributes = Buff->ScreenDefaultAttrib;
+                        Ptr->Attributes &= ~COMMON_LVB_SBCSDBCS;
+                    }
+                }
+                UpdateRect.Right = max(UpdateRect.Right, Buff->CursorPosition.X);
+
+                if (Buff->CursorPosition.X >= Buff->ScreenBufferSize.X)
                 {
                     if (Buff->Mode & ENABLE_WRAP_AT_EOL_OUTPUT)
                     {
+                        /* Wrapping mode: Go to next line */
                         Buff->CursorPosition.X = 0;
+                        CursorStartX = Buff->CursorPosition.X;
                         ConioNextLine(Buff, &UpdateRect, &ScrolledLines);
                     }
                     else
                     {
-                        Buff->CursorPosition.X--;
+                        /* The cursor wraps back to its starting position on the same line */
+                        Buff->CursorPosition.X = CursorStartX;
                     }
                 }
                 continue;
@@ -589,23 +696,124 @@ ConioWriteConsole(PFRONTEND FrontEnd,
                 continue;
             }
         }
-        UpdateRect.Left  = min(UpdateRect.Left, Buff->CursorPosition.X);
+        UpdateRect.Left  = min(UpdateRect.Left , Buff->CursorPosition.X);
         UpdateRect.Right = max(UpdateRect.Right, Buff->CursorPosition.X);
 
+        /* For Chinese, Japanese and Korean */
+        bFullwidth = (bCJK && IS_FULL_WIDTH(Buffer[i]));
+
+        /* Check whether we can insert the full-width character */
+        if (bFullwidth)
+        {
+            /* It spans two cells and should all fit on the current line */
+            if (Buff->CursorPosition.X >= Buff->ScreenBufferSize.X - 1)
+            {
+                if (Buff->Mode & ENABLE_WRAP_AT_EOL_OUTPUT)
+                {
+                    /* Wrapping mode: Go to next line */
+                    Buff->CursorPosition.X = 0;
+                    CursorStartX = Buff->CursorPosition.X;
+                    ConioNextLine(Buff, &UpdateRect, &ScrolledLines);
+                }
+                else
+                {
+                    /* The cursor wraps back to its starting position on the same line */
+                    Buff->CursorPosition.X = CursorStartX;
+                }
+            }
+
+            /*
+             * Now be sure we can fit the full-width character.
+             * If the screenbuffer is one cell wide we cannot display
+             * the full-width character, so just skip it.
+             */
+            if (Buff->CursorPosition.X >= Buff->ScreenBufferSize.X - 1)
+            {
+                DPRINT1("Cannot display full-width character! CursorPosition.X = %d, ScreenBufferSize.X = %d\n",
+                        Buff->CursorPosition.X, Buff->ScreenBufferSize.X);
+                continue;
+            }
+        }
+
         Ptr = ConioCoordToPointer(Buff, Buff->CursorPosition.X, Buff->CursorPosition.Y);
-        Ptr->Char.UnicodeChar = Buffer[i];
-        if (Attrib) Ptr->Attributes = Buff->ScreenDefaultAttrib;
 
+        /*
+         * Check whether we are overwriting part of a full-width character,
+         * in which case we need to invalidate it.
+         */
+        if (Ptr->Attributes & COMMON_LVB_TRAILING_BYTE)
+        {
+            /*
+             * The cursor is on the trailing byte of a full-width character.
+             * Go back one position to kill the previous leading byte.
+             */
+            if (Buff->CursorPosition.X > 0)
+            {
+                Ptr = ConioCoordToPointer(Buff, Buff->CursorPosition.X - 1, Buff->CursorPosition.Y);
+                Ptr->Char.UnicodeChar = L' ';
+                if (Attrib)
+                    Ptr->Attributes = Buff->ScreenDefaultAttrib;
+                Ptr->Attributes &= ~COMMON_LVB_SBCSDBCS;
+            }
+            Ptr = ConioCoordToPointer(Buff, Buff->CursorPosition.X, Buff->CursorPosition.Y);
+        }
+
+        /* Insert the character */
+        if (bFullwidth)
+        {
+            ASSERT(Buff->CursorPosition.X < Buff->ScreenBufferSize.X - 1);
+
+            /* Set the leading byte */
+            Ptr->Char.UnicodeChar = Buffer[i];
+            if (Attrib)
+                Ptr->Attributes = Buff->ScreenDefaultAttrib;
+            Ptr->Attributes &= ~COMMON_LVB_SBCSDBCS;
+            Ptr->Attributes |= COMMON_LVB_LEADING_BYTE;
+
+            /* Set the trailing byte */
+            Buff->CursorPosition.X++;
+            Ptr = ConioCoordToPointer(Buff, Buff->CursorPosition.X, Buff->CursorPosition.Y);
+            // Ptr->Char.UnicodeChar = Buffer[i]; // L' ';
+            if (Attrib)
+                Ptr->Attributes = Buff->ScreenDefaultAttrib;
+            Ptr->Attributes &= ~COMMON_LVB_SBCSDBCS;
+            Ptr->Attributes |= COMMON_LVB_TRAILING_BYTE;
+        }
+        else
+        {
+            Ptr->Char.UnicodeChar = Buffer[i];
+            if (Attrib)
+                Ptr->Attributes = Buff->ScreenDefaultAttrib;
+            Ptr->Attributes &= ~COMMON_LVB_SBCSDBCS;
+        }
+
+        ++Ptr;
         Buff->CursorPosition.X++;
-        if (Buff->CursorPosition.X == Buff->ScreenBufferSize.X)
+
+        if (Buff->CursorPosition.X < Buff->ScreenBufferSize.X)
+        {
+            /* If the following cell is the trailing byte of a full-width character, reset it */
+            if (Ptr->Attributes & COMMON_LVB_TRAILING_BYTE)
+            {
+                Ptr->Char.UnicodeChar = L' ';
+                if (Attrib)
+                    Ptr->Attributes = Buff->ScreenDefaultAttrib;
+                Ptr->Attributes &= ~COMMON_LVB_SBCSDBCS;
+            }
+        }
+
+        if (Buff->CursorPosition.X >= Buff->ScreenBufferSize.X)
         {
             if (Buff->Mode & ENABLE_WRAP_AT_EOL_OUTPUT)
             {
+                /* Wrapping mode: Go to next line */
                 Buff->CursorPosition.X = 0;
+                CursorStartX = Buff->CursorPosition.X;
                 ConioNextLine(Buff, &UpdateRect, &ScrolledLines);
             }
             else
             {
+                /* The cursor wraps back to its starting position on the same line */
                 Buff->CursorPosition.X = CursorStartX;
             }
         }
@@ -636,7 +844,7 @@ ConSrvTermWriteStream(IN OUT PTERMINAL This,
                       DWORD Length,
                       BOOL Attrib)
 {
-    PFRONTEND FrontEnd = This->Data;
+    PFRONTEND FrontEnd = This->Context;
     return ConioWriteConsole(FrontEnd,
                              Buff,
                              Buffer,
@@ -648,11 +856,26 @@ ConSrvTermWriteStream(IN OUT PTERMINAL This,
 
 
 
+VOID
+ConioDrawConsole(PCONSRV_CONSOLE Console)
+{
+    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->Data;
+    PFRONTEND FrontEnd = This->Context;
     FrontEnd->Vtbl->DrawRegion(FrontEnd, Region);
 }
 
@@ -660,7 +883,7 @@ 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);
 }
 
@@ -670,7 +893,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,
@@ -680,14 +903,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);
 }
 
@@ -695,7 +918,7 @@ 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);
 }
 
@@ -703,7 +926,7 @@ static VOID NTAPI
 ConSrvTermGetLargestConsoleWindowSize(IN OUT PTERMINAL This,
                                  PCOORD pSize)
 {
-    PFRONTEND FrontEnd = This->Data;
+    PFRONTEND FrontEnd = This->Context;
     FrontEnd->Vtbl->GetLargestConsoleWindowSize(FrontEnd, pSize);
 }
 
@@ -712,15 +935,23 @@ ConSrvTermSetPalette(IN OUT PTERMINAL This,
                 HPALETTE PaletteHandle,
                 UINT PaletteUsage)
 {
-    PFRONTEND FrontEnd = This->Data;
+    PFRONTEND FrontEnd = This->Context;
     return FrontEnd->Vtbl->SetPalette(FrontEnd, PaletteHandle, PaletteUsage);
 }
 
+static BOOL NTAPI
+ConSrvTermSetCodePage(IN OUT PTERMINAL This,
+                      UINT CodePage)
+{
+    PFRONTEND FrontEnd = This->Context;
+    return FrontEnd->Vtbl->SetCodePage(FrontEnd, CodePage);
+}
+
 static INT NTAPI
 ConSrvTermShowMouseCursor(IN OUT PTERMINAL This,
                      BOOL Show)
 {
-    PFRONTEND FrontEnd = This->Data;
+    PFRONTEND FrontEnd = This->Context;
     return FrontEnd->Vtbl->ShowMouseCursor(FrontEnd, Show);
 }
 
@@ -740,6 +971,7 @@ static TERMINAL_VTBL ConSrvTermVtbl =
     ConSrvTermReleaseScreenBuffer,
     ConSrvTermGetLargestConsoleWindowSize,
     ConSrvTermSetPalette,
+    ConSrvTermSetCodePage,
     ConSrvTermShowMouseCursor,
 };
 
@@ -747,11 +979,12 @@ static TERMINAL_VTBL ConSrvTermVtbl =
 VOID
 ResetFrontEnd(IN PCONSOLE Console)
 {
+    PCONSRV_CONSOLE ConSrvConsole = (PCONSRV_CONSOLE)Console;
     if (!Console) return;
 
     /* Reinitialize the frontend interface */
-    RtlZeroMemory(&Console->FrontEndIFace, sizeof(Console->FrontEndIFace));
-    Console->FrontEndIFace.Vtbl = &ConSrvTermVtbl;
+    RtlZeroMemory(&ConSrvConsole->FrontEndIFace, sizeof(ConSrvConsole->FrontEndIFace));
+    ConSrvConsole->FrontEndIFace.Vtbl = &ConSrvTermVtbl;
 }
 #endif