[WIN32SS][WINSRV] Fullwidth character handling Part 2 (#2240)
[reactos.git] / win32ss / user / winsrv / consrv / condrv / text.c
index 06d4d31..92a6f4d 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * COPYRIGHT:       See COPYING in the top level directory
  * PROJECT:         ReactOS Console Driver DLL
- * FILE:            consrv/condrv/text.c
+ * FILE:            win32ss/user/winsrv/consrv/condrv/text.c
  * PURPOSE:         Console Output Functions for text-mode screen-buffers
  * PROGRAMMERS:     Jeffrey Morlan
  *                  Hermes Belusca-Maito (hermes.belusca@sfr.fr)
@@ -14,6 +14,8 @@
 #define NDEBUG
 #include <debug.h>
 
+#define COMMON_LEAD_TRAIL (COMMON_LVB_LEADING_BYTE | COMMON_LVB_TRAILING_BYTE)
+
 /* GLOBALS ********************************************************************/
 
 /*
@@ -162,20 +164,14 @@ ConioComputeUpdateRect(IN PTEXTMODE_SCREEN_BUFFER Buff,
                        IN PCOORD Start,
                        IN UINT Length)
 {
-    if (Buff->ScreenBufferSize.X <= Start->X + Length)
-    {
-        UpdateRect->Left = 0;
-    }
-    else
-    {
-        UpdateRect->Left = Start->X;
-    }
-    if (Buff->ScreenBufferSize.X <= Start->X + Length)
+    if ((UINT)Buff->ScreenBufferSize.X <= Start->X + Length)
     {
+        UpdateRect->Left  = 0;
         UpdateRect->Right = Buff->ScreenBufferSize.X - 1;
     }
     else
     {
+        UpdateRect->Left  = Start->X;
         UpdateRect->Right = Start->X + Length - 1;
     }
     UpdateRect->Top = Start->Y;
@@ -391,7 +387,7 @@ ConDrvChangeScreenBufferAttributes(IN PCONSOLE Console,
                                    IN USHORT NewScreenAttrib,
                                    IN USHORT NewPopupAttrib)
 {
-    DWORD X, Y, Length;
+    ULONG X, Y, Length;
     PCHAR_INFO Ptr;
 
     COORD  TopLeft = {0};
@@ -414,13 +410,9 @@ ConDrvChangeScreenBufferAttributes(IN PCONSOLE Console,
     Y = (TopLeft.Y + Buffer->VirtualY) % Buffer->ScreenBufferSize.Y;
     Length = NumCodesToWrite;
 
-    // Ptr = ConioCoordToPointer(Buffer, X, Y); // Doesn't work
-    // Ptr = &Buffer->Buffer[X + Y * Buffer->ScreenBufferSize.X]; // May work
-
     while (Length--)
     {
-        // Ptr = ConioCoordToPointer(Buffer, X, Y); // Doesn't work either
-        Ptr = &Buffer->Buffer[X + Y * Buffer->ScreenBufferSize.X];
+        Ptr = ConioCoordToPointer(Buffer, X, Y);
 
         /*
          * Change the current colors only if they are the old ones.
@@ -522,7 +514,7 @@ ConDrvReadConsoleOutput(IN PCONSOLE Console,
                 WideCharToMultiByte(Console->OutputCodePage, 0, &Ptr->Char.UnicodeChar, 1,
                                     &CurCharInfo->Char.AsciiChar, 1, NULL, NULL);
             }
-            CurCharInfo->Attributes = Ptr->Attributes;
+            CurCharInfo->Attributes = (Ptr->Attributes & ~COMMON_LEAD_TRAIL);
             ++Ptr;
             ++CurCharInfo;
         }
@@ -729,102 +721,147 @@ ConDrvWriteConsole(IN PCONSOLE Console,
     return Status;
 }
 
-NTSTATUS NTAPI
-ConDrvReadConsoleOutputString(IN PCONSOLE Console,
-                              IN PTEXTMODE_SCREEN_BUFFER Buffer,
-                              IN CODE_TYPE CodeType,
-                              OUT PVOID StringBuffer,
-                              IN ULONG NumCodesToRead,
-                              IN PCOORD ReadCoord,
-                              // OUT PCOORD EndCoord,
-                              OUT PULONG NumCodesRead OPTIONAL)
+NTSTATUS FASTCALL
+IntReadConsoleOutputStringAscii(IN PCONSOLE Console,
+                                IN PTEXTMODE_SCREEN_BUFFER Buffer,
+                                OUT PVOID StringBuffer,
+                                IN ULONG NumCodesToRead,
+                                IN PCOORD ReadCoord,
+                                OUT PULONG NumCodesRead OPTIONAL)
 {
-    SHORT Xpos, Ypos;
-    PVOID ReadBuffer;
+    ULONG CodeSize = RTL_FIELD_SIZE(CODE_ELEMENT, AsciiChar);
+    LPBYTE ReadBuffer = StringBuffer;
+    SHORT Xpos = ReadCoord->X;
+    SHORT Ypos = (ReadCoord->Y + Buffer->VirtualY) % Buffer->ScreenBufferSize.Y;
     ULONG i;
-    ULONG CodeSize;
     PCHAR_INFO Ptr;
+    BOOLEAN bCJK = Console->IsCJK;
 
-    if (Console == NULL || Buffer == NULL || ReadCoord == NULL /* || EndCoord == NULL */)
+    for (i = 0; i < NumCodesToRead; ++i)
     {
-        return STATUS_INVALID_PARAMETER;
+        Ptr = ConioCoordToPointer(Buffer, Xpos, Ypos);
+
+        ConsoleOutputUnicodeToAnsiChar(Console, (PCHAR)ReadBuffer, &Ptr->Char.UnicodeChar);
+        ReadBuffer += CodeSize;
+
+        Xpos++;
+        if (Xpos == Buffer->ScreenBufferSize.X)
+        {
+            Xpos = 0;
+            Ypos++;
+            if (Ypos == Buffer->ScreenBufferSize.Y)
+            {
+                Ypos = 0;
+            }
+        }
+
+        /* For Chinese, Japanese and Korean */
+        if (bCJK && (Ptr->Attributes & COMMON_LVB_LEADING_BYTE))
+        {
+            Xpos++;
+            if (Xpos == Buffer->ScreenBufferSize.X)
+            {
+                Xpos = 0;
+                Ypos++;
+                if (Ypos == Buffer->ScreenBufferSize.Y)
+                {
+                    Ypos = 0;
+                }
+            }
+            ++i;
+        }
     }
 
