You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1119 lines
28 KiB
1119 lines
28 KiB
/*++
|
|
|
|
Copyright (c) 1993 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
spterm.c
|
|
|
|
Abstract:
|
|
|
|
Text setup support for terminals
|
|
|
|
Author:
|
|
|
|
Sean Selitrennikoff (v-seans) 25-May-1999
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
|
|
|
|
#include "spprecmp.h"
|
|
#include "ntddser.h"
|
|
#pragma hdrstop
|
|
#include <hdlsblk.h>
|
|
#include <hdlsterm.h>
|
|
|
|
#define MS_DSRCTSCD 0xB0 // Status bits for DSR, CTS and CD
|
|
|
|
|
|
|
|
BOOLEAN HeadlessTerminalConnected = FALSE;
|
|
UCHAR Utf8ConversionBuffer[80*3+1];
|
|
PUCHAR TerminalBuffer = Utf8ConversionBuffer;
|
|
WCHAR UnicodeScratchBuffer[80+1];
|
|
|
|
//
|
|
// Use these variables to decode incoming UTF8
|
|
// data streams.
|
|
//
|
|
WCHAR IncomingUnicodeValue;
|
|
UCHAR IncomingUtf8ConversionBuffer[3];
|
|
|
|
|
|
//
|
|
// Determine if we will do UTF8 encoding before we send the string off
|
|
// to the headless terminal.
|
|
//
|
|
BOOLEAN SpTermDoUtf8 = FALSE;
|
|
|
|
|
|
//
|
|
// Reverse lookup table for the odd-ball unicode line drawing
|
|
// characters they use for JPN builds. If we detect one of these
|
|
// we need to convert it into real unicode before we UTF8
|
|
// encode it.
|
|
//
|
|
|
|
typedef struct _UNICODE_CROSS_REFERENCE {
|
|
|
|
//
|
|
// Whacky far east unicode value. For example, we
|
|
// might get 0x0006 for a double-horizontal line, which
|
|
// is just nonsense.
|
|
//
|
|
WCHAR FECode;
|
|
|
|
//
|
|
// The corresponding real unicode value.
|
|
//
|
|
WCHAR Unicode;
|
|
} UNICODE_CROSS_REFERENCE;
|
|
|
|
UNICODE_CROSS_REFERENCE FEUnicodeToRealUnicodeValue[LineCharMax] = {
|
|
|
|
0x0001, 0x2554, // DoubleUpperLeft
|
|
0x0002, 0x2557, // DoubleUpperRight
|
|
0x0003, 0x255a, // DoubleLowerLeft
|
|
0x0004, 0x255d, // DoubleLowerRight
|
|
0x0006, 0x2550, // DoubleHorizontal
|
|
0x0005, 0x2551, // DoubleVertical
|
|
0x0001, 0x250c, // SingleUpperLeft
|
|
0x0002, 0x2510, // SingleUpperRight
|
|
0x0003, 0x2514, // SingleLowerLeft
|
|
0x0004, 0x2518, // SingleLowerRight
|
|
0x0006, 0x2500, // SingleHorizontal
|
|
0x0005, 0x2502, // SingleVertical
|
|
0x0019, 0x255f, // DoubleVerticalToSingleHorizontalRight,
|
|
0x0017, 0x2562 // DoubleVerticalToSingleHorizontalLeft
|
|
};
|
|
|
|
BOOLEAN
|
|
SpTranslateUnicodeToUtf8(
|
|
PCWSTR SourceBuffer,
|
|
UCHAR *DestinationBuffer
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
translates a unicode buffer into a UTF8 version.
|
|
|
|
Arguments:
|
|
|
|
SourceBuffer - unicode buffer to be translated.
|
|
DestinationBuffer - receives UTF8 version of same buffer.
|
|
|
|
Return Value:
|
|
|
|
TRUE - We successfully translated the Unicode value into its
|
|
corresponding UTF8 encoding.
|
|
|
|
FALSE - The translation failed.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG Count = 0;
|
|
ULONG i = 0;
|
|
WCHAR CurrentChar = 0;
|
|
//
|
|
// convert into UTF8 for actual transmission
|
|
//
|
|
// UTF-8 encodes 2-byte Unicode characters as follows:
|
|
// If the first nine bits are zero (00000000 0xxxxxxx), encode it as one byte 0xxxxxxx
|
|
// If the first five bits are zero (00000yyy yyxxxxxx), encode it as two bytes 110yyyyy 10xxxxxx
|
|
// Otherwise (zzzzyyyy yyxxxxxx), encode it as three bytes 1110zzzz 10yyyyyy 10xxxxxx
|
|
//
|
|
DestinationBuffer[Count] = (UCHAR)'\0';
|
|
while (*SourceBuffer) {
|
|
|
|
CurrentChar = *SourceBuffer;
|
|
|
|
if( CurrentChar < 0x0020 ) {
|
|
//
|
|
// See if we need to convert this FarEast whacky unicode value into a real
|
|
// unicode encoding.
|
|
//
|
|
for (i = 0; i < LineCharMax; i++) {
|
|
if( FEUnicodeToRealUnicodeValue[i].FECode == CurrentChar ) {
|
|
CurrentChar = FEUnicodeToRealUnicodeValue[i].Unicode;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if( (CurrentChar & 0xFF80) == 0 ) {
|
|
//
|
|
// if the top 9 bits are zero, then just
|
|
// encode as 1 byte. (ASCII passes through unchanged).
|
|
//
|
|
DestinationBuffer[Count++] = (UCHAR)(CurrentChar & 0x7F);
|
|
} else if( (CurrentChar & 0xF800) == 0 ) {
|
|
//
|
|
// if the top 5 bits are zero, then encode as 2 bytes
|
|
//
|
|
DestinationBuffer[Count++] = (UCHAR)((CurrentChar >> 6) & 0x1F) | 0xC0;
|
|
DestinationBuffer[Count++] = (UCHAR)(CurrentChar & 0xBF) | 0x80;
|
|
} else {
|
|
//
|
|
// encode as 3 bytes
|
|
//
|
|
DestinationBuffer[Count++] = (UCHAR)((CurrentChar >> 12) & 0xF) | 0xE0;
|
|
DestinationBuffer[Count++] = (UCHAR)((CurrentChar >> 6) & 0x3F) | 0x80;
|
|
DestinationBuffer[Count++] = (UCHAR)(CurrentChar & 0xBF) | 0x80;
|
|
}
|
|
SourceBuffer += 1;
|
|
}
|
|
|
|
DestinationBuffer[Count] = (UCHAR)'\0';
|
|
|
|
return(TRUE);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BOOLEAN
|
|
SpTranslateUtf8ToUnicode(
|
|
UCHAR IncomingByte,
|
|
UCHAR *ExistingUtf8Buffer,
|
|
WCHAR *DestinationUnicodeVal
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Takes IncomingByte and concatenates it onto ExistingUtf8Buffer.
|
|
Then attempts to decode the new contents of ExistingUtf8Buffer.
|
|
|
|
Arguments:
|
|
|
|
IncomingByte - New character to be appended onto
|
|
ExistingUtf8Buffer.
|
|
|
|
|
|
ExistingUtf8Buffer - running buffer containing incomplete UTF8
|
|
encoded unicode value. When it gets full,
|
|
we'll decode the value and return the
|
|
corresponding Unicode value.
|
|
|
|
Note that if we *do* detect a completed UTF8
|
|
buffer and actually do a decode and return a
|
|
Unicode value, then we will zero-fill the
|
|
contents of ExistingUtf8Buffer.
|
|
|
|
|
|
DestinationUnicodeVal - receives Unicode version of the UTF8 buffer.
|
|
|
|
Note that if we do *not* detect a completed
|
|
UTF8 buffer and thus can not return any data
|
|
in DestinationUnicodeValue, then we will
|
|
zero-fill the contents of DestinationUnicodeVal.
|
|
|
|
|
|
Return Value:
|
|
|
|
TRUE - We received a terminating character for our UTF8 buffer and will
|
|
return a decoded Unicode value in DestinationUnicode.
|
|
|
|
FALSE - We haven't yet received a terminating character for our UTF8
|
|
buffer.
|
|
|
|
--*/
|
|
|
|
{
|
|
// ULONG Count = 0;
|
|
ULONG i = 0;
|
|
BOOLEAN ReturnValue = FALSE;
|
|
|
|
|
|
|
|
//
|
|
// Insert our byte into ExistingUtf8Buffer.
|
|
//
|
|
i = 0;
|
|
do {
|
|
if( ExistingUtf8Buffer[i] == 0 ) {
|
|
ExistingUtf8Buffer[i] = IncomingByte;
|
|
break;
|
|
}
|
|
|
|
i++;
|
|
} while( i < 3 );
|
|
|
|
//
|
|
// If we didn't get to actually insert our IncomingByte,
|
|
// then someone sent us a fully-qualified UTF8 buffer.
|
|
// This means we're about to drop IncomingByte.
|
|
//
|
|
// Drop the zero-th byte, shift everything over by one
|
|
// and insert our new character.
|
|
//
|
|
// This implies that we should *never* need to zero out
|
|
// the contents of ExistingUtf8Buffer unless we detect
|
|
// a completed UTF8 packet. Otherwise, assume one of
|
|
// these cases:
|
|
// 1. We started listening mid-stream, so we caught the
|
|
// last half of a UTF8 packet. In this case, we'll
|
|
// end up shifting the contents of ExistingUtf8Buffer
|
|
// until we detect a proper UTF8 start byte in the zero-th
|
|
// position.
|
|
// 2. We got some garbage character, which would invalidate
|
|
// a UTF8 packet. By using the logic below, we would
|
|
// end up disregarding that packet and waiting for
|
|
// the next UTF8 packet to come in.
|
|
if( i >= 3 ) {
|
|
ExistingUtf8Buffer[0] = ExistingUtf8Buffer[1];
|
|
ExistingUtf8Buffer[1] = ExistingUtf8Buffer[2];
|
|
ExistingUtf8Buffer[2] = IncomingByte;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
// Attempt to convert the UTF8 buffer
|
|
//
|
|
// UTF8 decodes to Unicode in the following fashion:
|
|
// If the high-order bit is 0 in the first byte:
|
|
// 0xxxxxxx yyyyyyyy zzzzzzzz decodes to a Unicode value of 00000000 0xxxxxxx
|
|
//
|
|
// If the high-order 3 bits in the first byte == 6:
|
|
// 110xxxxx 10yyyyyy zzzzzzzz decodes to a Unicode value of 00000xxx xxyyyyyy
|
|
//
|
|
// If the high-order 3 bits in the first byte == 7:
|
|
// 1110xxxx 10yyyyyy 10zzzzzz decodes to a Unicode value of xxxxyyyy yyzzzzzz
|
|
//
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: SpTranslateUtf8ToUnicode - About to decode the UTF8 buffer.\n" ));
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, " UTF8[0]: 0x%02lx UTF8[1]: 0x%02lx UTF8[2]: 0x%02lx\n",
|
|
ExistingUtf8Buffer[0],
|
|
ExistingUtf8Buffer[1],
|
|
ExistingUtf8Buffer[2] ));
|
|
|
|
if( (ExistingUtf8Buffer[0] & 0x80) == 0 ) {
|
|
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: SpTranslateUtf8ToUnicode - Case1\n" ));
|
|
|
|
//
|
|
// First case described above. Just return the first byte
|
|
// of our UTF8 buffer.
|
|
//
|
|
*DestinationUnicodeVal = (WCHAR)(ExistingUtf8Buffer[0]);
|
|
|
|
|
|
//
|
|
// We used 1 byte. Discard that byte and shift everything
|
|
// in our buffer over by 1.
|
|
//
|
|
ExistingUtf8Buffer[0] = ExistingUtf8Buffer[1];
|
|
ExistingUtf8Buffer[1] = ExistingUtf8Buffer[2];
|
|
ExistingUtf8Buffer[2] = 0;
|
|
|
|
ReturnValue = TRUE;
|
|
|
|
} else if( (ExistingUtf8Buffer[0] & 0xE0) == 0xC0 ) {
|
|
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: SpTranslateUtf8ToUnicode - 1st byte of UTF8 buffer says Case2\n" ));
|
|
|
|
//
|
|
// Second case described above. Decode the first 2 bytes of
|
|
// of our UTF8 buffer.
|
|
//
|
|
if( (ExistingUtf8Buffer[1] & 0xC0) == 0x80 ) {
|
|
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: SpTranslateUtf8ToUnicode - 2nd byte of UTF8 buffer says Case2.\n" ));
|
|
|
|
// upper byte: 00000xxx
|
|
*DestinationUnicodeVal = ((ExistingUtf8Buffer[0] >> 2) & 0x07);
|
|
*DestinationUnicodeVal = *DestinationUnicodeVal << 8;
|
|
|
|
// high bits of lower byte: xx000000
|
|
*DestinationUnicodeVal |= ((ExistingUtf8Buffer[0] & 0x03) << 6);
|
|
|
|
// low bits of lower byte: 00yyyyyy
|
|
*DestinationUnicodeVal |= (ExistingUtf8Buffer[1] & 0x3F);
|
|
|
|
|
|
//
|
|
// We used 2 bytes. Discard those bytes and shift everything
|
|
// in our buffer over by 2.
|
|
//
|
|
ExistingUtf8Buffer[0] = ExistingUtf8Buffer[2];
|
|
ExistingUtf8Buffer[1] = 0;
|
|
ExistingUtf8Buffer[2] = 0;
|
|
|
|
ReturnValue = TRUE;
|
|
|
|
}
|
|
} else if( (ExistingUtf8Buffer[0] & 0xF0) == 0xE0 ) {
|
|
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: SpTranslateUtf8ToUnicode - 1st byte of UTF8 buffer says Case3\n" ));
|
|
|
|
//
|
|
// Third case described above. Decode the all 3 bytes of
|
|
// of our UTF8 buffer.
|
|
//
|
|
|
|
if( (ExistingUtf8Buffer[1] & 0xC0) == 0x80 ) {
|
|
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: SpTranslateUtf8ToUnicode - 2nd byte of UTF8 buffer says Case3\n" ));
|
|
|
|
if( (ExistingUtf8Buffer[2] & 0xC0) == 0x80 ) {
|
|
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: SpTranslateUtf8ToUnicode - 3rd byte of UTF8 buffer says Case3\n" ));
|
|
|
|
// upper byte: xxxx0000
|
|
*DestinationUnicodeVal = ((ExistingUtf8Buffer[0] << 4) & 0xF0);
|
|
|
|
// upper byte: 0000yyyy
|
|
*DestinationUnicodeVal |= ((ExistingUtf8Buffer[1] >> 2) & 0x0F);
|
|
|
|
*DestinationUnicodeVal = *DestinationUnicodeVal << 8;
|
|
|
|
// lower byte: yy000000
|
|
*DestinationUnicodeVal |= ((ExistingUtf8Buffer[1] << 6) & 0xC0);
|
|
|
|
// lower byte: 00zzzzzz
|
|
*DestinationUnicodeVal |= (ExistingUtf8Buffer[2] & 0x3F);
|
|
|
|
//
|
|
// We used all 3 bytes. Zero out the buffer.
|
|
//
|
|
ExistingUtf8Buffer[0] = 0;
|
|
ExistingUtf8Buffer[1] = 0;
|
|
ExistingUtf8Buffer[2] = 0;
|
|
|
|
ReturnValue = TRUE;
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
return ReturnValue;
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
SpTermInitialize(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Attempts to connect to a VT100 attached to COM1
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
HEADLESS_CMD_ENABLE_TERMINAL Command;
|
|
NTSTATUS Status;
|
|
|
|
Command.Enable = TRUE;
|
|
Status = HeadlessDispatch(HeadlessCmdEnableTerminal,
|
|
&Command,
|
|
sizeof(HEADLESS_CMD_ENABLE_TERMINAL),
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
HeadlessTerminalConnected = NT_SUCCESS(Status);
|
|
}
|
|
|
|
VOID
|
|
SpTermDisplayStringOnTerminal(
|
|
IN PWSTR String,
|
|
IN UCHAR Attribute,
|
|
IN ULONG X, // 0-based coordinates (character units)
|
|
IN ULONG Y
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Write a string of characters to the terminal.
|
|
|
|
Arguments:
|
|
|
|
Character - supplies a string to be displayed at the given position.
|
|
|
|
Attribute - supplies the attributes for the characters in the string.
|
|
|
|
X,Y - specify the character-based (0-based) position of the output.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PWSTR EscapeString;
|
|
|
|
//
|
|
// send <CSI>x;yH to move the cursor to the specified location
|
|
//
|
|
swprintf(UnicodeScratchBuffer, L"\033[%d;%dH", Y + 1, X + 1);
|
|
SpTermSendStringToTerminal(UnicodeScratchBuffer, TRUE);
|
|
|
|
//
|
|
// convert any attributes to an escape string. EscapeString uses
|
|
// the TerminalBuffer global scratch buffer
|
|
//
|
|
EscapeString = SpTermAttributeToTerminalEscapeString(Attribute);
|
|
|
|
//
|
|
// transmit the escape string if we received one
|
|
//
|
|
if (EscapeString != NULL) {
|
|
SpTermSendStringToTerminal(EscapeString, TRUE);
|
|
}
|
|
|
|
//
|
|
// finally send the actual string contents to the terminal
|
|
//
|
|
SpTermSendStringToTerminal(String, FALSE);
|
|
}
|
|
|
|
PWSTR
|
|
SpTermAttributeToTerminalEscapeString(
|
|
IN UCHAR Attribute
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Convert a vga attribute byte to an escape sequence to send to the terminal.
|
|
|
|
Arguments:
|
|
|
|
Attribute - supplies the attribute.
|
|
|
|
Return Value:
|
|
|
|
A pointer to the escape sequence, or NULL if it could not be converted.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG BgColor;
|
|
ULONG FgColor;
|
|
BOOLEAN Inverse;
|
|
|
|
BgColor = (Attribute & 0x70) >> 4;
|
|
FgColor = Attribute & 0x07;
|
|
|
|
Inverse = !((BgColor == 0) || (BgColor == DEFAULT_BACKGROUND));
|
|
|
|
//
|
|
// Convert the colors.
|
|
//
|
|
switch (BgColor) {
|
|
case ATT_BLUE:
|
|
BgColor = 44;
|
|
break;
|
|
case ATT_GREEN:
|
|
BgColor = 42;
|
|
break;
|
|
case ATT_CYAN:
|
|
BgColor = 46;
|
|
break;
|
|
case ATT_RED:
|
|
BgColor = 41;
|
|
break;
|
|
case ATT_MAGENTA:
|
|
BgColor = 45;
|
|
break;
|
|
case ATT_YELLOW:
|
|
BgColor = 43;
|
|
break;
|
|
case ATT_BLACK:
|
|
BgColor = 40;
|
|
break;
|
|
case ATT_WHITE:
|
|
BgColor = 47;
|
|
break;
|
|
}
|
|
switch (FgColor) {
|
|
case ATT_BLUE:
|
|
FgColor = 34;
|
|
break;
|
|
case ATT_GREEN:
|
|
FgColor = 32;
|
|
break;
|
|
case ATT_CYAN:
|
|
FgColor = 36;
|
|
break;
|
|
case ATT_RED:
|
|
FgColor = 31;
|
|
break;
|
|
case ATT_MAGENTA:
|
|
FgColor = 35;
|
|
break;
|
|
case ATT_YELLOW:
|
|
FgColor = 33;
|
|
break;
|
|
case ATT_BLACK:
|
|
FgColor = 30;
|
|
break;
|
|
case ATT_WHITE:
|
|
FgColor = 37;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// <CSI>%1;%2;%3m is the escape to set a color
|
|
// where 1 = video mode
|
|
// 2 = foreground color
|
|
// 3 = background color
|
|
//
|
|
swprintf(UnicodeScratchBuffer,
|
|
L"\033[%u;%u;%um",
|
|
(Inverse ? 7 : 0),
|
|
FgColor,
|
|
BgColor
|
|
);
|
|
|
|
return UnicodeScratchBuffer;
|
|
}
|
|
|
|
VOID
|
|
SpTermSendStringToTerminal(
|
|
IN PWSTR String,
|
|
IN BOOLEAN Raw
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Write a character string to the terminal, translating some codes if desired.
|
|
|
|
Arguments:
|
|
|
|
String - NULL terminated string to write.
|
|
|
|
Raw - Send the string raw or not.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
|
|
{
|
|
ULONG i = 0;
|
|
PWSTR LocalBuffer = UnicodeScratchBuffer;
|
|
|
|
|
|
ASSERT(FIELD_OFFSET(HEADLESS_CMD_PUT_STRING, String) == 0); // ASSERT if anyone changes this structure.
|
|
|
|
//
|
|
// Don't do anything if we aren't running headless.
|
|
//
|
|
if( !HeadlessTerminalConnected ) {
|
|
return;
|
|
}
|
|
|
|
if (Raw) {
|
|
|
|
if (SpTermDoUtf8) {
|
|
SpTranslateUnicodeToUtf8( String, Utf8ConversionBuffer );
|
|
|
|
HeadlessDispatch( HeadlessCmdPutData,
|
|
Utf8ConversionBuffer,
|
|
strlen(Utf8ConversionBuffer),
|
|
NULL,
|
|
NULL
|
|
);
|
|
} else {
|
|
//
|
|
// Convert unicode string to oem, guarding against overflow.
|
|
//
|
|
RtlUnicodeToOemN(
|
|
Utf8ConversionBuffer,
|
|
sizeof(Utf8ConversionBuffer)-1, // guarantee room for nul
|
|
NULL,
|
|
String,
|
|
(wcslen(String)+1)*sizeof(WCHAR)
|
|
);
|
|
|
|
Utf8ConversionBuffer[sizeof(Utf8ConversionBuffer)-1] = '\0';
|
|
|
|
HeadlessDispatch( HeadlessCmdPutString,
|
|
Utf8ConversionBuffer,
|
|
strlen(Utf8ConversionBuffer) + sizeof(UCHAR),
|
|
NULL,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
|
|
|
|
|
|
return;
|
|
}
|
|
|
|
while (*String != L'\0') {
|
|
|
|
LocalBuffer[i++] = *String;
|
|
|
|
if (*String == L'\n') {
|
|
|
|
//
|
|
// Every \n becomes a \n\r sequence.
|
|
//
|
|
LocalBuffer[i++] = L'\r';
|
|
|
|
} else if (*String == 0x00DC) {
|
|
|
|
//
|
|
// The cursor becomes a space and then a backspace, this is to
|
|
// delete the old character and position the terminal cursor properly.
|
|
//
|
|
LocalBuffer[i-1] = 0x0020;
|
|
LocalBuffer[i++] = 0x0008;
|
|
|
|
}
|
|
|
|
//
|
|
// we've got an entire line of text -- we need to transmit it now or
|
|
// we can end up scrolling the text and everything will look funny from
|
|
// this point forward.
|
|
//
|
|
if (i >= 70) {
|
|
|
|
LocalBuffer[i] = L'\0';
|
|
if (SpTermDoUtf8) {
|
|
SpTranslateUnicodeToUtf8( LocalBuffer, Utf8ConversionBuffer );
|
|
|
|
HeadlessDispatch(HeadlessCmdPutData,
|
|
Utf8ConversionBuffer,
|
|
strlen(Utf8ConversionBuffer),
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
|
|
} else {
|
|
//
|
|
// Convert unicode string to oem, guarding against overflow.
|
|
//
|
|
RtlUnicodeToOemN(
|
|
Utf8ConversionBuffer,
|
|
sizeof(Utf8ConversionBuffer)-1, // guarantee room for nul
|
|
NULL,
|
|
LocalBuffer,
|
|
(wcslen(LocalBuffer)+1)*sizeof(WCHAR)
|
|
);
|
|
|
|
|
|
Utf8ConversionBuffer[sizeof(Utf8ConversionBuffer)-1] = '\0';
|
|
|
|
HeadlessDispatch(HeadlessCmdPutString,
|
|
Utf8ConversionBuffer,
|
|
strlen(Utf8ConversionBuffer) + sizeof(UCHAR),
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
}
|
|
|
|
i = 0;
|
|
}
|
|
|
|
String++;
|
|
}
|
|
|
|
LocalBuffer[i] = L'\0';
|
|
if (SpTermDoUtf8) {
|
|
SpTranslateUnicodeToUtf8( LocalBuffer, Utf8ConversionBuffer );
|
|
|
|
HeadlessDispatch(HeadlessCmdPutData,
|
|
Utf8ConversionBuffer,
|
|
strlen(Utf8ConversionBuffer),
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
} else {
|
|
//
|
|
// Convert unicode string to oem, guarding against overflow.
|
|
//
|
|
RtlUnicodeToOemN(
|
|
Utf8ConversionBuffer,
|
|
sizeof(Utf8ConversionBuffer)-1, // guarantee room for nul
|
|
NULL,
|
|
LocalBuffer,
|
|
(wcslen(LocalBuffer)+1)*sizeof(WCHAR)
|
|
);
|
|
|
|
Utf8ConversionBuffer[sizeof(Utf8ConversionBuffer)-1] = '\0';
|
|
|
|
HeadlessDispatch(HeadlessCmdPutString,
|
|
Utf8ConversionBuffer,
|
|
strlen(Utf8ConversionBuffer) + sizeof(UCHAR),
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
VOID
|
|
SpTermTerminate(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Close down connection to the dumb terminal
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
HEADLESS_CMD_ENABLE_TERMINAL Command;
|
|
|
|
//
|
|
// Don't do anything if we aren't running headless.
|
|
//
|
|
if( !HeadlessTerminalConnected ) {
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
Command.Enable = FALSE;
|
|
HeadlessDispatch(HeadlessCmdEnableTerminal,
|
|
&Command,
|
|
sizeof(HEADLESS_CMD_ENABLE_TERMINAL),
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
HeadlessTerminalConnected = FALSE;
|
|
}
|
|
|
|
BOOLEAN
|
|
SpTermIsKeyWaiting(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Probe for a read.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
TRUE if there is a character waiting for input, else FALSE.
|
|
|
|
--*/
|
|
|
|
{
|
|
HEADLESS_RSP_POLL Response;
|
|
NTSTATUS Status;
|
|
SIZE_T Length;
|
|
|
|
|
|
//
|
|
// Don't do anything if we aren't running headless.
|
|
//
|
|
if( !HeadlessTerminalConnected ) {
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
Length = sizeof(HEADLESS_RSP_POLL);
|
|
|
|
Response.QueuedInput = FALSE;
|
|
|
|
Status = HeadlessDispatch(HeadlessCmdTerminalPoll,
|
|
NULL,
|
|
0,
|
|
&Response,
|
|
&Length
|
|
);
|
|
|
|
return (NT_SUCCESS(Status) && Response.QueuedInput);
|
|
}
|
|
|
|
ULONG
|
|
SpTermGetKeypress(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Read in a (possible) sequence of keystrokes and return a Key value.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
0 if no key is waiting, else a ULONG key value.
|
|
|
|
--*/
|
|
|
|
{
|
|
UCHAR Byte;
|
|
BOOLEAN Success;
|
|
TIME_FIELDS StartTime;
|
|
TIME_FIELDS EndTime;
|
|
HEADLESS_RSP_GET_BYTE Response;
|
|
SIZE_T Length;
|
|
NTSTATUS Status;
|
|
|
|
|
|
//
|
|
// Don't do anything if we aren't running headless.
|
|
//
|
|
if( !HeadlessTerminalConnected ) {
|
|
return 0;
|
|
}
|
|
|
|
|
|
//
|
|
// Read first character
|
|
//
|
|
Length = sizeof(HEADLESS_RSP_GET_BYTE);
|
|
|
|
Status = HeadlessDispatch(HeadlessCmdGetByte,
|
|
NULL,
|
|
0,
|
|
&Response,
|
|
&Length
|
|
);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
Byte = Response.Value;
|
|
} else {
|
|
Byte = 0;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Handle all the special escape codes.
|
|
//
|
|
if (Byte == 0x8) { // backspace (^h)
|
|
return ASCI_BS;
|
|
}
|
|
if (Byte == 0x7F) { // delete
|
|
return KEY_DELETE;
|
|
}
|
|
if ((Byte == '\r') || (Byte == '\n')) { // return
|
|
return ASCI_CR;
|
|
}
|
|
|
|
if (Byte == 0x1b) { // Escape key
|
|
|
|
do {
|
|
|
|
Success = HalQueryRealTimeClock(&StartTime);
|
|
ASSERT(Success);
|
|
|
|
//
|
|
// Adjust StartTime to be our ending time.
|
|
//
|
|
StartTime.Second += 2;
|
|
if (StartTime.Second > 59) {
|
|
StartTime.Second -= 60;
|
|
}
|
|
|
|
while (!SpTermIsKeyWaiting()) {
|
|
|
|
//
|
|
// Give the user 1 second to type in a follow up key.
|
|
//
|
|
Success = HalQueryRealTimeClock(&EndTime);
|
|
ASSERT(Success);
|
|
|
|
if (StartTime.Second == EndTime.Second) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!SpTermIsKeyWaiting()) {
|
|
return ASCI_ESC;
|
|
}
|
|
|
|
//
|
|
// Read the next keystroke
|
|
//
|
|
Length = sizeof(HEADLESS_RSP_GET_BYTE);
|
|
|
|
Status = HeadlessDispatch(HeadlessCmdGetByte,
|
|
NULL,
|
|
0,
|
|
&Response,
|
|
&Length
|
|
);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
Byte = Response.Value;
|
|
} else {
|
|
Byte = 0;
|
|
}
|
|
|
|
|
|
//
|
|
// Some terminals send ESC, or ESC-[ to mean
|
|
// they're about to send a control sequence. We've already
|
|
// gotten an ESC key, so ignore an '[' if it comes in.
|
|
//
|
|
} while ( Byte == '[' );
|
|
|
|
|
|
switch (Byte) {
|
|
case '@':
|
|
return KEY_F12;
|
|
case '!':
|
|
return KEY_F11;
|
|
case '0':
|
|
return KEY_F10;
|
|
case '9':
|
|
return KEY_F9;
|
|
case '8':
|
|
return KEY_F8;
|
|
case '7':
|
|
return KEY_F7;
|
|
case '6':
|
|
return KEY_F6;
|
|
case '5':
|
|
return KEY_F5;
|
|
case '4':
|
|
return KEY_F4;
|
|
case '3':
|
|
return KEY_F3;
|
|
case '2':
|
|
return KEY_F2;
|
|
case '1':
|
|
return KEY_F1;
|
|
case '+':
|
|
return KEY_INSERT;
|
|
case '-':
|
|
return KEY_DELETE;
|
|
case 'H':
|
|
return KEY_HOME;
|
|
case 'K':
|
|
return KEY_END;
|
|
case '?':
|
|
return KEY_PAGEUP;
|
|
case '/':
|
|
return KEY_PAGEDOWN;
|
|
case 'A':
|
|
return KEY_UP;
|
|
case 'B':
|
|
return KEY_DOWN;
|
|
case 'C':
|
|
return KEY_RIGHT;
|
|
case 'D':
|
|
return KEY_LEFT;
|
|
|
|
}
|
|
|
|
//
|
|
// We didn't get anything we recognized after the
|
|
// ESC key. Just return the ESC key.
|
|
//
|
|
return ASCI_ESC;
|
|
|
|
} // Escape key
|
|
|
|
|
|
|
|
//
|
|
// The incoming byte isn't an escape code.
|
|
//
|
|
// Decode it as if it's a UTF8 stream.
|
|
//
|
|
if( SpTranslateUtf8ToUnicode( Byte,
|
|
IncomingUtf8ConversionBuffer,
|
|
&IncomingUnicodeValue ) ) {
|
|
|
|
//
|
|
// He returned TRUE, so we must have recieved a complete
|
|
// UTF8-encoded character.
|
|
//
|
|
return IncomingUnicodeValue;
|
|
} else {
|
|
//
|
|
// The UTF8 stream isn't complete yet, so we don't have
|
|
// a decoded character to return yet.
|
|
//
|
|
return 0;
|
|
}
|
|
|
|
}
|
|
|
|
VOID
|
|
SpTermDrain(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Read in and throw out all characters in input stream
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
while (SpTermIsKeyWaiting()) {
|
|
SpTermGetKeypress();
|
|
}
|
|
}
|
|
|
|
|
|
|