summaryrefslogtreecommitdiff
path: root/util/AnsiTerm.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'util/AnsiTerm.cpp')
-rw-r--r--util/AnsiTerm.cpp1892
1 files changed, 1892 insertions, 0 deletions
diff --git a/util/AnsiTerm.cpp b/util/AnsiTerm.cpp
new file mode 100644
index 0000000..be94465
--- /dev/null
+++ b/util/AnsiTerm.cpp
@@ -0,0 +1,1892 @@
+/*
+ * AnsiTerm.cpp
+ * Windows ANSI Terminal Emulation
+ *
+ * Author: Robert Nelson robertnelson at users.sourceforge.net
+ * Copyright (c) 2009 Robert Nelson
+ *
+ * 10/07/09 Robert Nelson - Created
+ * 10/08/09 Robert Nelson
+ * - Fixed bug with resetting attribute to Black on Black on exit
+ * - Fixed setting of attribute when erasing
+ * - Added automatic handling of buffer resize events
+ * - Added display of unrecognized escape sequences
+ * 10/15/09 Robert Nelson
+ * - Fixed display problems caused by custom ColorTable used by cmd.exe
+ * - Fixed cursor positioning problems with OriginMode.
+ * - Changed to use block cursor because I think its more terminal like :-)
+ * - Added Reset handling
+ * - Added Cursor Show / Hide
+ * 10/16/09 Robert Nelson
+ * - Better handling of ColorTable.
+ * 10/17/09 Robert Nelson
+ * - Use GetProcAddress for (Get/Set)ConsoleScreenBufferInfoEx since they
+ * are only available on Vista and beyond.
+ *
+ * Todo:
+ * - Implement soft tabs
+ */
+
+/*
+Copyright (c) 2009, Robert Nelson
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ a.. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ b.. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#include <windows.h>
+#include <stdio.h>
+#include <assert.h>
+#include "AnsiTerm.h"
+
+extern "C" unsigned char fCRLF;
+extern "C" void dbglog( char *pattn, ... );
+
+CAnsiTerm::CSICode CAnsiTerm::s_CSITable[] =
+{
+ { '[', DS_CSIParam },
+ { '#', DS_DECPrivate },
+ { '(', DS_SelectG0 },
+ { ')', DS_SelectG1 },
+ { '=', DS_None, &ProcessDECKPAM }, // Keypad Application Mode
+ { '>', DS_None, &ProcessDECKPNM }, // Keypad Numeric Mode
+ { '7', DS_None, &ProcessDECSC }, // Save Cursor
+ { '8', DS_None, &ProcessDECRC }, // Restore Cursor
+ { 'D', DS_None, &ProcessIND }, // Index
+ { 'E', DS_None, &ProcessNEL }, // Next Line
+ { 'H', DS_None, &ProcessHTS }, // Horizontal Tab Set
+ { 'M', DS_None, &ProcessRI }, // Reverse Index
+ { 'Z', DS_None, &ProcessDECID }, // Identify Terminal
+ { 'c', DS_None, &ProcessRIS }, // Reset to Initial State
+ { 's', DS_None, &ProcessSCP }, // Save Cursor Position
+ { 'u', DS_None, &ProcessRCP }, // Restore Cursor Position
+ { '\0' }
+};
+
+CAnsiTerm::CSIFunction CAnsiTerm::s_DECFunction[] =
+{
+ { '3', &ProcessDECDHLT }, // Double Height Line Top
+ { '4', &ProcessDECDHLB }, // Double Height Line Bottom
+ { '5', &ProcessDECSWL }, // Single Width Line
+ { '6', &ProcessDECDWL }, // Double Width Line
+ { '8', &ProcessDECALN }, // Screen Alignment Display
+ { '\0' }
+};
+
+CAnsiTerm::CSIFunction CAnsiTerm::s_CSIFunction[] =
+{
+ { 'A', &ProcessCUU }, // Cursor Up
+ { 'B', &ProcessCUD }, // Cursor Down
+ { 'C', &ProcessCUF }, // Cursor Forward
+ { 'D', &ProcessCUB }, // Cursor Backward
+ { 'H', &ProcessCUP }, // Cursor Position
+ { 'J', &ProcessED }, // Erase in Display
+ { 'K', &ProcessEL }, // Erase in Line
+ { 'c', &ProcessDA }, // Device Attributes
+ { 'f', &ProcessHVP }, // Horizontal and Vertical Position
+ { 'g', &ProcessTBC }, // Tabulation Clear
+ { 'h', &ProcessSM }, // Set Mode
+ { 'l', &ProcessRM }, // Reset Mode
+ { 'm', &ProcessSGR }, // Select Graphics Rendition
+ { 'n', &ProcessDSR }, // Device Status Report
+ { 'q', &ProcessDECLL }, // DEC Load LEDs
+ { 'r', &ProcessDECSTBM }, // DEC Set Top and Bottom Margins
+ { 'x', &ProcessDECREQTPARM }, // Request Terminal Parameters
+ { 'y', &ProcessDECTST }, // Invoke Confidence Test
+ { '\0' }
+};
+
+wchar_t CAnsiTerm::s_GraphicChars[kMaxGraphicsChar - kMinGraphicsChar + 1] =
+{
+ 0x0020, // 0137 5F 95 _ Blank
+ 0x2666, // 0140 60 96 ` Diamond
+ 0x2592, // 0141 61 97 a Checkerboard
+ 0x2409, // 0142 62 98 b Horizontal Tab
+ 0x240C, // 0143 63 99 c Form Feed
+ 0x240D, // 0144 64 100 d Carriage Return
+ 0x240A, // 0145 65 101 e Line Feed
+ 0x00B0, // 0146 66 102 f Degree Symbol
+ 0x00B1, // 0147 67 103 g Plus/Minus
+ 0x2424, // 0150 68 104 h New Line
+ 0x240B, // 0151 69 105 i Vertical Tab
+ 0x2518, // 0152 6A 106 j Lower-right corner
+ 0x2510, // 0153 6B 107 k Upper-right corner
+ 0x250C, // 0154 6C 108 l Upper-left corner
+ 0x2514, // 0155 6D 109 m Lower-left corner
+ 0x253C, // 0156 6E 110 n Crossing Lines
+ 0x00AF, // 0157 6F 111 o Horizontal Line - Scan 1
+ 0x0070, // 0160 70 112 p Horizontal Line - Scan 3 (No translation)
+ 0x2500, // 0161 71 113 q Horizontal Line - Scan 5
+ 0x0072, // 0162 72 114 r Horizontal Line - Scan 7 (No translation)
+ 0x005F, // 0163 73 115 s Horizontal Line - Scan 9
+ 0x251C, // 0164 74 116 t Left "T"
+ 0x2524, // 0165 75 117 u Right "T"
+ 0x2534, // 0166 76 118 v Bottom "T"
+ 0x252C, // 0167 77 119 w Top "T"
+ 0x2502, // 0170 78 120 x | Vertical bar
+ 0x2264, // 0171 79 121 y Less than or equal to
+ 0x2265, // 0172 7A 122 z Greater than or equal to
+ 0x03C0, // 0173 7B 123 { Pi
+ 0x2260, // 0174 7C 124 | Not equal to
+ 0x00A3, // 0175 7D 125 } UK Pound Sign
+ 0x00B7 // 0176 7E 126 ~ Centered dot
+};
+
+wchar_t CAnsiTerm::s_OemToUnicode[256] =
+{
+ 0x2007, 0x263A, 0x263B, 0x2665, 0x2666, 0x2663, 0x2660, 0x2022, // 00-07
+ 0x25D8, 0x25CB, 0x25D9, 0x2642, 0x2640, 0x266A, 0x266B, 0x263C, // 08-0F
+ 0x25BA, 0x25C4, 0x2195, 0x203C, 0x00B6, 0x00A7, 0x25AC, 0x21A8, // 10-17
+ 0x2191, 0x2193, 0x2192, 0x2190, 0x221F, 0x2194, 0x25B2, 0x25BC, // 18-1F
+ 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, // 20-27
+ 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, // 28-2F
+ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, // 30-37
+ 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, // 38-3F
+ 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, // 40-47
+ 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, // 48-4F
+ 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, // 50-57
+ 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, // 58-5F
+ 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, // 60-67
+ 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, // 68-6F
+ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, // 70-77
+ 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, // 78-7F
+ 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, // 80-87
+ 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5, // 88-8F
+ 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, // 90-97
+ 0x00FF, 0x00D6, 0x00DC, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192, // 98-9F
+ 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, // A0-A7
+ 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, // A8-AF
+ 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, // B0-B7
+ 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, // B8-BF
+ 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, // C0-C7
+ 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, // C8-CF
+ 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, // D0-D7
+ 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, // D8-DF
+ 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, // E0-E7
+ 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229, // E8-EF
+ 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, // F0-F7
+ 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0 // F8-FF
+};
+
+COLORREF CAnsiTerm::s_ColorTable[kColorTableSize] =
+{
+ RGB(0, 0, 0),
+ RGB(0, 0, 128),
+ RGB(0, 128, 0),
+ RGB(0, 128, 128),
+ RGB(128, 0, 0),
+ RGB(128, 0, 128),
+ RGB(128, 128, 0),
+ RGB(192, 192, 192),
+ RGB(128, 128, 128),
+ RGB(0, 0, 255),
+ RGB(0, 255, 0),
+ RGB(0, 255, 255),
+ RGB(255, 0, 0),
+ RGB(255, 0, 255),
+ RGB(255, 255, 0),
+ RGB(255, 255, 255)
+};
+
+CAnsiTerm::PFN_GetConsoleScreenBufferInfoEx CAnsiTerm::s_pfnGetConsoleScreenBufferInfoEx;
+CAnsiTerm::PFN_SetConsoleScreenBufferInfoEx CAnsiTerm::s_pfnSetConsoleScreenBufferInfoEx;
+
+CAnsiTerm::CAnsiTerm(void)
+{
+ m_hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
+
+ GetConsoleMode(m_hConsole, &m_dwOrigConsoleMode);
+
+ SetConsoleMode(m_hConsole, ENABLE_PROCESSED_OUTPUT);
+
+ CONSOLE_CURSOR_INFO cursorInfo;
+
+ GetConsoleCursorInfo(m_hConsole, &cursorInfo);
+
+ m_dwOrigCursorSize = cursorInfo.dwSize;
+
+ WindowSizeChanged(true);
+}
+
+CAnsiTerm::~CAnsiTerm(void)
+{
+ CONSOLE_CURSOR_INFO cursorInfo = { m_dwOrigCursorSize, TRUE };
+
+ SetConsoleCursorInfo(m_hConsole, &cursorInfo);
+
+ SetConsoleMode(m_hConsole, m_dwOrigConsoleMode);
+
+ SetConsoleTextAttribute(m_hConsole, m_wOrigConsoleAttribute);
+
+ if (m_bResetColorTable)
+ {
+ CONSOLE_SCREEN_BUFFER_INFOEX consoleInfo = { sizeof(consoleInfo) };
+
+ s_pfnGetConsoleScreenBufferInfoEx(m_hConsole, &consoleInfo);
+
+ memcpy(consoleInfo.ColorTable, m_OrigColorTable, kColorTableSize * sizeof(consoleInfo.ColorTable[0]));
+
+ // There is a bug between GetConsoleScreenBufferInfoEx and SetConsoleScreenBufferInfoEx.
+ // The first treats srWindow.Right and srWindow.Bottom as inclusive and the latter as exclusive.
+ consoleInfo.srWindow.Right++;
+ consoleInfo.srWindow.Bottom++;
+
+ s_pfnSetConsoleScreenBufferInfoEx(m_hConsole, &consoleInfo);
+
+ // Reset the attributes on existing lines so that at least white on black looks
+ // correct.
+ COORD coordStart = { 0, 0 };
+ DWORD dwWritten;
+
+ FillConsoleOutputAttribute(m_hConsole, m_wOrigConsoleAttribute, m_BufferSize.X * m_BufferSize.Y, coordStart, &dwWritten);
+ }
+
+ CloseHandle(m_hConsole);
+}
+
+void
+CAnsiTerm::WindowSizeChanged(bool bInitial)
+{
+ WORD wCurrentAttribute;
+ COORD dwCurrentCursorPosition;
+
+ if (bInitial)
+ {
+ HMODULE hKernel32 = GetModuleHandle("kernel32");
+
+ if (hKernel32 != NULL)
+ {
+ s_pfnGetConsoleScreenBufferInfoEx = (PFN_GetConsoleScreenBufferInfoEx)GetProcAddress(hKernel32, "GetConsoleScreenBufferInfoEx");
+ s_pfnSetConsoleScreenBufferInfoEx = (PFN_SetConsoleScreenBufferInfoEx)GetProcAddress(hKernel32, "SetConsoleScreenBufferInfoEx");
+
+ if (s_pfnGetConsoleScreenBufferInfoEx == NULL || s_pfnSetConsoleScreenBufferInfoEx == NULL)
+ {
+ s_pfnGetConsoleScreenBufferInfoEx = NULL;
+ s_pfnSetConsoleScreenBufferInfoEx = NULL;
+ }
+ }
+
+ m_bResetColorTable = false;
+ }
+
+ if (s_pfnGetConsoleScreenBufferInfoEx != NULL)
+ {
+ CONSOLE_SCREEN_BUFFER_INFOEX consoleInfo = { sizeof(consoleInfo) };
+
+ s_pfnGetConsoleScreenBufferInfoEx(m_hConsole, &consoleInfo);
+
+ m_BufferSize = consoleInfo.dwSize;
+
+ m_WindowSize.X = consoleInfo.srWindow.Right - consoleInfo.srWindow.Left + 1;
+ m_WindowSize.Y = consoleInfo.srWindow.Bottom - consoleInfo.srWindow.Top + 1;
+
+ wCurrentAttribute = consoleInfo.wAttributes;
+ dwCurrentCursorPosition = consoleInfo.dwCursorPosition;
+
+ if (bInitial)
+ {
+ m_bResetColorTable = memcmp(consoleInfo.ColorTable, s_ColorTable, sizeof(s_ColorTable)) != 0;
+
+ if (m_bResetColorTable)
+ {
+ // The command prompt (cmd.exe) uses a nonstandard color table
+ // So we save it away and reset it to match the Console API documentation
+ size_t colorCopyLen = kColorTableSize * sizeof(consoleInfo.ColorTable[0]);
+
+ memcpy(m_OrigColorTable, consoleInfo.ColorTable, colorCopyLen);
+
+ memcpy(consoleInfo.ColorTable, s_ColorTable, colorCopyLen);
+
+ // There is a bug between GetConsoleScreenBufferInfoEx and SetConsoleScreenBufferInfoEx.
+ // The first treats srWindow.Right and srWindow.Bottom as inclusive and the latter as exclusive.
+ consoleInfo.srWindow.Right++;
+ consoleInfo.srWindow.Bottom++;
+
+ s_pfnSetConsoleScreenBufferInfoEx(m_hConsole, &consoleInfo);
+
+ // Reset the attributes on existing lines so that at least white on black looks
+ // correct.
+ COORD coordStart = { 0, 0 };
+ DWORD dwWritten;
+
+ FillConsoleOutputAttribute(m_hConsole, kDefaultAttribute, m_BufferSize.X * m_BufferSize.Y, coordStart, &dwWritten);
+ }
+ }
+ }
+ else
+ {
+ CONSOLE_SCREEN_BUFFER_INFO consoleInfo;
+
+ GetConsoleScreenBufferInfo(m_hConsole, &consoleInfo);
+
+ m_BufferSize = consoleInfo.dwSize;
+
+ m_WindowSize.X = consoleInfo.srWindow.Right - consoleInfo.srWindow.Left + 1;
+ m_WindowSize.Y = consoleInfo.srWindow.Bottom - consoleInfo.srWindow.Top + 1;
+
+ wCurrentAttribute = consoleInfo.wAttributes;
+ dwCurrentCursorPosition = consoleInfo.dwCursorPosition;
+ }
+
+ m_WindowOrigin.X = 0;
+ m_WindowOrigin.Y = m_BufferSize.Y - m_WindowSize.Y;
+
+ if (bInitial)
+ {
+ m_wOrigConsoleAttribute = wCurrentAttribute;
+
+ SHORT nLines = dwCurrentCursorPosition.Y - m_WindowOrigin.Y;
+
+ if (nLines != 0)
+ {
+ SMALL_RECT rectSource = { 0, 0, m_BufferSize.X - 1, m_BufferSize.Y - 1 };
+ COORD coordDest = { 0, 0 };
+ CHAR_INFO charInfo = { ' ', kDefaultAttribute };
+
+ if (nLines > 0)
+ {
+ rectSource.Top = nLines;
+ }
+ else
+ {
+ coordDest.Y -= nLines;
+ rectSource.Bottom += nLines;
+ }
+
+ ScrollConsoleScreenBuffer(m_hConsole, &rectSource, NULL, coordDest, &charInfo);
+ }
+ }
+
+ ResetTerm();
+}
+
+int
+CAnsiTerm::ProcessInput(CAnsiTerm::KeyCode keyCode, unsigned char *pOutput, int iOutputSize)
+{
+ int iOutputLength = 0;
+
+ if (pOutput == NULL || iOutputSize < 1)
+ {
+ return 0;
+ }
+
+ if (!keyCode.bKeyDown)
+ {
+ return 0;
+ }
+
+ if (VK_F1 <= keyCode.VirtualKeyCode && keyCode.VirtualKeyCode <= VK_F12)
+ {
+ pOutput[iOutputLength++] = CH_ESC;
+ pOutput[iOutputLength++] = 'O';
+ pOutput[iOutputLength++] = 'P' + keyCode.VirtualKeyCode - VK_F1;
+ }
+ else
+ if (keyCode.VirtualKeyCode == VK_UP ||
+ keyCode.VirtualKeyCode == VK_DOWN ||
+ keyCode.VirtualKeyCode == VK_RIGHT ||
+ keyCode.VirtualKeyCode == VK_LEFT)
+ {
+ pOutput[iOutputLength++] = CH_ESC;
+
+ if ((m_bCursorKeyMode && !keyCode.bControl) ||
+ (!m_bCursorKeyMode && keyCode.bControl))
+ {
+ pOutput[iOutputLength++] = 'O';
+ }
+ else
+ {
+ pOutput[iOutputLength++] = '[';
+ }
+
+ switch (keyCode.VirtualKeyCode)
+ {
+ case VK_UP:
+ pOutput[iOutputLength++] = 'A';
+ break;
+
+ case VK_DOWN:
+ pOutput[iOutputLength++] = 'B';
+ break;
+
+ case VK_RIGHT:
+ pOutput[iOutputLength++] = 'C';
+ break;
+
+ case VK_LEFT:
+ pOutput[iOutputLength++] = 'D';
+ break;
+ }
+ }
+ else
+ if (keyCode.VirtualKeyCode == VK_HOME ||
+ keyCode.VirtualKeyCode == VK_INSERT ||
+ keyCode.VirtualKeyCode == VK_DELETE ||
+ keyCode.VirtualKeyCode == VK_END ||
+ keyCode.VirtualKeyCode == VK_PRIOR ||
+ keyCode.VirtualKeyCode == VK_NEXT)
+ {
+ pOutput[iOutputLength++] = CH_ESC;
+ pOutput[iOutputLength++] = '[';
+
+ switch (keyCode.VirtualKeyCode)
+ {
+ case VK_HOME:
+ pOutput[iOutputLength++] = '1';
+ break;
+
+ case VK_INSERT:
+ pOutput[iOutputLength++] = '2';
+ break;
+
+ case VK_DELETE:
+ pOutput[iOutputLength++] = '3';
+ break;
+
+ case VK_END:
+ pOutput[iOutputLength++] = '4';
+ break;
+
+ case VK_PRIOR:
+ pOutput[iOutputLength++] = '5';
+ break;
+
+ case VK_NEXT:
+ pOutput[iOutputLength++] = '6';
+ break;
+ }
+
+ pOutput[iOutputLength++] = '~';
+ }
+ else
+ if (keyCode.VirtualKeyCode == VK_RETURN)
+ {
+ pOutput[iOutputLength++] = CH_CR;
+ if (fCRLF == 1)
+ {
+ pOutput[iOutputLength++] = CH_LF;
+ }
+ }
+ else
+ if (keyCode.AsciiChar != '\0')
+ {
+ pOutput[iOutputLength++] = keyCode.AsciiChar;
+ }
+
+ return iOutputLength;
+}
+
+bool
+CAnsiTerm::ProcessOutput(const unsigned char *szData, int iLength)
+{
+ const unsigned char *pEnd = &szData[iLength];
+
+ for (const unsigned char *pCurrent = szData; pCurrent < pEnd; pCurrent++)
+ {
+ if (*pCurrent < 0x20 || *pCurrent == 0x7F)
+ {
+ dbglog("ProcessOutput: control_ch = %02x\n",*pCurrent);
+ OutputText();
+
+ switch (*pCurrent)
+ {
+ case CH_NUL:
+ case CH_ENQ:
+ case CH_DEL:
+ // These are ignored
+ break;
+
+ case CH_BEL:
+ MessageBeep(MB_ICONASTERISK);
+ break;
+
+ case CH_BS:
+ ProcessBackspace();
+ break;
+
+ case CH_HT:
+ ProcessTab();
+ break;
+
+ case CH_LF:
+ case CH_VT:
+ case CH_FF:
+ ProcessLinefeed(m_bLineFeedNewLineMode);
+ break;
+
+ case CH_CR:
+ ProcessReturn();
+ break;
+
+ case CH_SO:
+ m_SelectedCharset = CharsetG1;
+ break;
+
+ case CH_SI:
+ m_SelectedCharset = CharsetG0;
+ break;
+
+ case CH_XON:
+ // Not yet implemented
+ break;
+
+ case CH_XOF:
+ // Not yet implemented
+ break;
+
+#if 0
+ case CH_CAN:
+ case CH_SUB:
+ // Output error character
+ break;
+#endif
+
+ case CH_ESC:
+ m_State = DS_Escape;
+ break;
+
+ default:
+ AddOutputData(s_OemToUnicode[*pCurrent]);
+ break;
+ }
+ }
+ else
+ {
+ /* db b3 or b0 b3 */
+ if (*pCurrent & 0x80)
+ dbglog("ProcessOutput: state=%d ch=%02x\n",m_State,*pCurrent);
+ switch (m_State)
+ {
+ case DS_Normal:
+ if (*pCurrent & 0x80)
+ {
+ // Could be start of a UTF-8 sequence or an ANSI extended character
+
+ if ((*pCurrent & 0xE0) == 0xC0)
+ {
+ m_UTF8Size = 2;
+ }
+ else if ((*pCurrent & 0xF0) == 0xE0)
+ {
+ m_UTF8Size = 3;
+ }
+ else
+ {
+ // Not a UTF-8 lead character
+ AddOutputData(s_OemToUnicode[*pCurrent]);
+ break;
+ }
+ m_UTF8Count = 1;
+ m_UTF8Buffer[0] = *pCurrent;
+ m_State = DS_UTF8;
+ break;
+ }
+
+ if ((m_SelectedCharset == CharsetG0 && m_G0Charset == SpecialGraphicsCharset) ||
+ (m_SelectedCharset == CharsetG1 && m_G1Charset == SpecialGraphicsCharset))
+ {
+ if (kMinGraphicsChar <= *pCurrent && *pCurrent <= kMaxGraphicsChar)
+ {
+ AddOutputData(s_GraphicChars[*pCurrent - kMinGraphicsChar]);
+ }
+ else
+ {
+ AddOutputData(*pCurrent);
+ }
+ }
+ else
+ {
+ AddOutputData(*pCurrent);
+ }
+ break;
+
+ case DS_UTF8:
+ if ((*pCurrent & 0xC0) != 0x80)
+ {
+ for (int index = 0; index < m_UTF8Count; index++)
+ {
+ AddOutputData(s_OemToUnicode[m_UTF8Buffer[index]]);
+ }
+
+ if (*pCurrent & 0x80)
+ {
+ AddOutputData(s_OemToUnicode[*pCurrent]);
+ }
+ else
+ {
+ AddOutputData(*pCurrent);
+ }
+ m_State = DS_Normal;
+ }
+ else
+ {
+ m_UTF8Buffer[m_UTF8Count++] = *pCurrent;
+
+ if (m_UTF8Count == m_UTF8Size)
+ {
+ wchar_t wchUTF16;
+ if (m_UTF8Size == 2)
+ {
+ wchUTF16 = ((m_UTF8Buffer[0] & 0x1F) << 6) | (m_UTF8Buffer[1] & 0x3F);
+ }
+ else
+ {
+ wchUTF16 = ((m_UTF8Buffer[0] & 0x0F) << 12) | ((m_UTF8Buffer[1] & 0x3F) << 6) | (m_UTF8Buffer[2] & 0x3F);
+ }
+
+ AddOutputData(wchUTF16);
+ m_State = DS_Normal;
+ }
+ }
+ break;
+
+ case DS_Escape:
+ for (int index = 0; s_CSITable[index].chCode != '\0'; index++)
+ {
+ if (*pCurrent == s_CSITable[index].chCode)
+ {
+ if (s_CSITable[index].dsNextState != DS_None)
+ {
+ m_State = s_CSITable[index].dsNextState;
+ m_Parameters[0] = 0;
+ m_ParameterCount = 0;
+ m_bParametersStart = true;
+ }
+ else
+ {
+ (this->*s_CSITable[index].pfnProcess)();
+ m_State = DS_Normal;
+ }
+ break;
+ }
+ }
+
+ if (m_State == DS_Escape)
+ {
+ AddOutputData(L'^');
+ AddOutputData(L'[');
+ AddOutputData(*pCurrent);
+ m_State = DS_Normal;
+ }
+ break;
+
+ case DS_CSIParam:
+ if (m_bParametersStart)
+ {
+ m_bParametersStart = false;
+
+ if (*pCurrent == '?')
+ {
+ m_bPrivateParameters = true;
+ break;
+ }
+ else
+ {
+ m_bPrivateParameters = false;
+ }
+ }
+ if ('0' <= *pCurrent && *pCurrent <= '9')
+ {
+ if (m_ParameterCount < kMaxParameterCount)
+ {
+ m_Parameters[m_ParameterCount] *= 10;
+ m_Parameters[m_ParameterCount] += *pCurrent - '0';
+ }
+ }
+ else if (*pCurrent == ';')
+ {
+ if (m_ParameterCount < kMaxParameterCount)
+ {
+ m_ParameterCount++;
+ }
+
+ if (m_ParameterCount < kMaxParameterCount)
+ {
+ m_Parameters[m_ParameterCount] = 0;
+ }
+ }
+ else
+ {
+ if (m_ParameterCount < kMaxParameterCount)
+ {
+ m_ParameterCount++;
+ }
+
+ for (int index = 0; s_CSIFunction[index].chCode != '\0'; index++)
+ {
+ if (*pCurrent == s_CSIFunction[index].chCode)
+ {
+ (this->*s_CSIFunction[index].pfnProcess)();
+ m_State = DS_Normal;
+ break;
+ }
+ }
+ if (m_State != DS_Normal)
+ {
+ DisplayCSI(*pCurrent);
+ m_State = DS_Normal;
+ }
+ }
+ break;
+
+ case DS_DECPrivate:
+ for (int index = 0; s_DECFunction[index].chCode != '\0'; index++)
+ {
+ if (*pCurrent == s_DECFunction[index].chCode)
+ {
+ (this->*s_DECFunction[index].pfnProcess)();
+ m_State = DS_Normal;
+ break;
+ }
+ }
+
+ if (m_State != DS_Normal)
+ {
+ AddOutputData(L'^');
+ AddOutputData(L'[');
+ AddOutputData(L'#');
+ AddOutputData(*pCurrent);
+ }
+ break;
+
+ case DS_SelectG0:
+ ProcessSCSG0(*pCurrent);
+ m_State = DS_Normal;
+ break;
+
+ case DS_SelectG1:
+ ProcessSCSG1(*pCurrent);
+ m_State = DS_Normal;
+ break;
+
+ default:
+ dbglog("ProcessOutput: illegal m_State=%d\n",m_State);
+ assert(false);
+ break;
+ }
+ }
+ }
+
+ OutputText();
+
+ return true;
+}
+
+void
+CAnsiTerm::DisplayCSI(char ch)
+{
+ char szParam[15];
+
+ AddOutputData(L'^');
+ AddOutputData(L'[');
+ AddOutputData(L'[');
+ for (int idxParam = 0; idxParam < m_ParameterCount; idxParam++)
+ {
+ if (idxParam > 0)
+ {
+ AddOutputData(L';');
+ }
+
+ int iLenParam = sprintf(szParam, "%d", m_Parameters[idxParam]);
+
+ for (int idxChar = 0; idxChar < iLenParam; idxChar++)
+ {
+ AddOutputData(szParam[idxChar]);
+ }
+ }
+ AddOutputData(ch);
+}
+
+bool
+CAnsiTerm::ResetTerm(void)
+{
+ m_State = DS_Normal;
+
+ m_SelectedCharset = CharsetG0;
+ m_G0Charset = AsciiCharset;
+ m_G1Charset = SpecialGraphicsCharset;
+
+ m_Cursor.X = 0;
+ m_Cursor.Y = 0;
+
+ m_SavedCursor = m_Cursor;
+
+ m_sTopMargin = 0;
+ m_sBottomMargin = m_WindowSize.Y - 1;
+
+ m_Attribute = kDefaultAttribute;
+
+ UpdateTextAttribute();
+
+ m_dwOutputCount = 0;
+
+ // if (fCRLF == 1)
+ m_bLineFeedNewLineMode = true; /*was false*/
+ m_bCursorKeyMode = false;
+ m_bAnsiMode = true;
+ m_bColumnMode = true;
+ m_bScrollingMode = false;
+ m_bScreenMode = false;
+ m_bOriginMode = false;
+ m_bAutoRepeatingMode = true;
+ m_bInterlaceMode = false;
+ m_bDisplayCursor = true;
+ m_bAutoWrapMode = true; /*default to wrap*/
+
+ EraseDisplay(EraseAll);
+
+ SetCursorPosition();
+
+ DisplayCursor();
+
+ return true;
+}
+
+bool
+CAnsiTerm::DisplayCursor(void)
+{
+ CONSOLE_CURSOR_INFO cursorInfo = { 100, m_bDisplayCursor };
+
+ return SetConsoleCursorInfo(m_hConsole, &cursorInfo) != FALSE;
+}
+
+bool
+CAnsiTerm::UpdateTextAttribute(void)
+{
+ return SetConsoleTextAttribute(m_hConsole, m_Attribute) != FALSE;
+}
+
+bool
+CAnsiTerm::GetCursorPosition(void)
+{
+ CONSOLE_SCREEN_BUFFER_INFO bufferInfo;
+
+ if (GetConsoleScreenBufferInfo(m_hConsole, &bufferInfo))
+ {
+ m_Cursor = bufferInfo.dwCursorPosition;
+ m_Cursor.Y -= m_WindowOrigin.Y + (m_bOriginMode ? m_sTopMargin : 0);
+
+ return true;
+ }
+
+ return false;
+}
+
+bool
+CAnsiTerm::SetCursorPosition(void)
+{
+ COORD cursor = m_Cursor;
+
+ cursor.Y += m_WindowOrigin.Y + (m_bOriginMode ? m_sTopMargin : 0);
+
+ return SetConsoleCursorPosition(m_hConsole, cursor) != FALSE;
+}
+
+bool
+CAnsiTerm::ScrollDisplay(int n, bool bWindowOnly)
+{
+ SHORT nLines = (SHORT)n;
+ if (nLines == 0)
+ {
+ return true;
+ }
+
+ SMALL_RECT rectSource = { 0, 0, m_BufferSize.X - 1, m_BufferSize.Y - 1 };
+ COORD coordDest = { 0, 0 };
+ CHAR_INFO charInfo = { ' ', m_Attribute };
+
+ if (nLines > 0)
+ {
+ if (bWindowOnly || m_sTopMargin > 0)
+ {
+ coordDest.Y = m_WindowOrigin.Y + m_sTopMargin;
+ rectSource.Top = m_WindowOrigin.Y + m_sTopMargin + nLines;
+ rectSource.Bottom = m_WindowOrigin.Y + m_sBottomMargin;
+ }
+ else
+ {
+ rectSource.Top = nLines;
+ rectSource.Bottom = m_WindowOrigin.Y + m_sBottomMargin;
+ }
+ }
+ else
+ {
+ if (bWindowOnly)
+ {
+ coordDest.Y = m_WindowOrigin.Y + m_sTopMargin - nLines;
+ rectSource.Top = m_WindowOrigin.Y + m_sTopMargin;
+ rectSource.Bottom = m_WindowOrigin.Y + m_sBottomMargin + 1 + nLines;
+ }
+ else
+ {
+ coordDest.Y -= nLines;
+ }
+
+ rectSource.Bottom += nLines;
+ }
+
+ return ScrollConsoleScreenBuffer(m_hConsole, &rectSource, NULL, coordDest, &charInfo) != FALSE;
+}
+
+bool
+CAnsiTerm::EraseLine(EraseType eType)
+{
+ DWORD dwLength;
+
+ COORD coordStart = m_WindowOrigin;
+
+ coordStart.Y += m_Cursor.Y + (m_bOriginMode ? m_sTopMargin : 0);
+
+ switch (eType)
+ {
+ case EraseCursorToEnd:
+ if (m_Cursor.X < m_WindowSize.X)
+ {
+ coordStart.X += m_Cursor.X;
+ dwLength = m_BufferSize.X - m_Cursor.X;
+ }
+ else
+ {
+ dwLength = 0;
+ }
+ break;
+
+ case EraseBeginningToCursor:
+ if (m_Cursor.X < m_WindowSize.X)
+ {
+ dwLength = m_Cursor.X + 1;
+ }
+ else
+ {
+ dwLength = m_BufferSize.X;
+ }
+ break;
+
+ case EraseAll:
+ dwLength = m_BufferSize.X;
+ break;
+
+ default:
+ return false;
+ }
+
+ if (dwLength > 0)
+ {
+ DWORD dwWritten;
+
+ FillConsoleOutputAttribute(m_hConsole, m_Attribute, dwLength, coordStart, &dwWritten);
+ return FillConsoleOutputCharacter(m_hConsole, ' ', dwLength, coordStart, &dwWritten) != FALSE;
+ }
+ else
+ {
+ return true;
+ }
+}
+
+bool
+CAnsiTerm::EraseDisplay(EraseType eType)
+{
+ COORD coordStart = m_WindowOrigin;
+ DWORD dwLength;
+
+ switch (eType)
+ {
+ case EraseCursorToEnd:
+ if (m_Cursor.X < m_WindowSize.X)
+ {
+ coordStart.X += m_Cursor.X;
+ coordStart.Y += m_Cursor.Y + (m_bOriginMode ? m_sTopMargin : 0);
+ dwLength = (m_WindowSize.Y - m_Cursor.Y - (m_bOriginMode ? m_sTopMargin : 0)) * m_BufferSize.X - m_Cursor.X;
+ }
+ else if (m_Cursor.Y < (m_WindowSize.Y - 1))
+ {
+ coordStart.X = 0;
+ coordStart.Y += m_Cursor.Y + 1;
+ dwLength = (m_WindowSize.Y - m_Cursor.Y - (m_bOriginMode ? m_sTopMargin : 0) - 1) * m_BufferSize.X;
+ }
+ else
+ {
+ dwLength = 0;
+ }
+ break;
+
+ case EraseBeginningToCursor:
+ if (m_Cursor.X < m_WindowSize.X)
+ {
+ dwLength = (m_Cursor.Y + (m_bOriginMode ? m_sTopMargin : 0)) * m_BufferSize.X + m_Cursor.X;
+ }
+ else
+ {
+ dwLength = (m_Cursor.Y + (m_bOriginMode ? m_sTopMargin : 0) + 1) * m_BufferSize.X;
+ }
+ break;
+
+ case EraseAll:
+ dwLength = m_BufferSize.X * m_WindowSize.Y;
+ break;
+
+ default:
+ return false;
+ }
+
+ if (dwLength > 0)
+ {
+ DWORD dwWritten;
+
+ FillConsoleOutputAttribute(m_hConsole, m_Attribute, dwLength, coordStart, &dwWritten);
+ return FillConsoleOutputCharacter(m_hConsole, ' ', dwLength, coordStart, &dwWritten) != FALSE;
+ }
+ else
+ {
+ return true;
+ }
+}
+
+DWORD
+CAnsiTerm::OutputText(void)
+{
+ if (m_dwOutputCount == 0)
+ {
+ return 0;
+ }
+
+ DWORD dwTotalWritten = 0;
+ wchar_t * pwszCurrent = m_OutputBuffer;
+ DWORD dwLeftToWrite = m_dwOutputCount;
+
+ while (dwLeftToWrite > 0)
+ {
+ DWORD dwWritten;
+
+ if (m_Cursor.X >= m_WindowSize.X)
+ {
+ if (m_bAutoWrapMode)
+ {
+ ProcessLinefeed(true);
+ }
+ else
+ {
+ m_Cursor.X = m_WindowSize.X - 1;
+
+ SetCursorPosition();
+
+ if (WriteConsoleW(m_hConsole, &m_OutputBuffer[m_dwOutputCount - 1], 1, &dwWritten, NULL))
+ {
+ assert(dwWritten == 1);
+ }
+
+ m_Cursor.X++;
+ dwTotalWritten += dwLeftToWrite;
+ break;
+ }
+ }
+
+ DWORD dwPartialCount = min(dwLeftToWrite, (DWORD)(m_WindowSize.X - m_Cursor.X));
+
+ if (WriteConsoleW(m_hConsole, pwszCurrent, dwPartialCount, &dwWritten, NULL))
+ {
+ assert(dwWritten == dwPartialCount);
+ }
+ else
+ {
+ DWORD dwError = GetLastError();
+ }
+
+ m_Cursor.X += (SHORT)dwPartialCount;
+ pwszCurrent += dwPartialCount;
+ dwTotalWritten += dwPartialCount;
+ dwLeftToWrite -= dwPartialCount;
+ }
+
+ m_dwOutputCount = 0;
+
+ return dwTotalWritten;
+}
+
+bool
+CAnsiTerm::ProcessBackspace(void)
+{
+ if (m_Cursor.X > 0)
+ {
+ if (m_Cursor.X < m_WindowSize.X)
+ {
+ m_Cursor.X--;
+ }
+ else
+ {
+ m_Cursor.X = m_WindowSize.X - 1;
+ }
+
+ return SetCursorPosition();
+ }
+ return true;
+}
+
+bool
+CAnsiTerm::ProcessTab(void)
+{
+ if (m_Cursor.X >= m_WindowSize.X)
+ {
+ if (m_bAutoWrapMode)
+ {
+ ProcessLinefeed(true);
+ }
+ else
+ {
+ return true;
+ }
+ }
+
+ int newX = m_Cursor.X + 8;
+
+ newX &= ~7;
+
+ if (newX >= m_WindowSize.X)
+ {
+ newX = m_WindowSize.X - 1;
+ }
+
+ int cntSpaces = newX - m_Cursor.X;
+
+ for (int index = 0; index < cntSpaces; index++)
+ {
+ AddOutputData(L' ');
+ }
+
+ return true;
+}
+
+bool
+CAnsiTerm::ProcessReverseLinefeed(void)
+{
+ if (m_Cursor.Y == 0)
+ {
+ ScrollDisplay(-1, true);
+ }
+ else
+ {
+ m_Cursor.Y--;
+ }
+
+ return SetCursorPosition();
+}
+
+bool
+CAnsiTerm::ProcessLinefeed(bool bNewLine)
+{
+ if (bNewLine)
+ {
+ m_Cursor.X = 0;
+ }
+
+ if (m_bOriginMode || (m_Cursor.Y >= m_sTopMargin && m_Cursor.Y <= m_sBottomMargin))
+ {
+ if (m_Cursor.Y >= (m_sBottomMargin - m_sTopMargin))
+ {
+ ScrollDisplay(1, false);
+ m_Cursor.Y = m_sBottomMargin;
+ }
+ else
+ {
+ m_Cursor.Y++;
+ }
+ }
+ else if (m_Cursor.Y < m_sBottomMargin)
+ {
+ m_Cursor.Y++;
+ }
+
+ return SetCursorPosition();
+}
+
+bool
+CAnsiTerm::ProcessReturn(void)
+{
+ m_Cursor.X = 0;
+
+ return SetCursorPosition();
+}
+
+bool
+CAnsiTerm::ProcessDECALN(void)
+{
+ // Fill the display with 'E' for adjusting the CRT on a VT100 - Ignore it
+ return true;
+}
+
+bool
+CAnsiTerm::ProcessDECDHLB(void)
+{
+ // Double Height Line Bottom - Not supported
+ return true;
+}
+
+bool
+CAnsiTerm::ProcessDECDHLT(void)
+{
+ // Double Height Line Top - Not supported
+ return true;
+}
+
+bool
+CAnsiTerm::ProcessDECDWL(void)
+{
+ // Double Width Line - Not supported
+ return true;
+}
+
+bool
+CAnsiTerm::ProcessDECID(void)
+{
+ return ProcessDA();
+}
+
+bool
+CAnsiTerm::ProcessDECKPAM(void)
+{
+ // Keypad Application Mode - Not yet implemented
+ return true;
+}
+
+bool
+CAnsiTerm::ProcessDECKPNM(void)
+{
+ // Keypad Numeric Mode - Not yet implemented
+ return true;
+}
+
+bool
+CAnsiTerm::ProcessDECLL(void)
+{
+ // Load LEDs - Not Supported
+ return true;
+}
+
+bool
+CAnsiTerm::ProcessDECRC(void)
+{
+ return ProcessRCP();
+}
+
+bool
+CAnsiTerm::ProcessDECREQTPARM(void)
+{
+ // Request Terminal Parameters (Baud Rate, Parity, etc) - Not supported
+ return true;
+}
+
+bool
+CAnsiTerm::ProcessDECSC(void)
+{
+ return ProcessSCP();
+}
+
+bool
+CAnsiTerm::ProcessDECSTBM(void)
+{
+ assert(m_ParameterCount >= 1);
+
+ if (m_Parameters[0] > 0)
+ {
+ m_Parameters[0]--;
+ }
+
+ if (m_ParameterCount < 2)
+ {
+ m_Parameters[1] = m_WindowSize.Y - 1;
+ }
+ else
+ {
+ if (m_Parameters[1] > 0)
+ {
+ m_Parameters[1]--;
+ }
+ else
+ {
+ m_Parameters[1] = m_WindowSize.Y - 1;
+ }
+ }
+
+ if (m_Parameters[0] >= m_WindowSize.Y ||
+ m_Parameters[1] >= m_WindowSize.Y ||
+ m_Parameters[0] >= m_Parameters[1])
+ {
+ return false;
+ }
+
+ m_sTopMargin = (SHORT)m_Parameters[0];
+ m_sBottomMargin = (SHORT)m_Parameters[1];
+
+ m_Cursor.X = 0;
+ m_Cursor.Y = 0;
+
+ SetCursorPosition();
+
+ return true;
+}
+
+bool
+CAnsiTerm::ProcessDECSWL(void)
+{
+ // Single Width Line - Since Double Width Line isn't supported, this isn't necessary
+ return true;
+}
+
+bool
+CAnsiTerm::ProcessDECTST(void)
+{
+ // Perform Self Test - Not supported
+ return true;
+}
+
+bool
+CAnsiTerm::ProcessCUB(void)
+{
+ if (m_Parameters[0] == 0)
+ {
+ m_Parameters[0]++;
+ }
+
+ m_Cursor.X -= min(m_Cursor.X, m_Parameters[0]);
+
+ return SetCursorPosition();
+}
+
+bool
+CAnsiTerm::ProcessCUD(void)
+{
+ if (m_Parameters[0] == 0)
+ {
+ m_Parameters[0]++;
+ }
+
+ m_Cursor.Y += min((m_bOriginMode ? m_sBottomMargin : m_WindowSize.Y - 1) - m_Cursor.Y, m_Parameters[0]);
+
+ return SetCursorPosition();
+}
+
+bool
+CAnsiTerm::ProcessCUF(void)
+{
+ if (m_Parameters[0] == 0)
+ {
+ m_Parameters[0]++;
+ }
+
+ m_Cursor.X += min(m_WindowSize.X - m_Cursor.X - 1, m_Parameters[0]);
+
+ return SetCursorPosition();
+}
+
+bool
+CAnsiTerm::ProcessCUP(void)
+{
+ return ProcessHVP();
+}
+
+bool
+CAnsiTerm::ProcessCUU(void)
+{
+ if (m_Parameters[0] == 0)
+ {
+ m_Parameters[0]++;
+ }
+
+ m_Cursor.Y -= min(m_Cursor.Y, m_Parameters[0]);
+
+ return SetCursorPosition();
+}
+
+bool
+CAnsiTerm::ProcessDA(void)
+{
+ // Send Device Attributes - Not supported
+ return true;
+}
+
+bool
+CAnsiTerm::ProcessDSR(void)
+{
+ // Send Device Status Request - Not supported
+ return true;
+}
+
+bool
+CAnsiTerm::ProcessED(void)
+{
+ return EraseDisplay((EraseType)m_Parameters[0]);
+}
+
+bool
+CAnsiTerm::ProcessEL(void)
+{
+ return EraseLine((EraseType)m_Parameters[0]);
+}
+
+bool
+CAnsiTerm::ProcessHTS(void)
+{
+ // Soft Tab Set - Not implemented yet
+ return true;
+}
+
+bool
+CAnsiTerm::ProcessHVP(void)
+{
+ assert(m_ParameterCount >= 1);
+
+ if (m_Parameters[0] > 0)
+ {
+ m_Parameters[0]--;
+ }
+
+ if (m_ParameterCount < 2)
+ {
+ m_Parameters[1] = 0;
+ }
+ else
+ {
+ if (m_Parameters[1] > 0)
+ {
+ m_Parameters[1]--;
+ }
+ }
+
+ if (m_bOriginMode)
+ {
+ if (m_Parameters[0] >= (m_sBottomMargin - m_sTopMargin + 1))
+ {
+ m_Parameters[0] = m_sBottomMargin - m_sTopMargin;
+ }
+ }
+ else
+ {
+ if (m_Parameters[0] >= m_WindowSize.Y)
+ {
+ m_Parameters[0] = m_WindowSize.Y - 1;
+ }
+ }
+
+ if (m_Parameters[1] >= m_WindowSize.X)
+ {
+ m_Parameters[1] = m_WindowSize.X - 1;
+ }
+
+ m_Cursor.Y = (SHORT)m_Parameters[0];
+ m_Cursor.X = (SHORT)m_Parameters[1];
+
+ return SetCursorPosition();
+}
+
+bool
+CAnsiTerm::ProcessIND(void)
+{
+ return ProcessLinefeed(false);
+}
+
+bool
+CAnsiTerm::ProcessNEL(void)
+{
+ return ProcessLinefeed(true);
+}
+
+bool
+CAnsiTerm::ProcessRCP(void)
+{
+ m_Cursor = m_SavedCursor;
+
+ return SetCursorPosition();
+}
+
+bool
+CAnsiTerm::ProcessRI(void)
+{
+ return ProcessReverseLinefeed();
+}
+
+bool
+CAnsiTerm::ProcessRIS(void)
+{
+ return ResetTerm();
+}
+
+bool
+CAnsiTerm::ProcessRM(void)
+{
+ if (m_bPrivateParameters)
+ {
+ for (int index = 0; index < m_ParameterCount; index++)
+ {
+ switch (m_Parameters[index])
+ {
+ case 0:
+ default:
+ assert(false);
+ break;
+ case DECCKM:
+ m_bCursorKeyMode = false;
+ break;
+ case DECANM:
+ m_bAnsiMode = false;
+ break;
+ case DECCOLM:
+ m_bColumnMode = false;
+ break;
+ case DECSCLM:
+ m_bScrollingMode = false;
+ break;
+ case DECSCNM:
+ m_bScreenMode = false;
+ break;
+ case DECOM:
+ m_bOriginMode = false;
+ m_Cursor.X = 0;
+ m_Cursor.Y = 0;
+ SetCursorPosition();
+ break;
+ case DECAWM:
+ m_bAutoWrapMode = false;
+ break;
+ case DECARM:
+ m_bAutoRepeatingMode = false;
+ break;
+ case DECINLM:
+ m_bInterlaceMode = false;
+ break;
+ case DECTCEM:
+ m_bDisplayCursor = false;
+ DisplayCursor();
+ break;
+ }
+ }
+ }
+ else
+ {
+ for (int index = 0; index < m_ParameterCount; index++)
+ {
+ switch (m_Parameters[index])
+ {
+ case 20: m_bLineFeedNewLineMode = false; break; // LNM
+ default: assert(false); break;
+ }
+ }
+ }
+ return true;
+}
+
+bool
+CAnsiTerm::ProcessSCP(void)
+{
+ m_SavedCursor = m_Cursor;
+ return true;
+}
+
+bool
+CAnsiTerm::ProcessSCSG0(char ch)
+{
+ switch (ch)
+ {
+ case 'B': // ASCII Charset
+ m_G0Charset = AsciiCharset;
+ break;
+
+ case '0': // Special Graphics Charset
+ m_G0Charset = SpecialGraphicsCharset;
+ break;
+
+ case 'A': // UK Charset
+ case '1': // Alternate Character ROM Standard Charset
+ case '2': // Alternate Character ROM Special Graphics Charset
+ default: // Unsupported
+ return false;
+
+ }
+ return true;
+}
+
+bool
+CAnsiTerm::ProcessSCSG1(char ch)
+{
+ switch (ch)
+ {
+ case 'B': // ASCII Charset
+ m_G1Charset = AsciiCharset;
+ break;
+
+ case '0': // Special Graphics Charset
+ m_G1Charset = SpecialGraphicsCharset;
+ break;
+
+ case 'A': // UK Charset
+ case '1': // Alternate Character ROM Standard Charset
+ case '2': // Alternate Character ROM Special Graphics Charset
+ default: // Unsupported
+ return false;
+
+ }
+ return true;
+}
+
+bool
+CAnsiTerm::ProcessSGR(void)
+{
+ for (int index = 0; index < m_ParameterCount; index++)
+ {
+ switch (m_Parameters[index])
+ {
+ case 0:
+ m_Attribute = kDefaultAttribute;
+ break;
+
+ case 1:
+ m_Attribute |= FOREGROUND_INTENSITY;
+ break;
+
+ case 4:
+ m_Attribute |= COMMON_LVB_UNDERSCORE;
+ break;
+
+ case 5:
+ // Blinking isn't supported
+ break;
+
+ case 7:
+ m_Attribute |= COMMON_LVB_REVERSE_VIDEO;
+ break;
+
+ case 22:
+ m_Attribute &= ~FOREGROUND_INTENSITY;
+ break;
+
+ case 24:
+ m_Attribute &= ~COMMON_LVB_UNDERSCORE;
+ break;
+
+ case 25:
+ // Blinking isn't supported
+ break;
+
+ case 27:
+ m_Attribute &= ~COMMON_LVB_REVERSE_VIDEO;
+ break;
+
+ case 30: // Black text
+ m_Attribute &= ~(FOREGROUND_INTENSITY | COMMON_LVB_UNDERSCORE |
+ FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
+ break;
+
+ case 31: // Red text
+ m_Attribute &= ~(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
+ m_Attribute |= FOREGROUND_RED;
+ break;
+
+ case 32: // Green text
+ m_Attribute &= ~(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
+ m_Attribute |= FOREGROUND_GREEN;
+ break;
+
+ case 33: // Yellow text
+ m_Attribute &= ~(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
+ m_Attribute |= FOREGROUND_RED | FOREGROUND_GREEN;
+ break;
+
+ case 34: // Blue text
+ m_Attribute &= ~(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
+ m_Attribute |= FOREGROUND_BLUE;
+ break;
+
+ case 35: // Magenta text
+ m_Attribute &= ~(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
+ m_Attribute |= FOREGROUND_RED | FOREGROUND_BLUE;
+ break;
+
+ case 36: // Cyan text
+ m_Attribute &= ~(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
+ m_Attribute |= FOREGROUND_GREEN | FOREGROUND_BLUE;
+ break;
+
+ case 37: // White text
+ m_Attribute |= FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
+ break;
+
+ case 40: // Black background
+ m_Attribute &= ~(BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE);
+ break;
+
+ case 41: // Red background
+ m_Attribute &= ~(BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE);
+ m_Attribute |= BACKGROUND_RED;
+ break;
+
+ case 42: // Green background
+ m_Attribute &= ~(BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE);
+ m_Attribute |= BACKGROUND_GREEN;
+ break;
+
+ case 43: // Yellow background
+ m_Attribute &= ~(BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE);
+ m_Attribute |= BACKGROUND_RED | BACKGROUND_GREEN;
+ break;
+
+ case 44: // Blue background
+ m_Attribute &= ~(BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE);
+ m_Attribute |= BACKGROUND_BLUE;
+ break;
+
+ case 45: // Magenta background
+ m_Attribute &= ~(BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE);
+ m_Attribute |= BACKGROUND_RED | BACKGROUND_BLUE;
+ break;
+
+ case 46: // Cyan background
+ m_Attribute &= ~(BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE);
+ m_Attribute |= BACKGROUND_GREEN | BACKGROUND_BLUE;
+ break;
+
+ case 47: // White background
+ m_Attribute |= BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
+ break;
+
+ default:
+ assert(false);
+ break;
+ }
+ }
+
+ UpdateTextAttribute();
+
+ return true;
+}
+
+bool
+CAnsiTerm::ProcessSM(void)
+{
+
+ dbglog("ProcessSM: start, priv=%d param0=%d cnt=%d\n",
+ m_bPrivateParameters,m_Parameters[0],m_ParameterCount);
+ if (m_bPrivateParameters)
+ {
+ for (int index = 0; index < m_ParameterCount; index++)
+ {
+ switch (m_Parameters[0])
+ {
+ case 0:
+ default:
+ assert(false);
+ break;
+ case DECCKM:
+ m_bCursorKeyMode = true;
+ break;
+ case DECANM:
+ m_bAnsiMode = true;
+ break;
+ case DECCOLM:
+ m_bColumnMode = true;
+ break;
+ case DECSCLM:
+ m_bScrollingMode = true;
+ break;
+ case DECSCNM:
+ m_bScreenMode = true;
+ break;
+ case DECOM:
+ m_bOriginMode = true;
+ m_Cursor.X = 0;
+ m_Cursor.Y = 0;
+ m_SavedCursor = m_Cursor;
+ SetCursorPosition();
+ break;
+ case DECAWM:
+ m_bAutoWrapMode = true;
+ break;
+ case DECARM:
+ m_bAutoRepeatingMode = true;
+ break;
+ case DECINLM:
+ m_bInterlaceMode = true;
+ break;
+ case DECTCEM:
+ m_bDisplayCursor = true;
+ DisplayCursor();
+ break;
+ }
+ }
+ }
+ else
+ {
+ for (int index = 0; index < m_ParameterCount; index++)
+ {
+ switch (m_Parameters[0])
+ {
+ case 20: m_bLineFeedNewLineMode = true; break; // LNM
+ default:
+ dbglog("ProcessSM: param %d != 20\n",m_Parameters[0]);
+ assert(false); break;
+ }
+ }
+ }
+ return true;
+}
+
+bool
+CAnsiTerm::ProcessTBC(void)
+{
+ // Soft Tab Clear - Not implemented yet
+ return true;
+}
+
+static CAnsiTerm *g_pDisplay;
+
+typedef unsigned char uchar;
+
+extern "C"
+{
+
+void console_open(char fdebugcmd)
+{
+ g_pDisplay = new CAnsiTerm();
+}
+
+void console_close(void)
+{
+ delete g_pDisplay;
+ g_pDisplay = NULL;
+}
+
+int console_in(DWORD keydata, uchar *pdata, int len)
+{
+ if (keydata == ~0)
+ {
+ g_pDisplay->WindowSizeChanged(false);
+ return 0;
+ }
+
+ return g_pDisplay->ProcessInput(*(CAnsiTerm::KeyCode *)&keydata, pdata, len);
+}
+
+void console_out(uchar *pdata, int len)
+{
+ if (len > 0)
+ {
+ g_pDisplay->ProcessOutput(pdata, len);
+ }
+}
+
+}