[WIN32SS][WINSRV] Fullwidth character handling Part 2 (#2240)

Follow-up of #2231. Also fix FillConsoleOutputAttribute, WriteConsoleOutputCharacterW and WriteConsoleOutputAttribute functions.
CORE-12451
This commit is contained in:
Katayama Hirofumi MZ 2020-01-12 22:04:57 +09:00 committed by GitHub
parent dbe1d36e5b
commit b41299470b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 323 additions and 184 deletions

View 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,22 +933,195 @@ 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 (!StringBuffer)
goto Cleanup;
X = WriteCoord->X;
Y = (WriteCoord->Y + Buffer->VirtualY) % Buffer->ScreenBufferSize.Y;
Length = NumCodesToWrite;
for (i = 0; i < Length; ++i)
{
Ptr = ConioCoordToPointer(Buffer, X, Y);
Ptr->Char.UnicodeChar = *WriteBuffer;
++WriteBuffer;
++X;
if (X == Buffer->ScreenBufferSize.X)
{
X = 0;
++Y;
if (Y == Buffer->ScreenBufferSize.Y)
{
Y = 0;
}
}
/* For Chinese, Japanese and Korean */
if (bCJK && Ptr->Char.UnicodeChar >= 0x80 &&
mk_wcwidth_cjk(Ptr->Char.UnicodeChar) == 2)
{
/* 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;
++X;
if (X == Buffer->ScreenBufferSize.X)
{
X = 0;
++Y;
if (Y == Buffer->ScreenBufferSize.Y)
{
Y = 0;
}
}
}
}
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)
{
if (NumCodesWritten)
*NumCodesWritten = NumCodesToWrite;
return STATUS_SUCCESS;
}
/* 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;
for (i = 0; i < Length; ++i)
{
Ptr = ConioCoordToPointer(Buffer, X, Y);
Ptr->Attributes = (*WriteBuffer & ~COMMON_LEAD_TRAIL);
++WriteBuffer;
++X;
if (X == Buffer->ScreenBufferSize.X)
{
X = 0;
++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 */)
{
@ -958,112 +1132,37 @@ ConDrvWriteConsoleOutputString(IN PCONSOLE Console,
ASSERT(Console == Buffer->Header.Console);
ASSERT((StringBuffer != NULL) || (StringBuffer == NULL && NumCodesToWrite == 0));
//
// FIXME: Make overflow checks on WriteCoord !!!!!!
//
if (NumCodesWritten) *NumCodesWritten = 0;
if (NumCodesWritten)
*NumCodesWritten = 0;
switch (CodeType)
{
case CODE_ASCII:
CodeSize = RTL_FIELD_SIZE(CODE_ELEMENT, AsciiChar);
Status = IntWriteConsoleOutputStringAscii(
Console, Buffer, StringBuffer, NumCodesToWrite, WriteCoord, NumCodesWritten);
break;
case CODE_UNICODE:
CodeSize = RTL_FIELD_SIZE(CODE_ELEMENT, UnicodeChar);
Status = IntWriteConsoleOutputStringUnicode(
Console, Buffer, StringBuffer, NumCodesToWrite, WriteCoord, NumCodesWritten);
break;
case CODE_ATTRIBUTE:
CodeSize = RTL_FIELD_SIZE(CODE_ELEMENT, Attribute);
Status = IntWriteConsoleOutputStringAttribute(
Console, Buffer, StringBuffer, NumCodesToWrite, WriteCoord, NumCodesWritten);
break;
default:
return STATUS_INVALID_PARAMETER;
}
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)
{
MultiByteToWideChar(Console->OutputCodePage, 0,
(PCHAR)StringBuffer,
NumCodesToWrite,
(PWCHAR)WriteBuffer, Length);
}
else
{
Status = STATUS_NO_MEMORY;
}
// 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);
}
else
{
/* For CODE_UNICODE or CODE_ATTRIBUTE, we are already OK */
WriteBuffer = StringBuffer;
}
if (WriteBuffer == NULL || !NT_SUCCESS(Status)) goto Cleanup;
X = WriteCoord->X;
Y = (WriteCoord->Y + Buffer->VirtualY) % Buffer->ScreenBufferSize.Y;
Length = NumCodesToWrite;
while (Length--)
{
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;
// Written++;
if (++X == Buffer->ScreenBufferSize.X)
{
X = 0;
if (++Y == Buffer->ScreenBufferSize.Y)
{
Y = 0;
}
}
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;
}
}

View file

@ -991,17 +991,8 @@ OnPaint(PGUI_CONSOLE_DATA GuiData)
/* Compose the current screen-buffer on-memory */
if (GetType(ActiveBuffer) == TEXTMODE_BUFFER)
{
if (IsCJKCodePage(ActiveBuffer->Header.Console->OutputCodePage))
{
/* For Chinese, Japanese and Korean */
GuiPaintTextModeBufferCJK((PTEXTMODE_SCREEN_BUFFER)ActiveBuffer,
GuiData, &ps.rcPaint, &rcPaint);
}
else
{
GuiPaintTextModeBuffer((PTEXTMODE_SCREEN_BUFFER)ActiveBuffer,
GuiData, &ps.rcPaint, &rcPaint);
}
GuiPaintTextModeBuffer((PTEXTMODE_SCREEN_BUFFER)ActiveBuffer,
GuiData, &ps.rcPaint, &rcPaint);
}
else /* if (GetType(ActiveBuffer) == GRAPHICS_BUFFER) */
{

View file

@ -121,11 +121,4 @@ GuiPaintTextModeBuffer(PTEXTMODE_SCREEN_BUFFER Buffer,
PRECT rcView,
PRECT rcFramebuffer);
/* For Chinese, Japanese and Korean */
VOID
GuiPaintTextModeBufferCJK(PTEXTMODE_SCREEN_BUFFER Buffer,
PGUI_CONSOLE_DATA GuiData,
PRECT rcView,
PRECT rcFramebuffer);
/* EOF */

View file

@ -405,54 +405,88 @@ GuiPaintTextModeBuffer(PTEXTMODE_SCREEN_BUFFER Buffer,
NewFont = GuiData->Font[IsUnderline ? FONT_BOLD : FONT_NORMAL];
OldFont = SelectObject(GuiData->hMemDC, NewFont);
for (Line = TopLine; Line <= BottomLine; Line++)
if (Console->IsCJK)
{
WCHAR LineBuffer[80]; // Buffer containing a part or all the line to be displayed
From = ConioCoordToPointer(Buffer, LeftColumn, Line); // Get the first code of the line
Start = LeftColumn;
To = LineBuffer;
for (Char = LeftColumn; Char <= RightColumn; Char++)
for (Line = TopLine; Line <= BottomLine; Line++)
{
/*
* We flush the buffer if the new attribute is different
* from the current one, or if the buffer is full.
*/
if (From->Attributes != LastAttribute || (Char - Start == sizeof(LineBuffer) / sizeof(WCHAR)))
for (Char = LeftColumn; Char <= RightColumn; Char++)
{
TextOutW(GuiData->hMemDC,
Start * GuiData->CharWidth,
Line * GuiData->CharHeight,
LineBuffer,
Char - Start);
Start = Char;
To = LineBuffer;
From = ConioCoordToPointer(Buffer, Char, Line);
Attribute = From->Attributes;
if (Attribute != LastAttribute)
{
LastAttribute = Attribute;
SetTextColor(GuiData->hMemDC, PaletteRGBFromAttrib(Console, TextAttribFromAttrib(LastAttribute)));
SetBkColor(GuiData->hMemDC, PaletteRGBFromAttrib(Console, BkgdAttribFromAttrib(LastAttribute)));
SetTextColor(GuiData->hMemDC, PaletteRGBFromAttrib(Console, TextAttribFromAttrib(Attribute)));
SetBkColor(GuiData->hMemDC, PaletteRGBFromAttrib(Console, BkgdAttribFromAttrib(Attribute)));
/* Change underline state if needed */
if (!!(LastAttribute & COMMON_LVB_UNDERSCORE) != IsUnderline)
/* Change underline state if needed */
if (!!(Attribute & COMMON_LVB_UNDERSCORE) != IsUnderline)
{
IsUnderline = !!(Attribute & COMMON_LVB_UNDERSCORE);
/* Select the new font */
NewFont = GuiData->Font[IsUnderline ? FONT_BOLD : FONT_NORMAL];
SelectObject(GuiData->hMemDC, NewFont);
}
if (Attribute & COMMON_LVB_TRAILING_BYTE)
continue;
TextOutW(GuiData->hMemDC,
Char * GuiData->CharWidth,
Line * GuiData->CharHeight,
&From->Char.UnicodeChar, 1);
}
}
}
else
{
for (Line = TopLine; Line <= BottomLine; Line++)
{
WCHAR LineBuffer[80]; // Buffer containing a part or all the line to be displayed
From = ConioCoordToPointer(Buffer, LeftColumn, Line); // Get the first code of the line
Start = LeftColumn;
To = LineBuffer;
for (Char = LeftColumn; Char <= RightColumn; Char++)
{
/*
* We flush the buffer if the new attribute is different
* from the current one, or if the buffer is full.
*/
if (From->Attributes != LastAttribute || (Char - Start == sizeof(LineBuffer) / sizeof(WCHAR)))
{
TextOutW(GuiData->hMemDC,
Start * GuiData->CharWidth,
Line * GuiData->CharHeight,
LineBuffer,
Char - Start);
Start = Char;
To = LineBuffer;
Attribute = From->Attributes;
if (Attribute != LastAttribute)
{
IsUnderline = !!(LastAttribute & COMMON_LVB_UNDERSCORE);
/* Select the new font */
NewFont = GuiData->Font[IsUnderline ? FONT_BOLD : FONT_NORMAL];
/* OldFont = */ SelectObject(GuiData->hMemDC, NewFont);
LastAttribute = Attribute;
SetTextColor(GuiData->hMemDC, PaletteRGBFromAttrib(Console, TextAttribFromAttrib(LastAttribute)));
SetBkColor(GuiData->hMemDC, PaletteRGBFromAttrib(Console, BkgdAttribFromAttrib(LastAttribute)));
/* Change underline state if needed */
if (!!(LastAttribute & COMMON_LVB_UNDERSCORE) != IsUnderline)
{
IsUnderline = !!(LastAttribute & COMMON_LVB_UNDERSCORE);
/* Select the new font */
NewFont = GuiData->Font[IsUnderline ? FONT_BOLD : FONT_NORMAL];
SelectObject(GuiData->hMemDC, NewFont);
}
}
}
*(To++) = (From++)->Char.UnicodeChar;
}
*(To++) = (From++)->Char.UnicodeChar;
TextOutW(GuiData->hMemDC,
Start * GuiData->CharWidth,
Line * GuiData->CharHeight,
LineBuffer,
RightColumn - Start + 1);
}
TextOutW(GuiData->hMemDC,
Start * GuiData->CharWidth,
Line * GuiData->CharHeight,
LineBuffer,
RightColumn - Start + 1);
}
/* Restore the old font */
@ -479,12 +513,35 @@ GuiPaintTextModeBuffer(PTEXTMODE_SCREEN_BUFFER Buffer,
CursorBrush = CreateSolidBrush(PaletteRGBFromAttrib(Console, TextAttribFromAttrib(Attribute)));
OldBrush = SelectObject(GuiData->hMemDC, CursorBrush);
PatBlt(GuiData->hMemDC,
CursorX * GuiData->CharWidth,
CursorY * GuiData->CharHeight + (GuiData->CharHeight - CursorHeight),
GuiData->CharWidth,
CursorHeight,
PATCOPY);
if (Attribute & COMMON_LVB_LEADING_BYTE)
{
/* The caret is on the leading byte */
PatBlt(GuiData->hMemDC,
CursorX * GuiData->CharWidth,
CursorY * GuiData->CharHeight + (GuiData->CharHeight - CursorHeight),
GuiData->CharWidth * 2,
CursorHeight,
PATCOPY);
}
else if (Attribute & COMMON_LVB_TRAILING_BYTE)
{
/* The caret is on the trailing byte */
PatBlt(GuiData->hMemDC,
(CursorX - 1) * GuiData->CharWidth,
CursorY * GuiData->CharHeight + (GuiData->CharHeight - CursorHeight),
GuiData->CharWidth * 2,
CursorHeight,
PATCOPY);
}
else
{
PatBlt(GuiData->hMemDC,
CursorX * GuiData->CharWidth,
CursorY * GuiData->CharHeight + (GuiData->CharHeight - CursorHeight),
GuiData->CharWidth,
CursorHeight,
PATCOPY);
}
SelectObject(GuiData->hMemDC, OldBrush);
DeleteObject(CursorBrush);
@ -494,15 +551,4 @@ GuiPaintTextModeBuffer(PTEXTMODE_SCREEN_BUFFER Buffer,
LeaveCriticalSection(&Console->Lock);
}
/* For Chinese, Japanese and Korean */
VOID
GuiPaintTextModeBufferCJK(PTEXTMODE_SCREEN_BUFFER Buffer,
PGUI_CONSOLE_DATA GuiData,
PRECT rcView,
PRECT rcFramebuffer)
{
/* FIXME */
GuiPaintTextModeBuffer(Buffer, GuiData, rcView, rcFramebuffer);
}
/* EOF */

View file

@ -558,6 +558,17 @@ ConioWriteConsole(PFRONTEND FrontEnd,
}
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);