reactos/drivers/sac/driver/vtutf8chan.c

1388 lines
45 KiB
C

/*
* PROJECT: ReactOS Drivers
* LICENSE: BSD - See COPYING.ARM in the top level directory
* FILE: drivers/sac/driver/vtutf8chan.c
* PURPOSE: Driver for the Server Administration Console (SAC) for EMS
* PROGRAMMERS: ReactOS Portable Systems Group
*/
/* INCLUDES *******************************************************************/
#include "sacdrv.h"
/* GLOBALS ********************************************************************/
CHAR IncomingUtf8ConversionBuffer[4];
WCHAR IncomingUnicodeValue;
SAC_STATIC_ESCAPE_STRING SacStaticEscapeStrings [] =
{
{ VT_ANSI_CURSOR_UP, 2, SacCursorUp },
{ VT_ANSI_CURSOR_DOWN, 2, SacCursorDown },
{ VT_ANSI_CURSOR_RIGHT, 2, SacCursorRight },
{ VT_ANSI_CURSOR_LEFT, 2, SacCursorLeft },
{ VT_220_BACKTAB, 3, SacBackTab },
{ VT_ANSI_ERASE_END_LINE, 2, SacEraseEndOfLine },
{ VT_ANSI_ERASE_START_LINE, 3, SacEraseStartOfLine },
{ VT_ANSI_ERASE_ENTIRE_LINE, 3, SacEraseLine },
{ VT_ANSI_ERASE_DOWN_SCREEN, 2, SacEraseEndOfScreen },
{ VT_ANSI_ERASE_UP_SCREEN, 3, SacEraseStartOfScreen },
{ VT_ANSI_ERASE_ENTIRE_SCREEN, 3, SacEraseScreen },
};
/* FUNCTIONS ******************************************************************/
FORCEINLINE
VOID
VTUTF8ChannelAssertCursor(IN PSAC_CHANNEL Channel)
{
ASSERT(Channel->CursorRow < SAC_VTUTF8_ROW_HEIGHT);
ASSERT(Channel->CursorCol < SAC_VTUTF8_COL_WIDTH);
}
BOOLEAN
NTAPI
VTUTF8ChannelScanForNumber(IN PWCHAR String,
OUT PULONG Number)
{
/* If the first character is invalid, fail early */
if ((*String < L'0') || (*String > L'9')) return FALSE;
/* Otherwise, initialize the output and loop the string */
*Number = 0;
while ((*String >= L'0') && (*String <= L'9'))
{
/* Save the first decimal */
*Number = 10 * *Number;
/* Compute and add the second one */
*Number += *++String - L'0';
}
/* All done */
return TRUE;
}
NTSTATUS
NTAPI
VTUTF8ChannelAnsiDispatch(IN PSAC_CHANNEL Channel,
IN SAC_ANSI_DISPATCH AnsiCode,
IN INT* Data,
IN ULONG Length)
{
NTSTATUS Status = STATUS_SUCCESS;
PCHAR LocalBuffer = NULL, Tmp;
INT l;
CHECK_PARAMETER1(Channel);
/* Check which ANSI sequence we should output */
switch (AnsiCode)
{
/* Send the [2J (Clear Screen and Reset Cursor) */
case SacAnsiClearScreen:
Tmp = "\x1B[2J";
break;
/* Send the [0J (Clear From Position Till End Of Screen) */
case SacAnsiClearEndOfScreen:
Tmp = "\x1B[0J";
break;
/* Send the [0K (Clear from Position Till End Of Line) */
case SacAnsiClearEndOfLine:
Tmp = "\x1B[0K";
break;
/* Send a combination of two [#m attribute changes */
case SacAnsiSetColors:
/* Allocate a small local buffer for it */
LocalBuffer = SacAllocatePool(SAC_VTUTF8_COL_WIDTH, GLOBAL_BLOCK_TAG);
CHECK_ALLOCATION(LocalBuffer);
/* Caller should have sent two colors as two integers */
if (!(Data) || (Length != 8))
{
Status = STATUS_INVALID_PARAMETER;
break;
}
/* Create the escape sequence string */
l = sprintf(LocalBuffer, "\x1B[%dm\x1B[%dm", Data[1], Data[0]);
ASSERT((l + 1)*sizeof(UCHAR) < SAC_VTUTF8_COL_WIDTH);
ASSERT(LocalBuffer);
Tmp = LocalBuffer;
break;
/* Send the [#;#H (Cursor Position) sequence */
case SacAnsiSetPosition:
/* Allocate a small local buffer for it */
LocalBuffer = SacAllocatePool(SAC_VTUTF8_COL_WIDTH, GLOBAL_BLOCK_TAG);
CHECK_ALLOCATION(LocalBuffer);
/* Caller should have sent the position as two integers */
if (!(Data) || (Length != 8))
{
Status = STATUS_INVALID_PARAMETER;
break;
}
/* Create the escape sequence string */
l = sprintf(LocalBuffer, "\x1B[%d;%dH", Data[1] + 1, Data[0] + 1);
ASSERT((l + 1)*sizeof(UCHAR) < SAC_VTUTF8_COL_WIDTH);
ASSERT(LocalBuffer);
Tmp = LocalBuffer;
break;
/* Send the [0m sequence (Set Attribute 0) */
case SacAnsiClearAttributes:
Tmp = "\x1B[0m";
break;
/* Send the [7m sequence (Set Attribute 7) */
case SacAnsiSetInverseAttribute:
Tmp = "\x1B[7m";
break;
/* Send the [27m sequence (Set Attribute 27) */
case SacAnsiClearInverseAttribute:
Tmp = "\x1B[27m";
break;
/* Send the [5m sequence (Set Attribute 5) */
case SacAnsiSetBlinkAttribute:
Tmp = "\x1B[5m";
break;
/* Send the [25m sequence (Set Attribute 25) */
case SacAnsiClearBlinkAttribute:
Tmp = "\x1B[25m";
break;
/* Send the [1m sequence (Set Attribute 1) */
case SacAnsiSetBoldAttribute:
Tmp = "\x1B[1m";
break;
/* Send the [22m sequence (Set Attribute 22) */
case SacAnsiClearBoldAttribute:
Tmp = "\x1B[22m";
break;
/* We don't recognize it */
default:
Status = STATUS_INVALID_PARAMETER;
break;
}
/* Did everything work above? */
if (NT_SUCCESS(Status))
{
/* Go write out the sequence */
Status = ConMgrWriteData(Channel, Tmp, strlen(Tmp));
if (NT_SUCCESS(Status))
{
/* Now flush it */
Status = ConMgrFlushData(Channel);
}
}
/* Free the temporary buffer, if any, and return the status */
if (LocalBuffer) SacFreePool(LocalBuffer);
return Status;
}
NTSTATUS
NTAPI
VTUTF8ChannelProcessAttributes(IN PSAC_CHANNEL Channel,
IN UCHAR Attribute)
{
NTSTATUS Status;
CHECK_PARAMETER(Channel);
/* Set bold if needed */
Status = VTUTF8ChannelAnsiDispatch(Channel,
Attribute & SAC_CELL_FLAG_BOLD ?
SacAnsiSetBoldAttribute :
SacAnsiClearBoldAttribute,
NULL,
0);
if (!NT_SUCCESS(Status)) return Status;
/* Set blink if needed */
Status = VTUTF8ChannelAnsiDispatch(Channel,
Attribute & SAC_CELL_FLAG_BLINK ?
SacAnsiSetBlinkAttribute :
SacAnsiClearBlinkAttribute,
NULL,
0);
if (!NT_SUCCESS(Status)) return Status;
/* Set inverse if needed */
return VTUTF8ChannelAnsiDispatch(Channel,
Attribute & SAC_CELL_FLAG_INVERTED ?
SacAnsiSetInverseAttribute :
SacAnsiClearInverseAttribute,
NULL,
0);
}
//
// This function is the guts of the sequences that SAC supports.
//
// It is written to conform to the way that Microsoft's SAC driver interprets
// the ANSI standard. If you want to extend and/or "fix" it, please use a flag
// that can be set in the Registry to enable "extended" ANSI support or etc...
//
// Hermes, I'm looking at you, buddy.
//
ULONG
NTAPI
VTUTF8ChannelConsumeEscapeSequence(IN PSAC_CHANNEL Channel,
IN PWCHAR String)
{
ULONG Number, Number2, Number3, i, Action, Result;
PWCHAR Sequence;
PSAC_VTUTF8_SCREEN Screen;
ASSERT(String[0] == VT_ANSI_ESCAPE);
/* Microsoft's driver does this after the O(n) check below. Be smarter. */
if (String[1] != VT_ANSI_COMMAND) return 0;
/* Now that we know it's a valid command, look through the common cases */
for (i = 0; i < RTL_NUMBER_OF(SacStaticEscapeStrings); i++)
{
/* Check if an optimized sequence was detected */
if (!wcsncmp(String + 1,
SacStaticEscapeStrings[i].Sequence,
SacStaticEscapeStrings[i].Size))
{
/* Yep, return the right action, length, and set optionals to 1 */
Action = SacStaticEscapeStrings[i].Action;
Result = SacStaticEscapeStrings[i].Size + 1;
Number = Number2 = Number3 = 1;
goto ProcessString;
}
}
/* It's a more complex sequence, start parsing it */
Result = 0;
Sequence = String + 2;
/* First, check for the cursor sequences. This is useless due to above. */
switch (*Sequence)
{
case VT_ANSI_CURSOR_UP_CHAR:
Action = SacCursorUp;
goto ProcessString;
case VT_ANSI_CURSOR_DOWN_CHAR:
Action = SacCursorDown;
goto ProcessString;
case VT_ANSI_CURSOR_RIGHT_CHAR:
Action = SacCursorLeft; //bug
goto ProcessString;
case VT_ANSI_CURSOR_LEFT_CHAR:
Action = SacCursorRight; //bug
goto ProcessString;
case VT_ANSI_ERASE_LINE_CHAR:
Action = SacEraseEndOfLine;
goto ProcessString;
default:
break;
}
/* This must be a sequence starting with ESC[# */
if (!VTUTF8ChannelScanForNumber(Sequence, &Number)) return 0;
while ((*Sequence >= L'0') && (*Sequence <= L'9')) Sequence++;
/* Check if this is ESC[#m */
if (*Sequence == VT_ANSI_SET_ATTRIBUTE_CHAR)
{
/* Some attribute is being set, go over the ones we support */
switch (Number)
{
/* Make the font standard */
case Normal:
Action = SacFontNormal;
break;
/* Make the font bold */
case Bold:
Action = SacFontBold;
break;
/* Make the font blink */
case SlowBlink:
Action = SacFontBlink;
break;
/* Make the font colors inverted */
case Inverse:
Action = SacFontInverse;
break;
/* Make the font standard intensity */
case BoldOff:
Action = SacFontBoldOff;
break;
/* Turn off blinking */
case BlinkOff:
Action = SacFontBlinkOff;
break;
/* Turn off inverted colors */
case InverseOff:
Action = SacFontInverseOff;
break;
/* Something else... */
default:
/* Is a background color being set? */
if ((Number < SetBackColorStart) || (Number > SetBackColorMax))
{
/* Nope... is it the foreground color? */
if ((Number < SetColorStart) || (Number > SetColorMax))
{
/* Nope. SAC expects no other attributes so bail out */
ASSERT(FALSE);
return 0;
}
/* Yep -- the number will tell us which */
Action = SacSetFontColor;
}
else
{
/* Yep -- the number will tell us which */
Action = SacSetBackgroundColor;
}
break;
}
/* In all cases, we're done here */
goto ProcessString;
}
/* The only allowed possibility now is ESC[#;# */
if (*Sequence != VT_ANSI_SEPARATOR_CHAR) return 0;
Sequence++;
if (!VTUTF8ChannelScanForNumber(Sequence, &Number2)) return 0;
while ((*Sequence >= L'0') && (*Sequence <= L'9')) Sequence++;
/* There's two valid forms accepted at this point: ESC[#;#m and ESC[#;#H */
switch (*Sequence)
{
/* This is ESC[#;#m -- used to set both colors at once */
case VT_ANSI_SET_ATTRIBUTE_CHAR:
Action = SacSetColors;
goto ProcessString;
/* This is ESC[#;#H -- used to set the cursor position */
case VT_ANSI_CUP_CURSOR_CHAR:
Action = SacSetCursorPosition;
goto ProcessString;
/* Finally, this *could* be ESC[#;#; -- we'll keep parsing */
case VT_ANSI_SEPARATOR_CHAR:
Sequence++;
break;
/* Abandon anything else */
default:
return 0;
}
/* The SAC seems to accept a few more possibilities if a ';' follows... */
switch (*Sequence)
{
/* Both ESC[#;#;H and ESC[#;#;f are really the same command */
case VT_ANSI_CUP_CURSOR_CHAR:
case VT_ANSI_HVP_CURSOR_CHAR:
/* It's unclear why MS doesn't allow the HVP sequence on its own */
Action = SacSetCursorPosition;
goto ProcessString;
/* And this is the ESC[#;#;r command to set the scroll region... */
case VT_ANSI_SCROLL_CHAR:
/* Again, not clear why ESC[#;#r isn't supported */
Action = SacSetScrollRegion;
goto ProcessString;
/* Anything else must be ESC[#;#;# */
default:
break;
}
/* Get the last "#" */
if (!VTUTF8ChannelScanForNumber(Sequence, &Number3)) return 0;
while ((*Sequence >= L'0') && (*Sequence <= L'9')) Sequence++;
/* And now the only remaining possibility is ESC[#;#;#;m */
if (*Sequence == VT_ANSI_SET_ATTRIBUTE_CHAR)
{
/* Which sets both color and attributes in one command */
Action = SacSetColorsAndAttributes;
goto ProcessString;
}
/* No other sequences supported */
return 0;
ProcessString:
/* Unless we got here from the optimized path, calculate the length */
if (!Result) Result = Sequence - String + 1;
/* Get the current cell buffer */
Screen = (PSAC_VTUTF8_SCREEN)Channel->OBuffer;
VTUTF8ChannelAssertCursor(Channel);
/* Handle all the supported SAC ANSI commands */
switch (Action)
{
case SacCursorUp:
/* Check if we are scrolling too high */
if (Channel->CursorRow < Number)
{
/* Reset the row to the top */
Channel->CursorRow = 0;
}
else
{
/* We're fine -- scroll up by that much */
Channel->CursorRow -= Number;
}
/* All done */
VTUTF8ChannelAssertCursor(Channel);
break;
case SacCursorDown:
/* Check if we are scrolling too low */
if (Channel->CursorRow >= SAC_VTUTF8_ROW_HEIGHT)
{
/* Reset the row to the bottom */
Channel->CursorRow = SAC_VTUTF8_ROW_HEIGHT;
}
else
{
/* We're fine -- scroll down by that much */
Channel->CursorRow += Number;
}
/* All done */
VTUTF8ChannelAssertCursor(Channel);
break;
case SacCursorLeft:
/* Check if we're scrolling too much to the left */
if (Channel->CursorCol < Number)
{
/* Reset the column to the left-most margin */
Channel->CursorCol = 0;
}
else
{
/* We're fine -- scroll left by that much */
Channel->CursorCol -= Number;
}
/* All done */
VTUTF8ChannelAssertCursor(Channel);
break;
case SacCursorRight:
/* Check if we're scrolling too much to the right */
if (Channel->CursorCol >= SAC_VTUTF8_COL_WIDTH)
{
/* Reset the column to the right-most margin */
Channel->CursorCol = SAC_VTUTF8_COL_WIDTH;
}
else
{
/* We're fine -- scroll right by that much */
Channel->CursorCol += Number;
}
/* All done */
VTUTF8ChannelAssertCursor(Channel);
break;
case SacFontNormal:
/* Reset the cell attributes */
Channel->CellFlags = 0;
Channel->CellBackColor = SetBackColorBlack;
Channel->CellForeColor = SetColorWhite;
break;
case SacFontBlink:
/* Set the appropriate flag */
Channel->CellFlags |= SAC_CELL_FLAG_BLINK;
break;
case SacFontBlinkOff:
/* Clear the appropriate flag */
Channel->CellFlags &= ~SAC_CELL_FLAG_BLINK;
break;
case SacFontBold:
/* Set the appropriate flag */
Channel->CellFlags |= SAC_CELL_FLAG_BOLD;
break;
case SacFontBoldOff:
/* Clear the appropriate flag */
Channel->CellFlags &= ~SAC_CELL_FLAG_BOLD;
break;
case SacFontInverse:
/* Set the appropriate flag */
Channel->CellFlags |= SAC_CELL_FLAG_INVERTED;
break;
case SacFontInverseOff:
/* Clear the appropriate flag */
Channel->CellFlags &= ~SAC_CELL_FLAG_INVERTED;
break;
case SacEraseEndOfLine:
/* Loop all columns in this line past the current position */
for (i = Channel->CursorCol; i < SAC_VTUTF8_COL_WIDTH; i++)
{
/* Replace everything after the current position with blanks */
Screen->Cell[Channel->CursorRow][i].CellFlags = Channel->CellFlags;
Screen->Cell[Channel->CursorRow][i].CellBackColor = Channel->CellForeColor;
Screen->Cell[Channel->CursorRow][i].CellForeColor = Channel->CellBackColor;
Screen->Cell[Channel->CursorRow][i].Char = L' ';
}
break;
case SacEraseStartOfLine:
/* Loop all columns in this line, before the current position */
for (i = 0; i < (Channel->CursorCol + 1); i++)
{
/* Replace everything after the current position with blanks */
Screen->Cell[Channel->CursorRow][i].CellFlags = Channel->CellFlags;
Screen->Cell[Channel->CursorRow][i].CellBackColor = Channel->CellForeColor;
Screen->Cell[Channel->CursorRow][i].CellForeColor = Channel->CellBackColor;
Screen->Cell[Channel->CursorRow][i].Char = L' ';
}
break;
case SacEraseLine:
/* Loop all the columns in this line */
for (i = 0; i < SAC_VTUTF8_COL_WIDTH; i++)
{
/* Replace them all with blanks */
Screen->Cell[Channel->CursorRow][i].CellFlags = Channel->CellFlags;
Screen->Cell[Channel->CursorRow][i].CellBackColor = Channel->CellForeColor;
Screen->Cell[Channel->CursorRow][i].CellForeColor = Channel->CellBackColor;
Screen->Cell[Channel->CursorRow][i].Char = L' ';
}
break;
case SacEraseEndOfScreen:
ASSERT(FALSE); // todo
break;
case SacEraseStartOfScreen:
ASSERT(FALSE); // todo
break;
case SacEraseScreen:
ASSERT(FALSE); // todo
break;
case SacSetCursorPosition:
ASSERT(FALSE); // todo
break;
case SacSetScrollRegion:
ASSERT(FALSE); // todo
break;
case SacSetColors:
/* Set the cell colors */
Channel->CellForeColor = Number;
Channel->CellBackColor = Number2;
break;
case SacSetBackgroundColor:
/* Set the cell back color */
Channel->CellBackColor = Number;
break;
case SacSetFontColor:
/* Set the cell text color */
Channel->CellForeColor = Number;
break;
case SacSetColorsAndAttributes:
/* Set the cell flag and colors */
Channel->CellFlags = Number;
Channel->CellForeColor = Number2;
Channel->CellBackColor = Number3;
break;
default:
/* Unknown, do nothing */
break;
}
/* Return the length of the sequence */
return Result;
}
NTSTATUS
NTAPI
VTUTF8ChannelOInit(IN PSAC_CHANNEL Channel)
{
PSAC_VTUTF8_SCREEN Screen;
ULONG R, C;
CHECK_PARAMETER(Channel);
/* Set the current channel cell parameters */
Channel->CellFlags = 0;
Channel->CellBackColor = SetBackColorBlack;
Channel->CellForeColor = SetColorWhite;
/* Set the cell buffer position */
Screen = (PSAC_VTUTF8_SCREEN)Channel->OBuffer;
/* Loop the output buffer height by width */
for (R = 0; R < SAC_VTUTF8_ROW_HEIGHT; R++)
{
for (C = 0; C < SAC_VTUTF8_COL_WIDTH; C++)
{
/* For every character, set the defaults */
Screen->Cell[R][C].Char = L' ';
Screen->Cell[R][C].CellBackColor = SetBackColorBlack;
Screen->Cell[R][C].CellForeColor = SetColorWhite;
}
}
/* All done */
return STATUS_SUCCESS;
}
NTSTATUS
NTAPI
VTUTF8ChannelCreate(IN PSAC_CHANNEL Channel)
{
NTSTATUS Status;
CHECK_PARAMETER(Channel);
/* Allocate the output buffer */
Channel->OBuffer = SacAllocatePool(SAC_VTUTF8_OBUFFER_SIZE, GLOBAL_BLOCK_TAG);
CHECK_ALLOCATION(Channel->OBuffer);
/* Allocate the input buffer */
Channel->IBuffer = SacAllocatePool(SAC_VTUTF8_IBUFFER_SIZE, GLOBAL_BLOCK_TAG);
CHECK_ALLOCATION(Channel->IBuffer);
/* Initialize the output stream */
Status = VTUTF8ChannelOInit(Channel);
if (NT_SUCCESS(Status)) return Status;
/* Reset all flags and return success */
_InterlockedExchange(&Channel->ChannelHasNewOBufferData, 0);
_InterlockedExchange(&Channel->ChannelHasNewIBufferData, 0);
return STATUS_SUCCESS;
}
NTSTATUS
NTAPI
VTUTF8ChannelDestroy(IN PSAC_CHANNEL Channel)
{
CHECK_PARAMETER(Channel);
/* Free the buffer and then destroy the channel */
if (Channel->OBuffer) SacFreePool(Channel->OBuffer);
if (Channel->IBuffer) SacFreePool(Channel->IBuffer);
return ChannelDestroy(Channel);
}
NTSTATUS
NTAPI
VTUTF8ChannelORead(IN PSAC_CHANNEL Channel,
IN PCHAR Buffer,
IN ULONG BufferSize,
OUT PULONG ByteCount)
{
ASSERT(FALSE);
return STATUS_NOT_IMPLEMENTED;
}
NTSTATUS
NTAPI
VTUTF8ChannelOFlush(IN PSAC_CHANNEL Channel)
{
NTSTATUS Status;
PSAC_VTUTF8_SCREEN Screen;
INT Color[2], Position[2];
ULONG Utf8ProcessedCount, Utf8Count, R, C, ForeColor, BackColor, Attribute;
PWCHAR TmpBuffer;
BOOLEAN Overflow = FALSE;
CHECK_PARAMETER(Channel);
/* Set the cell buffer position */
Screen = (PSAC_VTUTF8_SCREEN)Channel->OBuffer;
/* Allocate a temporary buffer */
TmpBuffer = SacAllocatePool(40, GLOBAL_BLOCK_TAG);
if (!TmpBuffer)
{
Status = STATUS_NO_MEMORY;
goto Quickie;
}
/* First, clear the screen */
Status = VTUTF8ChannelAnsiDispatch(Channel,
SacAnsiClearScreen,
NULL,
0);
if (!NT_SUCCESS(Status)) goto Quickie;
/* Next, reset the cursor position */
Position[1] = 0;
Position[0] = 0;
Status = VTUTF8ChannelAnsiDispatch(Channel,
SacAnsiSetPosition,
Position,
sizeof(Position));
if (!NT_SUCCESS(Status)) goto Quickie;
/* Finally, reset the attributes */
Status = VTUTF8ChannelAnsiDispatch(Channel,
SacAnsiClearAttributes,
NULL,
0);
if (!NT_SUCCESS(Status)) goto Quickie;
/* Now set the current cell attributes */
Attribute = Channel->CellFlags;
Status = VTUTF8ChannelProcessAttributes(Channel, Attribute);
if (!NT_SUCCESS(Status)) goto Quickie;
/* And set the current cell colors */
ForeColor = Channel->CellForeColor;
BackColor = Channel->CellBackColor;
Color[1] = BackColor;
Color[0] = ForeColor;
Status = VTUTF8ChannelAnsiDispatch(Channel,
SacAnsiSetColors,
Color,
sizeof(Color));
if (!NT_SUCCESS(Status)) goto Quickie;
/* Now loop all the characters in the cell buffer */
for (R = 0; R < SAC_VTUTF8_ROW_HEIGHT; R++)
{
/* Across every row */
for (C = 0; C < SAC_VTUTF8_COL_WIDTH; C++)
{
/* Check if there's been a change in colors */
if ((Screen->Cell[R][C].CellBackColor != BackColor) ||
(Screen->Cell[R][C].CellForeColor != ForeColor))
{
/* New colors are being drawn -- are we also on a new row now? */
if (Overflow)
{
/* Reposition the cursor correctly */
Position[1] = R;
Position[0] = C;
Status = VTUTF8ChannelAnsiDispatch(Channel,
SacAnsiSetPosition,
Position,
sizeof(Position));
if (!NT_SUCCESS(Status)) goto Quickie;
Overflow = FALSE;
}
/* Cache the new colors */
ForeColor = Screen->Cell[R][C].CellForeColor;
BackColor = Screen->Cell[R][C].CellBackColor;
/* Set them on the screen */
Color[1] = BackColor;
Color[0] = ForeColor;
Status = VTUTF8ChannelAnsiDispatch(Channel,
SacAnsiSetColors,
Color,
sizeof(Color));
if (!NT_SUCCESS(Status)) goto Quickie;
}
/* Check if there's been a change in attributes */
if (Screen->Cell[R][C].CellFlags != Attribute)
{
/* Yep! Are we also on a new row now? */
if (Overflow)
{
/* Reposition the cursor correctly */
Position[1] = R;
Position[0] = C;
Status = VTUTF8ChannelAnsiDispatch(Channel,
SacAnsiSetPosition,
Position,
sizeof(Position));
if (!NT_SUCCESS(Status)) goto Quickie;
Overflow = FALSE;
}
/* Set the new attributes on screen */
Attribute = Screen->Cell[R][C].CellFlags;
Status = VTUTF8ChannelProcessAttributes(Channel, Attribute);
if (!NT_SUCCESS(Status)) goto Quickie;
}
/* Time to write the character -- are we on a new row now? */
if (Overflow)
{
/* Reposition the cursor correctly */
Position[1] = R;
Position[0] = C;
Status = VTUTF8ChannelAnsiDispatch(Channel,
SacAnsiSetPosition,
Position,
sizeof(Position));
if (!NT_SUCCESS(Status)) goto Quickie;
Overflow = FALSE;
}
/* Write the character into our temporary buffer */
*TmpBuffer = Screen->Cell[R][C].Char;
TmpBuffer[1] = UNICODE_NULL;
/* Convert it to UTF-8 */
if (!SacTranslateUnicodeToUtf8(TmpBuffer,
1,
Utf8ConversionBuffer,
Utf8ConversionBufferSize,
&Utf8Count,
&Utf8ProcessedCount))
{
/* Bail out if this failed */
Status = STATUS_UNSUCCESSFUL;
goto Quickie;
}
/* Make sure we have a remaining valid character */
if (Utf8Count)
{
/* Write it out on the wire */
Status = ConMgrWriteData(Channel, Utf8ConversionBuffer, Utf8Count);
if (!NT_SUCCESS(Status)) goto Quickie;
}
}
/* All the characters on the row are done, indicate we need a reset */
Overflow = TRUE;
}
/* Everything is done, set the position one last time */
Position[1] = Channel->CursorRow;
Position[0] = Channel->CursorCol;
Status = VTUTF8ChannelAnsiDispatch(Channel,
SacAnsiSetPosition,
Position,
sizeof(Position));
if (!NT_SUCCESS(Status)) goto Quickie;
/* Set the current attribute one last time */
Status = VTUTF8ChannelProcessAttributes(Channel, Channel->CellFlags);
if (!NT_SUCCESS(Status)) goto Quickie;
/* Set the current colors one last time */
Color[1] = Channel->CellBackColor;
Color[0] = Channel->CellForeColor;
Status = VTUTF8ChannelAnsiDispatch(Channel,
SacAnsiSetColors,
Color,
sizeof(Color));
if (!NT_SUCCESS(Status)) goto Quickie;
/* Flush all the data out on the wire */
Status = ConMgrFlushData(Channel);
Quickie:
/* We're done, free the temporary buffer */
if (TmpBuffer) SacFreePool(TmpBuffer);
/* Indicate that all new data has been flushed now */
if (NT_SUCCESS(Status))
{
_InterlockedExchange(&Channel->ChannelHasNewOBufferData, 0);
}
/* Return the result */
return Status;
}
NTSTATUS
NTAPI
VTUTF8ChannelOWrite2(IN PSAC_CHANNEL Channel,
IN PWCHAR String,
IN ULONG Size)
{
PSAC_VTUTF8_SCREEN Screen;
ULONG i, EscapeSize, R, C;
PWSTR pwch;
CHECK_PARAMETER1(Channel);
CHECK_PARAMETER2(String);
VTUTF8ChannelAssertCursor(Channel);
/* Loop every character */
Screen = (PSAC_VTUTF8_SCREEN)Channel->OBuffer;
for (i = 0; i < Size; i++)
{
/* Check what the character is */
pwch = &String[i];
switch (*pwch)
{
/* It's an escape sequence... */
case L'\x1B':
/* Send it to the parser, see how long the sequence was */
EscapeSize = VTUTF8ChannelConsumeEscapeSequence(Channel, pwch);
if (EscapeSize)
{
/* Consume that many characters for next time*/
i += EscapeSize - 1;
}
else
{
/* Invalid escape sequence, skip just the ESC character */
i++;
}
/* Keep going*/
break;
/* It's a line feed */
case L'\n':
/* Simply reset the column to zero on the current line */
Channel->CursorCol = 0;
break;
/* It's a carriage feed */
case L'\r':
/* Move to the next row */
Channel->CursorRow++;
/* Check if we hit the last row on the screen */
if (Channel->CursorRow >= SAC_VTUTF8_ROW_HEIGHT)
{
/* Go over every row before the last one */
for (R = 0; R < (SAC_VTUTF8_ROW_HEIGHT - 1); R++)
{
/* Sanity check, since we always copy one row below */
ASSERT((R + 1) < SAC_VTUTF8_ROW_HEIGHT);
/* Loop every character on the row */
for (C = 0; C < SAC_VTUTF8_COL_WIDTH; C++)
{
/* And replace it with one from the row below */
Screen->Cell[R][C] = Screen->Cell[R + 1][C];
}
}
/* Now we're left with the before-last row, zero it out */
ASSERT(R == (SAC_VTUTF8_ROW_HEIGHT - 1));
RtlZeroMemory(&Screen->Cell[R], sizeof(Screen->Cell[R]));
/* Reset the row back by one */
Channel->CursorRow--;
VTUTF8ChannelAssertCursor(Channel);
}
break;
/* It's a TAB character */
case L'\t':
/* Loop the remaining characters until a multiple of 4 */
VTUTF8ChannelAssertCursor(Channel);
for (C = (4 - Channel->CursorCol % 4); C; C--)
{
/* Fill each remaining character with a space */
VTUTF8ChannelAssertCursor(Channel);
Screen->Cell[Channel->CursorRow][Channel->CursorCol].CellFlags = Channel->CellFlags;
Screen->Cell[Channel->CursorRow][Channel->CursorCol].CellBackColor = Channel->CellBackColor;
Screen->Cell[Channel->CursorRow][Channel->CursorCol].CellForeColor = Channel->CellForeColor;
Screen->Cell[Channel->CursorRow][Channel->CursorCol].Char = L' ';
/* Move to the next character position, but don't overflow */
Channel->CursorCol++;
if (Channel->CursorCol >= SAC_VTUTF8_COL_WIDTH)
{
Channel->CursorCol = SAC_VTUTF8_COL_WIDTH - 1;
}
}
/* All done, move to the next one */
VTUTF8ChannelAssertCursor(Channel);
break;
/* It's a backspace or delete character */
case L'\b':
case L'\x7F':
/* Move back one character, unless we had nothing typed */
if (Channel->CursorCol) Channel->CursorCol--;
VTUTF8ChannelAssertCursor(Channel);
break;
/* It's some other character */
default:
/* Is it non-printable? Ignore it and keep parsing */
if (*pwch < L' ') continue;
/* Otherwise, print it out with the current attributes */
VTUTF8ChannelAssertCursor(Channel);
Screen->Cell[Channel->CursorRow][Channel->CursorCol].CellFlags = Channel->CellFlags;
Screen->Cell[Channel->CursorRow][Channel->CursorCol].CellBackColor = Channel->CellBackColor;
Screen->Cell[Channel->CursorRow][Channel->CursorCol].CellForeColor = Channel->CellForeColor;
Screen->Cell[Channel->CursorRow][Channel->CursorCol].Char = *pwch;
/* Move forward one character, but make sure not to overflow */
Channel->CursorCol++;
if (Channel->CursorCol == SAC_VTUTF8_COL_WIDTH)
{
Channel->CursorCol = SAC_VTUTF8_COL_WIDTH - 1;
}
/* All done, move to the next one */
VTUTF8ChannelAssertCursor(Channel);
break;
}
}
/* Parsing of the input string completed -- string was written */
VTUTF8ChannelAssertCursor(Channel);
return STATUS_SUCCESS;
}
NTSTATUS
NTAPI
VTUTF8ChannelOEcho(IN PSAC_CHANNEL Channel,
IN PCHAR String,
IN ULONG Size)
{
NTSTATUS Status = STATUS_SUCCESS;
PWSTR pwch;
ULONG i, k, TranslatedCount, UTF8TranslationSize;
BOOLEAN Result;
CHECK_PARAMETER1(Channel);
CHECK_PARAMETER2(String);
/* Return success if there's nothing to echo */
if (!(Size / sizeof(WCHAR))) return Status;
/* Start with the input string */
pwch = (PWCHAR)String;
/* First, figure out how much is outside of the block length alignment */
k = (Size / sizeof(WCHAR)) % MAX_UTF8_ENCODE_BLOCK_LENGTH;
if (k)
{
/* Translate the misaligned portion */
Result = SacTranslateUnicodeToUtf8(pwch,
k,
Utf8ConversionBuffer,
Utf8ConversionBufferSize,
&UTF8TranslationSize,
&TranslatedCount);
ASSERT(k == TranslatedCount);
if (!Result)
{
/* If we couldn't translate, write failure to break out below */
Status = STATUS_UNSUCCESSFUL;
}
else
{
/* Write the misaligned portion into the buffer */
Status = ConMgrWriteData(Channel,
Utf8ConversionBuffer,
UTF8TranslationSize);
}
/* If translation or write failed, bail out */
if (!NT_SUCCESS(Status)) goto Return;
}
/* Push the string to its new location (this could be a noop if aligned) */
pwch += k;
/* Now figure out how many aligned blocks we have, and loop each one */
k = (Size / sizeof(WCHAR)) / MAX_UTF8_ENCODE_BLOCK_LENGTH;
for (i = 0; i < k; i++)
{
/* Translate the aligned block */
Result = SacTranslateUnicodeToUtf8(pwch,
MAX_UTF8_ENCODE_BLOCK_LENGTH,
Utf8ConversionBuffer,
Utf8ConversionBufferSize,
&UTF8TranslationSize,
&TranslatedCount);
ASSERT(MAX_UTF8_ENCODE_BLOCK_LENGTH == TranslatedCount);
ASSERT(UTF8TranslationSize > 0);
if (!Result)
{
/* Set failure here, we'll break out below */
Status = STATUS_UNSUCCESSFUL;
}
else
{
/* Move the string location to the next aligned block */
pwch += MAX_UTF8_ENCODE_BLOCK_LENGTH;
/* Write the aligned block into the buffer */
Status = ConMgrWriteData(Channel,
Utf8ConversionBuffer,
UTF8TranslationSize);
}
/* If translation or write failed, bail out */
if (!NT_SUCCESS(Status)) break;
}
Return:
ASSERT(pwch == (PWSTR)(String + Size));
if (NT_SUCCESS(Status)) Status = ConMgrFlushData(Channel);
return Status;
}
NTSTATUS
NTAPI
VTUTF8ChannelOWrite(IN PSAC_CHANNEL Channel,
IN PCHAR String,
IN ULONG Length)
{
NTSTATUS Status;
CHECK_PARAMETER1(Channel);
CHECK_PARAMETER2(String);
/* Call the lower level function */
Status = VTUTF8ChannelOWrite2(Channel, (PWCHAR)String, Length / sizeof(WCHAR));
if (NT_SUCCESS(Status))
{
/* Is the channel enabled for output? */
if ((ConMgrIsWriteEnabled(Channel)) && (Channel->WriteEnabled))
{
/* Go ahead and output it */
Status = VTUTF8ChannelOEcho(Channel, String, Length);
}
else
{
/* Otherwise, just remember that we have new data */
_InterlockedExchange(&Channel->ChannelHasNewOBufferData, 1);
}
}
/* We're done */
return Status;
}
ULONG
NTAPI
VTUTF8ChannelGetIBufferIndex(IN PSAC_CHANNEL Channel)
{
ASSERT(Channel);
ASSERT((Channel->IBufferIndex % sizeof(WCHAR)) == 0);
ASSERT(Channel->IBufferIndex < SAC_VTUTF8_IBUFFER_SIZE);
/* Return the current buffer index */
return Channel->IBufferIndex;
}
VOID
NTAPI
VTUTF8ChannelSetIBufferIndex(IN PSAC_CHANNEL Channel,
IN ULONG BufferIndex)
{
NTSTATUS Status;
ASSERT(Channel);
ASSERT((Channel->IBufferIndex % sizeof(WCHAR)) == 0);
ASSERT(Channel->IBufferIndex < SAC_VTUTF8_IBUFFER_SIZE);
/* Set the new index, and if it's not zero, it means we have data */
Channel->IBufferIndex = BufferIndex;
_InterlockedExchange(&Channel->ChannelHasNewIBufferData, BufferIndex != 0);
/* If we have new data, and an event has been registered... */
if (!(Channel->IBufferIndex) &&
(Channel->Flags & SAC_CHANNEL_FLAG_HAS_NEW_DATA_EVENT))
{
/* Go ahead and signal it */
ChannelClearEvent(Channel, HasNewDataEvent);
UNREFERENCED_PARAMETER(Status);
}
}
NTSTATUS
NTAPI
VTUTF8ChannelIRead(IN PSAC_CHANNEL Channel,
IN PCHAR Buffer,
IN ULONG BufferSize,
IN PULONG ReturnBufferSize)
{
ULONG CopyChars, ReadLength;
CHECK_PARAMETER1(Channel);
CHECK_PARAMETER2(Buffer);
CHECK_PARAMETER_WITH_STATUS(BufferSize > 0, STATUS_INVALID_BUFFER_SIZE);
/* Assume failure */
*ReturnBufferSize = 0;
/* Check how many bytes are in the buffer */
if (Channel->ChannelInputBufferLength(Channel) == 0)
{
/* Apparently nothing. Make sure the flag indicates so too */
ASSERT(ChannelHasNewIBufferData(Channel) == FALSE);
}
else
{
/* Use the smallest number of bytes either in the buffer or requested */
ReadLength = min(Channel->ChannelInputBufferLength(Channel) * sizeof(WCHAR),
BufferSize);
/* Do some cheezy buffer alignment */
CopyChars = ReadLength / sizeof(WCHAR);
ReadLength = CopyChars * sizeof(WCHAR);
ASSERT(CopyChars <= Channel->ChannelInputBufferLength(Channel));
/* Copy them into the caller's buffer */
RtlCopyMemory(Buffer, Channel->IBuffer, ReadLength);
/* Update the channel's index past the copied (read) bytes */
VTUTF8ChannelSetIBufferIndex(Channel,
VTUTF8ChannelGetIBufferIndex(Channel) - ReadLength);
/* Are there still bytes that haven't been read yet? */
if (Channel->ChannelInputBufferLength(Channel))
{
/* Shift them up in the buffer */
RtlMoveMemory(Channel->IBuffer,
&Channel->IBuffer[ReadLength],
Channel->ChannelInputBufferLength(Channel) *
sizeof(WCHAR));
}
/* Return the number of bytes we actually copied */
*ReturnBufferSize = ReadLength;
}
/* Return success */
return STATUS_SUCCESS;
}
NTSTATUS
NTAPI
VTUTF8ChannelIBufferIsFull(IN PSAC_CHANNEL Channel,
OUT PBOOLEAN BufferStatus)
{
CHECK_PARAMETER1(Channel);
/* If the index is beyond the length, the buffer must be full */
*BufferStatus = VTUTF8ChannelGetIBufferIndex(Channel) > SAC_VTUTF8_IBUFFER_SIZE;
return STATUS_SUCCESS;
}
ULONG
NTAPI
VTUTF8ChannelIBufferLength(IN PSAC_CHANNEL Channel)
{
ASSERT(Channel);
/* The index is the length, so divide by two to get character count */
return VTUTF8ChannelGetIBufferIndex(Channel) / sizeof(WCHAR);
}
WCHAR
NTAPI
VTUTF8ChannelIReadLast(IN PSAC_CHANNEL Channel)
{
PWCHAR LastCharLocation;
WCHAR LastChar = 0;
ASSERT(Channel);
/* Check if there's anything to read in the buffer */
if (Channel->ChannelInputBufferLength(Channel))
{
/* Go back one character */
VTUTF8ChannelSetIBufferIndex(Channel,
VTUTF8ChannelGetIBufferIndex(Channel) -
sizeof(WCHAR));
/* Read it, and clear its current value */
LastCharLocation = (PWCHAR)&Channel->IBuffer[VTUTF8ChannelGetIBufferIndex(Channel)];
LastChar = *LastCharLocation;
*LastCharLocation = UNICODE_NULL;
}
/* Return the last character */
return LastChar;
}
NTSTATUS
NTAPI
VTUTF8ChannelIWrite(IN PSAC_CHANNEL Channel,
IN PCHAR Buffer,
IN ULONG BufferSize)
{
NTSTATUS Status;
BOOLEAN IsFull;
ULONG Index, i;
CHECK_PARAMETER1(Channel);
CHECK_PARAMETER2(Buffer);
CHECK_PARAMETER_WITH_STATUS(BufferSize > 0, STATUS_INVALID_BUFFER_SIZE);
/* First, check if the input buffer still has space */
Status = VTUTF8ChannelIBufferIsFull(Channel, &IsFull);
if (!NT_SUCCESS(Status)) return Status;
if (IsFull) return STATUS_UNSUCCESSFUL;
/* Get the current buffer index */
Index = VTUTF8ChannelGetIBufferIndex(Channel);
if ((SAC_VTUTF8_IBUFFER_SIZE - Index) < BufferSize)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
/* Copy the new data */
for (i = 0; i < BufferSize; i++)
{
/* Convert the character */
if (SacTranslateUtf8ToUnicode(Buffer[i],
IncomingUtf8ConversionBuffer,
&IncomingUnicodeValue))
{
/* Write it into the buffer */
*(PWCHAR)&Channel->IBuffer[VTUTF8ChannelGetIBufferIndex(Channel)] =
IncomingUnicodeValue;
/* Update the index */
Index = VTUTF8ChannelGetIBufferIndex(Channel);
VTUTF8ChannelSetIBufferIndex(Channel, Index + sizeof(WCHAR));
}
}
/* Signal the event, if one was set */
if (Channel->Flags & SAC_CHANNEL_FLAG_HAS_NEW_DATA_EVENT)
{
ChannelSetEvent(Channel, HasNewDataEvent);
}
/* All done */
return STATUS_SUCCESS;
}