[WIN32SS][WINSRV] Fullwidth character handling Part 2 (#2240)
[reactos.git] / win32ss / user / winsrv / consrv / condrv / text.c
index de0c740..92a6f4d 100644 (file)
@@ -14,6 +14,8 @@
 #define NDEBUG
 #include <debug.h>
 
+#define COMMON_LEAD_TRAIL (COMMON_LVB_LEADING_BYTE | COMMON_LVB_TRAILING_BYTE)
+
 /* GLOBALS ********************************************************************/
 
 /*
@@ -512,8 +514,7 @@ ConDrvReadConsoleOutput(IN PCONSOLE Console,
                 WideCharToMultiByte(Console->OutputCodePage, 0, &Ptr->Char.UnicodeChar, 1,
                                     &CurCharInfo->Char.AsciiChar, 1, NULL, NULL);
             }
-            CurCharInfo->Attributes =
-                (Ptr->Attributes & ~(COMMON_LVB_LEADING_BYTE | COMMON_LVB_TRAILING_BYTE));
+            CurCharInfo->Attributes = (Ptr->Attributes & ~COMMON_LEAD_TRAIL);
             ++Ptr;
             ++CurCharInfo;
         }
@@ -734,7 +735,7 @@ IntReadConsoleOutputStringAscii(IN PCONSOLE Console,
     SHORT Ypos = (ReadCoord->Y + Buffer->VirtualY) % Buffer->ScreenBufferSize.Y;
     ULONG i;
     PCHAR_INFO Ptr;
-    BOOL bCJK = Console->IsCJK;
+    BOOLEAN bCJK = Console->IsCJK;
 
     for (i = 0; i < NumCodesToRead; ++i)
     {
@@ -791,7 +792,7 @@ IntReadConsoleOutputStringUnicode(IN PCONSOLE Console,
     SHORT Ypos = (ReadCoord->Y + Buffer->VirtualY) % Buffer->ScreenBufferSize.Y;
     ULONG i, nNumChars = 0;
     PCHAR_INFO Ptr;
-    BOOL bCJK = Console->IsCJK;
+    BOOLEAN bCJK = Console->IsCJK;
 
     for (i = 0; i < NumCodesToRead; ++i, ++nNumChars)
     {
@@ -932,138 +933,236 @@ ConDrvReadConsoleOutputString(IN PCONSOLE Console,
     }
 }
 
-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)
+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;
-    PVOID WriteBuffer = NULL;
-    PWCHAR tmpString = NULL;
-    ULONG X, Y, Length; // , Written = 0;
-    ULONG CodeSize;
+    PWCHAR WriteBuffer = StringBuffer;
+    ULONG i, X, Y, Length;
     PCHAR_INFO Ptr;
+    BOOLEAN bCJK = Console->IsCJK;
 
-    if (Console == NULL || Buffer == NULL || WriteCoord == NULL /* || EndCoord == NULL */)
-    {
-        return STATUS_INVALID_PARAMETER;
-    }
+    if (!StringBuffer)
+        goto Cleanup;
 
-    /* Validity checks */
-    ASSERT(Console == Buffer->Header.Console);
-    ASSERT((StringBuffer != NULL) || (StringBuffer == NULL && NumCodesToWrite == 0));
-
-    //
-    // FIXME: Make overflow checks on WriteCoord !!!!!!
-    //
-
-    if (NumCodesWritten) *NumCodesWritten = 0;
+    X = WriteCoord->X;
+    Y = (WriteCoord->Y + Buffer->VirtualY) % Buffer->ScreenBufferSize.Y;
+    Length = NumCodesToWrite;
 
-    switch (CodeType)
+    for (i = 0; i < Length; ++i)
     {
-        case CODE_ASCII:
-            CodeSize = RTL_FIELD_SIZE(CODE_ELEMENT, AsciiChar);
-            break;
-
-        case CODE_UNICODE:
-            CodeSize = RTL_FIELD_SIZE(CODE_ELEMENT, UnicodeChar);
-            break;
-
-        case CODE_ATTRIBUTE:
-            CodeSize = RTL_FIELD_SIZE(CODE_ELEMENT, Attribute);
-            break;
+        Ptr = ConioCoordToPointer(Buffer, X, Y);
 
-        default:
-            return STATUS_INVALID_PARAMETER;
-    }
+        Ptr->Char.UnicodeChar = *WriteBuffer;
+        ++WriteBuffer;
 
-    if (CodeType == CODE_ASCII)
-    {
-        /* 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)
+        ++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);
+            }
 
-        // 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);
+            /* 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;
+
+            ++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;
 
-    while (Length--)
+    for (i = 0; i < Length; ++i)
     {
         Ptr = ConioCoordToPointer(Buffer, X, Y);
 
-        switch (CodeType)
-        {
-            case CODE_ASCII:
-            case CODE_UNICODE:
-                Ptr->Char.UnicodeChar = *(PWCHAR)WriteBuffer;
-                break;
-
-            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;
 }
 
@@ -1078,7 +1177,7 @@ ConDrvFillConsoleOutput(IN PCONSOLE Console,
 {
     ULONG X, Y, i;
     PCHAR_INFO Ptr;
-    BOOL bLead, bFullwidth;
+    BOOLEAN bLead, bFullwidth;
 
     if (Console == NULL || Buffer == NULL || WriteCoord == NULL)
     {
@@ -1133,7 +1232,7 @@ ConDrvFillConsoleOutput(IN PCONSOLE Console,
             case CODE_ASCII:
             case CODE_UNICODE:
                 Ptr->Char.UnicodeChar = Code.UnicodeChar;
-                Ptr->Attributes &= ~(COMMON_LVB_LEADING_BYTE | COMMON_LVB_TRAILING_BYTE);
+                Ptr->Attributes &= ~COMMON_LEAD_TRAIL;
                 if (bFullwidth)
                 {
                     if (bLead)
@@ -1144,18 +1243,17 @@ ConDrvFillConsoleOutput(IN PCONSOLE Console,
                 break;
 
             case CODE_ATTRIBUTE:
-                Ptr->Attributes &= ~0xFF;
-                Ptr->Attributes |= (Code.Attribute & 0xFF);
+                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;
             }
@@ -1170,7 +1268,7 @@ ConDrvFillConsoleOutput(IN PCONSOLE Console,
         {
             Ptr = ConioCoordToPointer(Buffer, X - 1, Y);
             Ptr->Char.UnicodeChar = L' ';
-            Ptr->Attributes &= ~(COMMON_LVB_LEADING_BYTE | COMMON_LVB_TRAILING_BYTE);
+            Ptr->Attributes &= ~COMMON_LEAD_TRAIL;
         }
     }