-    /* Validity checks */
-    ASSERT(Console == Buffer->Header.Console);
-    ASSERT((StringBuffer != NULL) || (StringBuffer == NULL && NumCodesToRead == 0));
+    if (NumCodesRead)
+        *NumCodesRead = i;
 
-    //
-    // FIXME: Make overflow checks on ReadCoord !!!!!!
-    //
+    return STATUS_SUCCESS;
+}
 
-    if (NumCodesRead) *NumCodesRead = 0;
+NTSTATUS FASTCALL
+IntReadConsoleOutputStringUnicode(IN PCONSOLE Console,
+                                  IN PTEXTMODE_SCREEN_BUFFER Buffer,
+                                  OUT PVOID StringBuffer,
+                                  IN ULONG NumCodesToRead,
+                                  IN PCOORD ReadCoord,
+                                  OUT PULONG NumCodesRead OPTIONAL)
+{
+    ULONG CodeSize = RTL_FIELD_SIZE(CODE_ELEMENT, UnicodeChar);
+    LPBYTE ReadBuffer = StringBuffer;
+    SHORT Xpos = ReadCoord->X;
+    SHORT Ypos = (ReadCoord->Y + Buffer->VirtualY) % Buffer->ScreenBufferSize.Y;
+    ULONG i, nNumChars = 0;
+    PCHAR_INFO Ptr;
+    BOOLEAN bCJK = Console->IsCJK;
 
-    switch (CodeType)
+    for (i = 0; i < NumCodesToRead; ++i, ++nNumChars)
     {
-        case CODE_ASCII:
-            CodeSize = RTL_FIELD_SIZE(CODE_ELEMENT, AsciiChar);
-            break;
+        Ptr = ConioCoordToPointer(Buffer, Xpos, Ypos);
 
-        case CODE_UNICODE:
-            CodeSize = RTL_FIELD_SIZE(CODE_ELEMENT, UnicodeChar);
-            break;
+        *(PWCHAR)ReadBuffer = Ptr->Char.UnicodeChar;
+        ReadBuffer += CodeSize;
 
-        case CODE_ATTRIBUTE:
-            CodeSize = RTL_FIELD_SIZE(CODE_ELEMENT, Attribute);
-            break;
+        Xpos++;
+        if (Xpos == Buffer->ScreenBufferSize.X)
+        {
+            Xpos = 0;
+            Ypos++;
+            if (Ypos == Buffer->ScreenBufferSize.Y)
+            {
+                Ypos = 0;
+            }
+        }
 
-        default:
-            return STATUS_INVALID_PARAMETER;
+        /* For Chinese, Japanese and Korean */
+        if (bCJK && (Ptr->Attributes & COMMON_LVB_LEADING_BYTE))
+        {
+            Xpos++;
+            if (Xpos == Buffer->ScreenBufferSize.X)
+            {
+                Xpos = 0;
+                Ypos++;
+                if (Ypos == Buffer->ScreenBufferSize.Y)
+                {
+                    Ypos = 0;
+                }
+            }
+            ++i;
+        }
     }
 
-    ReadBuffer = StringBuffer;
-    Xpos = ReadCoord->X;
-    Ypos = (ReadCoord->Y + Buffer->VirtualY) % Buffer->ScreenBufferSize.Y;
+    if (NumCodesRead)
+        *NumCodesRead = nNumChars;
 
-    /*
-     * MSDN (ReadConsoleOutputAttribute and ReadConsoleOutputCharacter) :
-     *
-     * If the number of attributes (resp. characters) to be read from extends
-     * beyond the end of the specified screen buffer row, attributes (resp.
-     * characters) are read from the next row. If the number of attributes
-     * (resp. characters) to be read from extends beyond the end of the console
-     * screen buffer, attributes (resp. characters) up to the end of the console
-     * screen buffer are read.
-     *
-     * TODO: Do NOT loop up to NumCodesToRead, but stop before
-     * if we are going to overflow...
-     */
-    // Ptr = ConioCoordToPointer(Buffer, Xpos, Ypos); // Doesn't work
-    for (i = 0; i < min(NumCodesToRead, Buffer->ScreenBufferSize.X * Buffer->ScreenBufferSize.Y); ++i)
-    {
-        // Ptr = ConioCoordToPointer(Buffer, Xpos, Ypos); // Doesn't work either
-        Ptr = &Buffer->Buffer[Xpos + Ypos * Buffer->ScreenBufferSize.X];
+    return STATUS_SUCCESS;
+}
 
-        switch (CodeType)
-        {
-            case CODE_ASCII:
-                ConsoleOutputUnicodeToAnsiChar(Console, (PCHAR)ReadBuffer, &Ptr->Char.UnicodeChar);
-                break;
+NTSTATUS FASTCALL
+IntReadConsoleOutputStringAttributes(IN PCONSOLE Console,
+                                     IN PTEXTMODE_SCREEN_BUFFER Buffer,
+                                     OUT PVOID StringBuffer,
+                                     IN ULONG NumCodesToRead,
+                                     IN PCOORD ReadCoord,
+                                     OUT PULONG NumCodesRead OPTIONAL)
+{
+    ULONG CodeSize = RTL_FIELD_SIZE(CODE_ELEMENT, Attribute);
+    LPBYTE ReadBuffer = StringBuffer;
+    SHORT Xpos = ReadCoord->X;
+    SHORT Ypos = (ReadCoord->Y + Buffer->VirtualY) % Buffer->ScreenBufferSize.Y;
+    ULONG i;
+    PCHAR_INFO Ptr;
 
-            case CODE_UNICODE:
-                *(PWCHAR)ReadBuffer = Ptr->Char.UnicodeChar;
-                break;
+    for (i = 0; i < NumCodesToRead; ++i)
+    {
+        Ptr = ConioCoordToPointer(Buffer, Xpos, Ypos);
 
-            case CODE_ATTRIBUTE:
-                *(PWORD)ReadBuffer = Ptr->Attributes;
-                break;
-        }
-        ReadBuffer = (PVOID)((ULONG_PTR)ReadBuffer + CodeSize);
-        // ++Ptr;
+        *(PWORD)ReadBuffer = Ptr->Attributes;
+        ReadBuffer += CodeSize;
 
         Xpos++;
-
         if (Xpos == Buffer->ScreenBufferSize.X)
         {
             Xpos = 0;
             Ypos++;
-
             if (Ypos == Buffer->ScreenBufferSize.Y)
             {
                 Ypos = 0;
@@ -832,151 +869,300 @@ ConDrvReadConsoleOutputString(IN PCONSOLE Console,
         }
     }
 
-    // EndCoord->X = Xpos;
-    // EndCoord->Y = (Ypos - Buffer->VirtualY + Buffer->ScreenBufferSize.Y) % Buffer->ScreenBufferSize.Y;
+    if (Xpos > 0 && Console->IsCJK)
+    {
+        ReadBuffer -= CodeSize;
+        *(PWORD)ReadBuffer &= ~COMMON_LVB_LEADING_BYTE;
+    }
 
     if (NumCodesRead)
-        *NumCodesRead = (ULONG)((ULONG_PTR)ReadBuffer - (ULONG_PTR)StringBuffer) / CodeSize;
-    // <= NumCodesToRead
+        *NumCodesRead = NumCodesToRead;
 
     return STATUS_SUCCESS;
 }
 
 NTSTATUS NTAPI
-ConDrvWriteConsoleOutputString(IN PCONSOLE Console,
-                               IN PTEXTMODE_SCREEN_BUFFER Buffer,
-                               IN CODE_TYPE CodeType,
-                               IN PVOID StringBuffer,
-                               IN ULONG NumCodesToWrite,
-                               IN PCOORD WriteCoord,
-                               // OUT PCOORD EndCoord,
-                               OUT PULONG NumCodesWritten OPTIONAL)
+ConDrvReadConsoleOutputString(IN PCONSOLE Console,
+                              IN PTEXTMODE_SCREEN_BUFFER Buffer,
+                              IN CODE_TYPE CodeType,
+                              OUT PVOID StringBuffer,
+                              IN ULONG NumCodesToRead,
+                              IN PCOORD ReadCoord,
+                              OUT PULONG NumCodesRead OPTIONAL)
 {
-    NTSTATUS Status = STATUS_SUCCESS;
-    PVOID WriteBuffer = NULL;
-    PWCHAR tmpString = NULL;
-    DWORD X, Y, Length; // , Written = 0;
-    ULONG CodeSize;
-    PCHAR_INFO Ptr;
-
-    if (Console == NULL || Buffer == NULL || WriteCoord == NULL /* || EndCoord == NULL */)
+    if (Console == NULL || Buffer == NULL || ReadCoord == NULL /* || EndCoord == NULL */)
     {
         return STATUS_INVALID_PARAMETER;
     }
 
     /* Validity checks */
     ASSERT(Console == Buffer->Header.Console);
-    ASSERT((StringBuffer != NULL) || (StringBuffer == NULL && NumCodesToWrite == 0));
-
-    //
-    // FIXME: Make overflow checks on WriteCoord !!!!!!
-    //
+    ASSERT((StringBuffer != NULL) || (StringBuffer == NULL && NumCodesToRead == 0));
 
-    if (NumCodesWritten) *NumCodesWritten = 0;
+    if (NumCodesRead)
+        *NumCodesRead = 0;
 
     switch (CodeType)
     {
         case CODE_ASCII:
-            CodeSize = RTL_FIELD_SIZE(CODE_ELEMENT, AsciiChar);
-            break;
+            return IntReadConsoleOutputStringAscii(Console,
+                                                   Buffer,
+                                                   StringBuffer,
+                                                   NumCodesToRead,
+                                                   ReadCoord,
+                                                   NumCodesRead);
 
         case CODE_UNICODE:
-            CodeSize = RTL_FIELD_SIZE(CODE_ELEMENT, UnicodeChar);
-            break;
+            return IntReadConsoleOutputStringUnicode(Console,
+                                                     Buffer,
+                                                     StringBuffer,
+                                                     NumCodesToRead,
+                                                     ReadCoord,
+                                                     NumCodesRead);
 
         case CODE_ATTRIBUTE:
-            CodeSize = RTL_FIELD_SIZE(CODE_ELEMENT, Attribute);
-            break;
+            return IntReadConsoleOutputStringAttributes(Console,
+                                                        Buffer,
+                                                        StringBuffer,
+                                                        NumCodesToRead,
+                                                        ReadCoord,
+                                                        NumCodesRead);
 
         default:
             return STATUS_INVALID_PARAMETER;
     }
+}
 
-    if (CodeType == CODE_ASCII)
+static NTSTATUS
+IntWriteConsoleOutputStringUnicode(
+    IN PCONSOLE Console,
+    IN PTEXTMODE_SCREEN_BUFFER Buffer,
+    IN PVOID StringBuffer,
+    IN ULONG NumCodesToWrite,
+    IN PCOORD WriteCoord,
+    OUT PULONG NumCodesWritten OPTIONAL)
+{
+    NTSTATUS Status = STATUS_SUCCESS;
+    PWCHAR WriteBuffer = StringBuffer;
+    ULONG i, X, Y, Length;
+    PCHAR_INFO Ptr;
+    BOOLEAN bCJK = Console->IsCJK;
+
+    if (!StringBuffer)
+        goto Cleanup;
+
+    X = WriteCoord->X;
+    Y = (WriteCoord->Y + Buffer->VirtualY) % Buffer->ScreenBufferSize.Y;
+    Length = NumCodesToWrite;
+
+    for (i = 0; i < Length; ++i)
     {
-        /* Convert the ASCII string into Unicode before writing it to the console */
-        Length = MultiByteToWideChar(Console->OutputCodePage, 0,
-                                     (PCHAR)StringBuffer,
-                                     NumCodesToWrite,
-                                     NULL, 0);
-        tmpString = WriteBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, Length * sizeof(WCHAR));
-        if (WriteBuffer)
+        Ptr = ConioCoordToPointer(Buffer, X, Y);
+
+        Ptr->Char.UnicodeChar = *WriteBuffer;
+        ++WriteBuffer;
+
+        ++X;
+        if (X == Buffer->ScreenBufferSize.X)
         {
-            MultiByteToWideChar(Console->OutputCodePage, 0,
-                                (PCHAR)StringBuffer,
-                                NumCodesToWrite,
-                                (PWCHAR)WriteBuffer, Length);
+            X = 0;
+            ++Y;
+            if (Y == Buffer->ScreenBufferSize.Y)
+            {
+                Y = 0;
+            }
         }
-        else
+
+        /* For Chinese, Japanese and Korean */
+        if (bCJK && Ptr->Char.UnicodeChar >= 0x80 &&
+            mk_wcwidth_cjk(Ptr->Char.UnicodeChar) == 2)
         {
-            Status = STATUS_NO_MEMORY;
-        }
+            /* A full-width character cannot cross a line boundary */
+            if (X == Buffer->ScreenBufferSize.X - 1)
+            {
+                /* go to next line */
+                X = 0;
+                ++Y;
+                if (Y == Buffer->ScreenBufferSize.Y)
+                {
+                    Y = 0;
+                }
+                Ptr = ConioCoordToPointer(Buffer, X, Y);
+            }
+
+            /* the leading byte */
+            Ptr->Attributes = Buffer->ScreenDefaultAttrib;
+            Ptr->Attributes |= COMMON_LVB_LEADING_BYTE;
+            ++i;
+
+            /* the trailing byte */
+            Ptr = ConioCoordToPointer(Buffer, X, Y);
+            Ptr->Attributes = Buffer->ScreenDefaultAttrib;
+            Ptr->Attributes |= COMMON_LVB_TRAILING_BYTE;
 
-        // FIXME: Quick fix: fix the CodeType and CodeSize since the
-        // ASCII string was converted into UNICODE.
-        // A proper fix needs to be written.
-        CodeType = CODE_UNICODE;
-        CodeSize = RTL_FIELD_SIZE(CODE_ELEMENT, UnicodeChar);
+            ++X;
+            if (X == Buffer->ScreenBufferSize.X)
+            {
+                X = 0;
+                ++Y;
+                if (Y == Buffer->ScreenBufferSize.Y)
+                {
+                    Y = 0;
+                }
+            }
+        }
     }
-    else
+
+Cleanup:
+    if (NumCodesWritten)
+        *NumCodesWritten = NumCodesToWrite;
+    return Status;
+}
+
+static NTSTATUS
+IntWriteConsoleOutputStringAscii(
+    IN PCONSOLE Console,
+    IN PTEXTMODE_SCREEN_BUFFER Buffer,
+    IN PVOID StringBuffer,
+    IN ULONG NumCodesToWrite,
+    IN PCOORD WriteCoord,
+    OUT PULONG NumCodesWritten OPTIONAL)
+{
+    NTSTATUS Status;
+    PWCHAR tmpString;
+    ULONG Length;
+
+    if (!StringBuffer)
     {
-        /* For CODE_UNICODE or CODE_ATTRIBUTE, we are already OK */
-        WriteBuffer = StringBuffer;
+        if (NumCodesWritten)
+            *NumCodesWritten = NumCodesToWrite;
+
+        return STATUS_SUCCESS;
     }
 
-    if (WriteBuffer == NULL || !NT_SUCCESS(Status)) goto Cleanup;
+    /* Convert the ASCII string into Unicode before writing it to the console */
+    Length = MultiByteToWideChar(Console->OutputCodePage, 0,
+                                 StringBuffer,
+                                 NumCodesToWrite,
+                                 NULL, 0);
+    tmpString = ConsoleAllocHeap(0, Length * sizeof(WCHAR));
+    if (!tmpString)
+        return STATUS_NO_MEMORY;
+
+    MultiByteToWideChar(Console->OutputCodePage, 0,
+                        StringBuffer,
+                        NumCodesToWrite,
+                        tmpString, Length);
+
+    Status = IntWriteConsoleOutputStringUnicode(Console,
+                                                Buffer,
+                                                tmpString,
+                                                Length,
+                                                WriteCoord,
+                                                NumCodesWritten);
+    ConsoleFreeHeap(tmpString);
+    return Status;
+}
+
+static NTSTATUS
+IntWriteConsoleOutputStringAttribute(
+    IN PCONSOLE Console,
+    IN PTEXTMODE_SCREEN_BUFFER Buffer,
+    IN PVOID StringBuffer,
+    IN ULONG NumCodesToWrite,
+    IN PCOORD WriteCoord,
+    OUT PULONG NumCodesWritten OPTIONAL)
+{
+    NTSTATUS Status = STATUS_SUCCESS;
+    PWORD WriteBuffer = StringBuffer;
+    ULONG i, X, Y, Length;
+    PCHAR_INFO Ptr;
+
+    if (!StringBuffer)
+        goto Cleanup;
 
     X = WriteCoord->X;
     Y = (WriteCoord->Y + Buffer->VirtualY) % Buffer->ScreenBufferSize.Y;
     Length = NumCodesToWrite;
-    // Ptr = ConioCoordToPointer(Buffer, X, Y); // Doesn't work
-    // Ptr = &Buffer->Buffer[X + Y * Buffer->ScreenBufferSize.X]; // May work
 
-    while (Length--)
+    for (i = 0; i < Length; ++i)
     {
-        // Ptr = ConioCoordToPointer(Buffer, X, Y); // Doesn't work either
-        Ptr = &Buffer->Buffer[X + Y * Buffer->ScreenBufferSize.X];
-
-        switch (CodeType)
-        {
-            case CODE_ASCII:
-            case CODE_UNICODE:
-                Ptr->Char.UnicodeChar = *(PWCHAR)WriteBuffer;
-                break;
+        Ptr = ConioCoordToPointer(Buffer, X, Y);
 
-            case CODE_ATTRIBUTE:
-                Ptr->Attributes = *(PWORD)WriteBuffer;
-                break;
-        }
-        WriteBuffer = (PVOID)((ULONG_PTR)WriteBuffer + CodeSize);
-        // ++Ptr;
+        Ptr->Attributes = (*WriteBuffer & ~COMMON_LEAD_TRAIL);
+        ++WriteBuffer;
 
-        // Written++;
-        if (++X == Buffer->ScreenBufferSize.X)
+        ++X;
+        if (X == Buffer->ScreenBufferSize.X)
         {
             X = 0;
-
-            if (++Y == Buffer->ScreenBufferSize.Y)
+            ++Y;
+            if (Y == Buffer->ScreenBufferSize.Y)
             {
                 Y = 0;
             }
         }
     }
 
+Cleanup:
+    if (NumCodesWritten)
+        *NumCodesWritten = NumCodesToWrite;
+    return Status;
+}
+
+NTSTATUS NTAPI
+ConDrvWriteConsoleOutputString(
+    IN PCONSOLE Console,
+    IN PTEXTMODE_SCREEN_BUFFER Buffer,
+    IN CODE_TYPE CodeType,
+    IN PVOID StringBuffer,
+    IN ULONG NumCodesToWrite,
+    IN PCOORD WriteCoord,
+    OUT PULONG NumCodesWritten OPTIONAL)
+{
+    NTSTATUS Status;
+    SMALL_RECT UpdateRect;
+
+    if (Console == NULL || Buffer == NULL || WriteCoord == NULL /* || EndCoord == NULL */)
+    {
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    /* Validity checks */
+    ASSERT(Console == Buffer->Header.Console);
+    ASSERT((StringBuffer != NULL) || (StringBuffer == NULL && NumCodesToWrite == 0));
+
+    if (NumCodesWritten)
+        *NumCodesWritten = 0;
+
+    switch (CodeType)
+    {
+        case CODE_ASCII:
+            Status = IntWriteConsoleOutputStringAscii(
+                Console, Buffer, StringBuffer, NumCodesToWrite, WriteCoord, NumCodesWritten);
+            break;
+
+        case CODE_UNICODE:
+            Status = IntWriteConsoleOutputStringUnicode(
+                Console, Buffer, StringBuffer, NumCodesToWrite, WriteCoord, NumCodesWritten);
+            break;
+
+        case CODE_ATTRIBUTE:
+            Status = IntWriteConsoleOutputStringAttribute(
+                Console, Buffer, StringBuffer, NumCodesToWrite, WriteCoord, NumCodesWritten);
+            break;
+
+        default:
+            Status = STATUS_INVALID_PARAMETER;
+            break;
+    }
+
     if ((PCONSOLE_SCREEN_BUFFER)Buffer == Console->ActiveBuffer)
     {
-        SMALL_RECT UpdateRect;
         ConioComputeUpdateRect(Buffer, &UpdateRect, WriteCoord, NumCodesToWrite);
         TermDrawRegion(Console, &UpdateRect);
     }
 
-    // EndCoord->X = X;
-    // EndCoord->Y = (Y + Buffer->ScreenBufferSize.Y - Buffer->VirtualY) % Buffer->ScreenBufferSize.Y;
-
-Cleanup:
-    if (tmpString) RtlFreeHeap(RtlGetProcessHeap(), 0, tmpString);
-
-    if (NumCodesWritten) *NumCodesWritten = NumCodesToWrite; // Written;
     return Status;
 }
 
@@ -989,8 +1175,9 @@ ConDrvFillConsoleOutput(IN PCONSOLE Console,
                         IN PCOORD WriteCoord,
                         OUT PULONG NumCodesWritten OPTIONAL)
 {
-    DWORD X, Y, Length; // , Written = 0;
+    ULONG X, Y, i;
     PCHAR_INFO Ptr;
+    BOOLEAN bLead, bFullwidth;
 
     if (Console == NULL || Buffer == NULL || WriteCoord == NULL)
     {
@@ -1016,38 +1203,73 @@ ConDrvFillConsoleOutput(IN PCONSOLE Console,
 
     X = WriteCoord->X;
     Y = (WriteCoord->Y + Buffer->VirtualY) % Buffer->ScreenBufferSize.Y;
-    Length = NumCodesToWrite;
     // Ptr = ConioCoordToPointer(Buffer, X, Y); // Doesn't work
     // Ptr = &Buffer->Buffer[X + Y * Buffer->ScreenBufferSize.X]; // May work
 
-    while (Length--)
+    /* For Chinese, Japanese and Korean */
+    bLead = TRUE;
+    bFullwidth = FALSE;
+    if (Console->IsCJK)
+    {
+        bFullwidth = (mk_wcwidth_cjk(Code.UnicodeChar) == 2);
+        if (X > 0)
+        {
+            Ptr = ConioCoordToPointer(Buffer, X - 1, Y);
+            if (Ptr->Attributes & COMMON_LVB_LEADING_BYTE)
+            {
+                Ptr->Char.UnicodeChar = L' ';
+                Ptr->Attributes &= ~COMMON_LVB_LEADING_BYTE;
+            }
+        }
+    }
+
+    for (i = 0; i < NumCodesToWrite; ++i)
     {
-        // Ptr = ConioCoordToPointer(Buffer, X, Y); // Doesn't work either
-        Ptr = &Buffer->Buffer[X + Y * Buffer->ScreenBufferSize.X];
+        Ptr = ConioCoordToPointer(Buffer, X, Y);
 
         switch (CodeType)
         {
             case CODE_ASCII:
             case CODE_UNICODE:
                 Ptr->Char.UnicodeChar = Code.UnicodeChar;
+                Ptr->Attributes &= ~COMMON_LEAD_TRAIL;
+                if (bFullwidth)
+                {
+                    if (bLead)
+                        Ptr->Attributes |= COMMON_LVB_LEADING_BYTE;
+                    else
+                        Ptr->Attributes |= COMMON_LVB_TRAILING_BYTE;
+                }
                 break;
 
             case CODE_ATTRIBUTE:
-                Ptr->Attributes = Code.Attribute;
+                Ptr->Attributes &= ~COMMON_LEAD_TRAIL;
+                Ptr->Attributes |= (Code.Attribute & ~COMMON_LEAD_TRAIL);
                 break;
         }
-        // ++Ptr;
 
-        // Written++;
-        if (++X == Buffer->ScreenBufferSize.X)
+        ++X;
+        if (X == Buffer->ScreenBufferSize.X)
         {
             X = 0;
-
-            if (++Y == Buffer->ScreenBufferSize.Y)
+            ++Y;
+            if (Y == Buffer->ScreenBufferSize.Y)
             {
                 Y = 0;
             }
         }
+
+        bLead = !bLead;
+    }
+
+    if ((NumCodesToWrite & 1) & bFullwidth)
+    {
+        if (X + Y * Buffer->ScreenBufferSize.X > 0)
+        {
+            Ptr = ConioCoordToPointer(Buffer, X - 1, Y);
+            Ptr->Char.UnicodeChar = L' ';
+            Ptr->Attributes &= ~COMMON_LEAD_TRAIL;
+        }
     }
 
     if ((PCONSOLE_SCREEN_BUFFER)Buffer == Console->ActiveBuffer)
@@ -1071,6 +1293,8 @@ ConDrvGetConsoleScreenBufferInfo(IN  PCONSOLE Console,
                                  OUT PCOORD MaximumViewSize,
                                  OUT PWORD  Attributes)
 {
+    COORD LargestWindowSize;
+
     if (Console == NULL || Buffer == NULL || ScreenBufferSize == NULL ||
         CursorPosition  == NULL || ViewOrigin == NULL || ViewSize == NULL ||
         MaximumViewSize == NULL || Attributes == NULL)
@@ -1087,8 +1311,14 @@ ConDrvGetConsoleScreenBufferInfo(IN  PCONSOLE Console,
     *ViewSize         = Buffer->ViewSize;
     *Attributes       = Buffer->ScreenDefaultAttrib;
 
-    // FIXME: Refine the computation
-    *MaximumViewSize  = Buffer->ScreenBufferSize;
+    /*
+     * Retrieve the largest possible console window size, taking
+     * into account the size of the console screen buffer.
+     */
+    TermGetLargestConsoleWindowSize(Console, &LargestWindowSize);
+    LargestWindowSize.X = min(LargestWindowSize.X, Buffer->ScreenBufferSize.X);
+    LargestWindowSize.Y = min(LargestWindowSize.Y, Buffer->ScreenBufferSize.Y);
+    *MaximumViewSize = LargestWindowSize;
 
     return STATUS_SUCCESS;
 }
@@ -1222,6 +1452,7 @@ ConDrvSetConsoleWindowInfo(IN PCONSOLE Console,
                            IN PSMALL_RECT WindowRect)
 {
     SMALL_RECT CapturedWindowRect;
+    COORD LargestWindowSize;
 
     if (Console == NULL || Buffer == NULL || WindowRect == NULL)
         return STATUS_INVALID_PARAMETER;
@@ -1231,32 +1462,65 @@ ConDrvSetConsoleWindowInfo(IN PCONSOLE Console,
 
     CapturedWindowRect = *WindowRect;
 
-    if (Absolute == FALSE)
+    if (!Absolute)
     {
-        /* Relative positions given. Transform them to absolute ones */
+        /* Relative positions are given, transform them to absolute ones */
         CapturedWindowRect.Left   += Buffer->ViewOrigin.X;
         CapturedWindowRect.Top    += Buffer->ViewOrigin.Y;
         CapturedWindowRect.Right  += Buffer->ViewOrigin.X + Buffer->ViewSize.X - 1;
         CapturedWindowRect.Bottom += Buffer->ViewOrigin.Y + Buffer->ViewSize.Y - 1;
     }
 
-    /* See MSDN documentation on SetConsoleWindowInfo about the performed checks */
-    if ( (CapturedWindowRect.Left < 0) || (CapturedWindowRect.Top < 0)  ||
-         (CapturedWindowRect.Right  >= Buffer->ScreenBufferSize.X)      ||
-         (CapturedWindowRect.Bottom >= Buffer->ScreenBufferSize.Y)      ||
-         (CapturedWindowRect.Right  <= CapturedWindowRect.Left)         ||
-         (CapturedWindowRect.Bottom <= CapturedWindowRect.Top) )
+    /*
+     * The MSDN documentation on SetConsoleWindowInfo is partially wrong about
+     * the performed checks this API performs. While it is correct that the
+     * 'Right'/'Bottom' members cannot be strictly smaller than the 'Left'/'Top'
+     * members, they can be equal.
+     * Also, if the 'Left' or 'Top' members are negative, this is automatically
+     * corrected for, and the window rectangle coordinates are shifted accordingly.
+     */
+    if ((CapturedWindowRect.Right  < CapturedWindowRect.Left) ||
+        (CapturedWindowRect.Bottom < CapturedWindowRect.Top))
+    {
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    /*
+     * Forbid window sizes larger than the largest allowed console window size,
+     * taking into account the size of the console screen buffer.
+     */
+    TermGetLargestConsoleWindowSize(Console, &LargestWindowSize);
+    LargestWindowSize.X = min(LargestWindowSize.X, Buffer->ScreenBufferSize.X);
+    LargestWindowSize.Y = min(LargestWindowSize.Y, Buffer->ScreenBufferSize.Y);
+    if ((CapturedWindowRect.Right - CapturedWindowRect.Left + 1 > LargestWindowSize.X) ||
+        (CapturedWindowRect.Bottom - CapturedWindowRect.Top + 1 > LargestWindowSize.Y))
     {
         return STATUS_INVALID_PARAMETER;
     }
 
+    /* Shift the window rectangle coordinates if 'Left' or 'Top' are negative */
+    if (CapturedWindowRect.Left < 0)
+    {
+        CapturedWindowRect.Right -= CapturedWindowRect.Left;
+        CapturedWindowRect.Left = 0;
+    }
+    if (CapturedWindowRect.Top < 0)
+    {
+        CapturedWindowRect.Bottom -= CapturedWindowRect.Top;
+        CapturedWindowRect.Top = 0;
+    }
+
+    /* Clip the window rectangle to the screen buffer */
+    CapturedWindowRect.Right  = min(CapturedWindowRect.Right , Buffer->ScreenBufferSize.X);
+    CapturedWindowRect.Bottom = min(CapturedWindowRect.Bottom, Buffer->ScreenBufferSize.Y);
+
     Buffer->ViewOrigin.X = CapturedWindowRect.Left;
     Buffer->ViewOrigin.Y = CapturedWindowRect.Top;
 
     Buffer->ViewSize.X = CapturedWindowRect.Right - CapturedWindowRect.Left + 1;
     Buffer->ViewSize.Y = CapturedWindowRect.Bottom - CapturedWindowRect.Top + 1;
 
-    // TermResizeTerminal(Console);
+    TermResizeTerminal(Console);
 
     return STATUS_SUCCESS;
 }